📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-05-29 08:33:53 +00:00
parent fdb52f1e96
commit 06e0d13d57
1615 changed files with 232858 additions and 0 deletions
@@ -0,0 +1,339 @@
# Beads Integration
Local issue tracking with dependency awareness. Complements remote platforms (GitHub, Linear) with project-scoped work items stored in `.beads/`.
## Overview
Beads provides:
- Local-first issue tracking (no remote dependency)
- Dependency graphs between issues
- Status workflow (open → in_progress → blocked → closed)
- Priority levels and type classification
- Assignee tracking for team awareness
**Key difference from Linear/GitHub**: Beads tracks work items at the project level, not org-wide. Data lives in `.beads/` directory.
## Core Commands for Status Reporting
### Stats Overview
```bash
bd stats
```
Returns project-level metrics:
- Total issues, open/closed counts
- In-progress and blocked counts
- Ready items (unblocked, actionable)
- Average lead time
**Use for**: Top-level summary section, health indicators.
### List Issues
```bash
bd list # All issues (default limit: 20)
bd list --status=open # Filter by status
bd list --status=in_progress # Active work
bd list --status=blocked # Stuck items
bd list --priority=1 # Urgent only (1=urgent, 4=low)
bd list --type=bug # Filter by type
bd list --assignee=alice # Filter by assignee
bd list --limit=10 # Pagination
```
**Statuses**: `open`, `in_progress`, `blocked`, `closed`
**Types**: `bug`, `feature`, `task`, `epic`, `chore`
**Priority**: 1 (urgent) → 4 (low), 0 (none)
**Use for**: Recent activity, filtered views, assignee workload.
### Ready Items
```bash
bd ready # Unblocked items ready for work
bd ready --limit=5 # Top 5 actionable
bd ready --priority=1 # Urgent and ready
bd ready --assignee=alice # Ready for specific person
```
Returns issues with zero blocking dependencies.
**Use for**: "What to work on next" section, actionable items.
### Blocked Items
```bash
bd blocked
```
Returns issues in blocked status with their blocking dependencies.
**Use for**: Dependency visibility, bottleneck identification.
### Issue Details
```bash
bd show <issue-id> # Full details with dependencies
```
Returns:
- Full description, design notes, acceptance criteria
- Blocking/blocked-by relationships
- Activity history
**Use for**: Deep dive on specific blocked items.
## Data Schema
```typescript
interface BeadsIssue {
id: string; // e.g., "AG-1", "BLZ-42"
title: string;
description?: string;
status: 'open' | 'in_progress' | 'blocked' | 'closed';
issue_type: 'bug' | 'feature' | 'task' | 'epic' | 'chore';
priority: 0 | 1 | 2 | 3 | 4; // 1=urgent, 4=low, 0=unset
assignee?: string;
labels: string[];
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
closed_at?: string;
dependency_count: number; // Issues blocking this
dependent_count: number; // Issues this blocks
}
interface BeadsStats {
total: number;
open: number;
in_progress: number;
blocked: number;
closed: number;
ready: number; // Unblocked and actionable
average_lead_time?: number; // Days from open to close
}
```
## Time Filtering
Beads lacks native time-based filtering. Apply client-side filtering on `updated_at`:
```typescript
// Filter to issues updated within time range
function filterByTime(issues: BeadsIssue[], hoursBack: number): BeadsIssue[] {
const cutoff = new Date();
cutoff.setHours(cutoff.getHours() - hoursBack);
return issues.filter(issue =>
new Date(issue.updated_at) >= cutoff
);
}
// Example: last 24 hours
const recentIssues = filterByTime(allIssues, 24);
```
**Recommendation**: Fetch with higher limit, filter client-side, then present top N.
## Gathering Pattern
```typescript
async function gatherBeadsData(timeHours: number = 24) {
// 1. Get overview stats
const stats = await bd.stats();
// 2. Get in-progress work
const inProgress = await bd.list({
status: 'in_progress',
limit: 10
});
// 3. Get ready items (actionable)
const ready = await bd.ready({ limit: 5 });
// 4. Get blocked items with dependencies
const blocked = await bd.blocked();
// 5. Get recently closed (for velocity)
const closed = await bd.list({
status: 'closed',
limit: 10
});
const recentlyClosed = filterByTime(closed, timeHours);
return { stats, inProgress, ready, blocked, recentlyClosed };
}
```
## Presentation Template
```
📋 BEADS ISSUES
{stats.total} total | {stats.open} open | {stats.in_progress} active | {stats.blocked} blocked
Ready to Work:
{id}: {title} [{type}, {priority_label}]
...
In Progress:
{id}: {title}
Status: {status} | Updated: {relative_time} | Assignee: {assignee}
...
Blocked ({blocked.length}):
{id}: {title}
⛔ Blocked by: {blocking_ids}
...
Recently Closed ({recentlyClosed.length}):
✓ {id}: {title} — closed {relative_time}
...
```
### Priority Labels
| Priority | Label | Indicator |
|----------|-------|-----------|
| 1 | urgent | 🔴 |
| 2 | high | 🟠 |
| 3 | normal | 🟡 |
| 4 | low | ⚪ |
| 0 | unset | — |
### Status Indicators
| Status | Indicator |
|--------|-----------|
| open | ◯ |
| in_progress | ◐ |
| blocked | ⛔ |
| closed | ✓ |
## Cross-Referencing
### With GitHub PRs
Match beads issue IDs in PR titles/branches:
- PR title: "AG-123: Implement feature" → links to beads AG-123
- Branch: `ag-123-feature` → links to beads AG-123
```typescript
function linkPRToBeads(prTitle: string, beadsIssues: BeadsIssue[]) {
const match = prTitle.match(/^([A-Z]+-\d+):/);
if (match) {
return beadsIssues.find(i => i.id === match[1]);
}
return null;
}
```
### With Linear Issues
Beads `external_ref` field can store Linear issue URL:
```bash
bd update AG-123 --external-ref="https://linear.app/team/issue/TEAM-456"
```
Query: Check `external_ref` for Linear correlation.
### With Graphite Stacks
Match branch names to beads issues:
- Branch: `ag-123-feature` → beads issue AG-123
- Stack contains multiple branches → multiple linked issues
## Context Detection
Beads requires workspace context. Detect via:
```bash
# Check if beads initialized
ls .beads/issues.db 2>/dev/null && echo "beads available"
# Or via MCP
bd where-am-i
```
**Auto-detection**: Include beads in sitrep when `.beads/` directory exists in project root.
## MCP Tools Reference
When using beads via MCP server:
| Tool | Purpose |
|------|---------|
| `beads__stats` | Project metrics overview |
| `beads__list` | Query issues with filters |
| `beads__ready` | Unblocked, actionable items |
| `beads__blocked` | Blocked items with dependencies |
| `beads__show` | Single issue details |
**Context**: Call `beads__set_context` with workspace root before other operations.
## Error Handling
```typescript
// Handle uninitialized beads
try {
const stats = await bd.stats();
} catch (e) {
if (e.message.includes('not initialized')) {
// Skip beads section, note in output
return { available: false, reason: 'Beads not initialized' };
}
throw e;
}
```
**Common errors**:
- "Beads not initialized" → `.beads/` doesn't exist
- "No context set" → call `set_context` first
- "Issue not found" → invalid issue ID
## Best Practices
1. **Prioritize Ready Items**: Show unblocked work prominently — these are actionable now
2. **Highlight Blockers**: Blocked items with their dependencies help identify bottlenecks
3. **Time-Filter Thoughtfully**: Since filtering is client-side, fetch reasonable limits (20-50) then filter
4. **Cross-Reference PRs**: Link beads issues to PRs/branches when ID patterns match
5. **Show Velocity**: Recently closed items indicate progress, especially useful for standups
6. **Respect Priority**: Sort by priority within each section (urgent first)
7. **Assignee Context**: When user has assignee, highlight their work specifically
## Integration Points
| Source | Correlation | Use Case |
|--------|-------------|----------|
| GitHub PRs | Issue ID in title/branch | Link PRs to tracked work |
| Graphite stacks | Branch naming | Show stack progress per issue |
| Linear | external_ref field | Bridge local ↔ team tracking |
## Troubleshooting
**"Beads not initialized"**
```bash
bd init # Initialize in project root
bd init --prefix=PROJ # Custom prefix (e.g., PROJ-1)
```
**"No issues found"**
- Check workspace context: `bd where-am-i`
- Verify `.beads/` exists in expected location
**"Wrong project context"**
```bash
bd set-context /path/to/project # Set correct workspace
```
**Stale data**
- Beads data is local — always fresh
- No caching concerns unlike remote APIs
@@ -0,0 +1,579 @@
# GitHub Integration
Tool-specific patterns for integrating GitHub PR status, CI checks, and review state into status reports.
## Overview
GitHub provides comprehensive PR metadata, CI/CD integration, and code review state. Status reports should extract actionable insights from PR state, check runs, and review decisions.
## Core Commands
### GitHub CLI (gh)
Primary tool for GitHub integration:
```bash
# List PRs with full metadata
gh pr list --json number,title,state,author,updatedAt,statusCheckRollup,reviewDecision
# Get specific PR details
gh pr view 123 --json number,title,state,statusCheckRollup,reviews,comments
# Check run details
gh pr checks 123
# Review status
gh pr status
```
### Repository Context
```bash
# Get current repo info
gh repo view --json nameWithOwner,defaultBranch
# Output: {"nameWithOwner": "owner/repo", "defaultBranch": "main"}
```
## Data Gathering
### PR List with Metadata
```typescript
interface GitHubPR {
number: number;
title: string;
state: 'OPEN' | 'CLOSED' | 'MERGED';
isDraft: boolean;
author: { login: string };
updatedAt: string;
statusCheckRollup: {
state: 'SUCCESS' | 'FAILURE' | 'PENDING' | 'EXPECTED';
contexts: CheckContext[];
};
reviewDecision: 'APPROVED' | 'CHANGES_REQUESTED' | 'REVIEW_REQUIRED' | null;
}
async function fetchOpenPRs(): Promise<GitHubPR[]> {
const result = await exec(
'gh pr list --json number,title,state,isDraft,author,updatedAt,statusCheckRollup,reviewDecision --limit 100'
);
return JSON.parse(result);
}
```
### CI Check Status
```typescript
interface CheckContext {
name: string;
state: 'SUCCESS' | 'FAILURE' | 'PENDING' | 'EXPECTED';
conclusion: 'SUCCESS' | 'FAILURE' | 'NEUTRAL' | 'CANCELLED' | 'SKIPPED' | null;
targetUrl?: string;
}
function analyzeCheckStatus(pr: GitHubPR): {
passing: number;
failing: number;
pending: number;
total: number;
failedChecks: string[];
} {
const contexts = pr.statusCheckRollup?.contexts || [];
const passing = contexts.filter(c =>
c.state === 'SUCCESS' || c.conclusion === 'SUCCESS'
).length;
const failing = contexts.filter(c =>
c.state === 'FAILURE' || c.conclusion === 'FAILURE'
).length;
const pending = contexts.filter(c =>
c.state === 'PENDING' || c.state === 'EXPECTED'
).length;
const failedChecks = contexts
.filter(c => c.state === 'FAILURE' || c.conclusion === 'FAILURE')
.map(c => c.name);
return {
passing,
failing,
pending,
total: contexts.length,
failedChecks
};
}
```
### Review State
```typescript
interface ReviewSummary {
approved: number;
changesRequested: number;
commented: number;
pending: number;
decision: 'APPROVED' | 'CHANGES_REQUESTED' | 'REVIEW_REQUIRED' | 'NONE';
}
function summarizeReviews(pr: GitHubPR): ReviewSummary {
// reviewDecision is aggregate state from GitHub
const decision = pr.reviewDecision || 'NONE';
// For detailed review counts, fetch full reviews:
// gh pr view {number} --json reviews
return {
decision,
// These would come from detailed review fetch if needed
approved: decision === 'APPROVED' ? 1 : 0,
changesRequested: decision === 'CHANGES_REQUESTED' ? 1 : 0,
commented: 0,
pending: decision === 'REVIEW_REQUIRED' ? 1 : 0
};
}
```
## Time Filtering
Filter PRs by update time:
```typescript
async function fetchRecentPRs(since: string): Promise<GitHubPR[]> {
// Convert time constraint to Date
const cutoffDate = parseTimeConstraint(since); // "-24h" → Date
// Fetch all open PRs
const allPRs = await fetchOpenPRs();
// Filter by updatedAt
return allPRs.filter(pr => {
const updatedAt = new Date(pr.updatedAt);
return updatedAt >= cutoffDate;
});
}
```
Alternative: Use GitHub API search:
```bash
# Search PRs updated since date
gh pr list --search "updated:>2024-01-15"
# Search with multiple criteria
gh pr list --search "is:open updated:>2024-01-15 -is:draft"
```
## Presentation Templates
### PR Section
```
🔀 PULL REQUESTS ({open_count} open, {recent_count} active)
PR #{number}: {title} [{state}]
Author: {author} | Updated: {relative_time}
CI: {ci_indicator} {passing}/{total} checks {failing_names}
Reviews: {review_indicator} {review_summary}
{blocker_indicator}
{pr_url}
```
### CI Status Indicators
```typescript
function formatCIStatus(checkSummary: ReturnType<typeof analyzeCheckStatus>): string {
const { passing, failing, pending, total, failedChecks } = checkSummary;
let indicator: string;
if (failing > 0) {
indicator = '✗';
} else if (pending > 0) {
indicator = '⏳';
} else if (passing === total && total > 0) {
indicator = '✓';
} else {
indicator = '○'; // No checks
}
let status = `${indicator} ${passing}/${total} checks`;
if (failing > 0) {
status += ` (failing: ${failedChecks.join(', ')})`;
}
return status;
}
```
### Review Status Indicators
```typescript
function formatReviewStatus(reviewSummary: ReviewSummary): string {
const { decision } = reviewSummary;
const indicators: Record<string, string> = {
'APPROVED': '✓ Approved',
'CHANGES_REQUESTED': '👀 Changes requested',
'REVIEW_REQUIRED': '⏸ Awaiting review',
'NONE': '○ No reviews'
};
return indicators[decision] || '○ No reviews';
}
```
### Example Output
```
🔀 PULL REQUESTS (3 open, 2 active in last 24h)
PR #156: Add authentication middleware [OPEN]
Author: @alice | Updated: 3 hours ago
CI: ✓ 4/4 checks passing
Reviews: ✓ Approved
https://github.com/owner/repo/pull/156
PR #155: Fix bug in user validation [OPEN]
Author: @bob | Updated: 5 hours ago
CI: ✗ 2/3 checks (failing: type-check, lint)
Reviews: 👀 Changes requested
◆ Blocker: Failing CI needs fixing
https://github.com/owner/repo/pull/155
PR #154: Update dependencies [OPEN] 🏷️ DRAFT
Author: @dependabot | Updated: 2 days ago
CI: ⏳ 1/2 checks pending
Reviews: ⏸ Awaiting review
https://github.com/owner/repo/pull/154
```
## Advanced Queries
### PR Comments and Activity
```bash
# Get comment counts
gh pr view 123 --json comments --jq '.comments | length'
# Recent activity (comments, reviews, commits)
gh pr view 123 --json timelineItems --jq '.timelineItems[] | select(.createdAt > "2024-01-15")'
```
### CI Run Details
```bash
# Get detailed check run info
gh run list --workflow=ci.yml --limit 10 --json status,conclusion,createdAt,displayTitle
# Download logs for failed runs
gh run view {run_id} --log-failed
```
### Cross-Repository Queries
For monorepos or multi-repo workflows:
```bash
# Query PRs across org
gh search prs --owner=org --state=open --json number,repository,title
# Filter by team
gh search prs --owner=org --team=@org/team-name --state=open
```
## Performance Optimization
### Batch Queries
Minimize API calls:
```typescript
async function fetchPRsBatch(prNumbers: number[]): Promise<GitHubPR[]> {
// Single gh pr list call with all metadata
const allPRs = await fetchOpenPRs();
// Filter to requested PRs
return allPRs.filter(pr => prNumbers.includes(pr.number));
}
```
### Caching
Cache PR data to avoid rate limits:
```typescript
interface PRCache {
timestamp: Date;
prs: GitHubPR[];
ttl: number;
}
function getCachedPRs(ttl = 300000): GitHubPR[] | null {
// Cache for 5 minutes by default
const cache = loadCache();
if (cache && Date.now() - cache.timestamp.getTime() < ttl) {
return cache.prs;
}
return null;
}
```
### Parallel Fetching
```typescript
async function fetchCompletePRData(): Promise<PRData> {
const [prs, repo, workflow_runs] = await Promise.all([
fetchOpenPRs(),
fetchRepoInfo(),
fetchRecentWorkflowRuns()
]);
return { prs, repo, workflow_runs };
}
```
## Cross-Referencing
### Link PRs to Branches
```typescript
function linkPRsToBranches(prs: GitHubPR[], branches: string[]): Map<string, GitHubPR> {
// Fetch branch info for each PR
const prBranchMap = new Map<string, GitHubPR>();
for (const pr of prs) {
// Get head ref (branch name) from PR
const headRef = await exec(`gh pr view ${pr.number} --json headRefName --jq .headRefName`);
prBranchMap.set(headRef.trim(), pr);
}
return prBranchMap;
}
```
### Link PRs to Issues
```typescript
function extractLinkedIssues(prBody: string): string[] {
// Match: "Closes #123", "Fixes #456", "Resolves #789"
const patterns = [
/(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)s?\s+#(\d+)/gi,
/#(\d+)/g // Generic issue references
];
const issueNumbers: string[] = [];
for (const pattern of patterns) {
const matches = prBody.matchAll(pattern);
for (const match of matches) {
issueNumbers.push(match[1]);
}
}
return [...new Set(issueNumbers)]; // Deduplicate
}
```
## Error Handling
### Authentication
```typescript
async function ensureGitHubAuth(): Promise<boolean> {
try {
await exec('gh auth status');
return true;
} catch (error) {
console.error('GitHub authentication required. Run: gh auth login');
return false;
}
}
```
### Rate Limiting
```typescript
async function checkRateLimit(): Promise<{ remaining: number; resetAt: Date }> {
const result = await exec('gh api rate_limit --jq .rate');
const data = JSON.parse(result);
return {
remaining: data.remaining,
resetAt: new Date(data.reset * 1000)
};
}
async function withRateLimitCheck<T>(fn: () => Promise<T>): Promise<T> {
const limit = await checkRateLimit();
if (limit.remaining < 10) {
const waitTime = limit.resetAt.getTime() - Date.now();
console.warn(`Rate limit low (${limit.remaining}). Resets in ${waitTime}ms`);
}
return fn();
}
```
### Repository Detection
```typescript
async function detectGitHubRepo(): Promise<string | null> {
try {
const result = await exec('gh repo view --json nameWithOwner --jq .nameWithOwner');
return result.trim();
} catch (error) {
// Not in a GitHub repo or gh not configured
return null;
}
}
```
## Integration Points
### With Graphite (see graphite.md)
Enrich Graphite stack with GitHub PR details:
```typescript
async function enrichGraphiteStackWithGitHub(stack: StackNode[]): Promise<void> {
const prNumbers = stack.map(n => n.prNumber).filter(Boolean);
const prs = await fetchPRsBatch(prNumbers);
for (const node of stack) {
const pr = prs.find(p => p.number === node.prNumber);
if (pr) {
node.githubPR = pr;
node.ciStatus = analyzeCheckStatus(pr);
node.reviewStatus = summarizeReviews(pr);
}
}
}
```
### With CI/CD Tools
```typescript
async function fetchWorkflowRuns(since: string): Promise<WorkflowRun[]> {
const cutoff = parseTimeConstraint(since);
const cutoffISO = cutoff.toISOString();
const result = await exec(
`gh run list --json status,conclusion,createdAt,displayTitle,workflowName,url ` +
`--created ">=${cutoffISO}" --limit 50`
);
return JSON.parse(result);
}
```
## Best Practices
### Minimize API Calls
- Use `--json` flag to fetch all needed fields in single call
- Cache results with appropriate TTL
- Use `gh pr list` once, filter in memory
### Handle Missing Data
```typescript
function safelyAccessPRData(pr: GitHubPR): {
hasChecks: boolean;
hasReviews: boolean;
isComplete: boolean;
} {
return {
hasChecks: Boolean(pr.statusCheckRollup?.contexts?.length),
hasReviews: Boolean(pr.reviewDecision),
isComplete: Boolean(pr.statusCheckRollup && pr.reviewDecision)
};
}
```
### Relative Timestamps
```typescript
function formatRelativeTime(isoDate: string): string {
const date = new Date(isoDate);
const now = new Date();
const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 60) return `${minutes} minutes ago`;
if (hours < 24) return `${hours} hours ago`;
return `${days} days ago`;
}
```
## CLI Reference
Essential GitHub CLI commands:
```bash
# PR listing
gh pr list # All open PRs
gh pr list --limit 100 # More PRs
gh pr list --json {fields} # Structured output
gh pr list --search "query" # Search PRs
# PR details
gh pr view {number} # Human-readable
gh pr view {number} --json {fields} # Structured
gh pr checks {number} # CI checks
gh pr diff {number} # Show diff
# Repository info
gh repo view # Current repo
gh repo view --json {fields} # Structured
# API access
gh api /repos/{owner}/{repo}/pulls # Direct API
gh api rate_limit # Check limits
# Search
gh search prs {query} # Search PRs
gh search issues {query} # Search issues
```
## Troubleshooting
### gh CLI Not Found
```bash
# Install GitHub CLI
# macOS: brew install gh
# Linux: See https://github.com/cli/cli#installation
# Verify installation
gh --version
```
### Not Authenticated
```bash
# Login to GitHub
gh auth login
# Check status
gh auth status
```
### Wrong Repository Context
```bash
# Verify current repo
gh repo view
# Switch to different repo
cd /path/to/repo
# Or specify repo explicitly
gh pr list --repo owner/repo
```
@@ -0,0 +1,428 @@
# Graphite Integration
Tool-specific patterns for integrating Graphite (gt) stack visualization and PR management into status reports.
## Overview
Graphite provides stack-aware version control with visual branch hierarchies and integrated PR management. Status reports should leverage stack structure for context-rich presentation.
## Core Commands
### Stack Visualization
```bash
# Get visual tree of stacked branches
gt log
# Output includes:
# - Branch hierarchy (parent/child relationships)
# - PR status per branch
# - Commit counts
# - Current branch indicator (◆)
# - Branch states (needs restack, needs submit, ready to merge)
```
**Example Output**:
```
◆ feature/auth-refactor (3) - #123 ✓ Ready to merge
├─ feature/add-jwt (2) - #122 ⏳ In progress
└─ feature/update-middleware (1) - #121 ⏸ Draft
```
### Branch State
```bash
# Get current stack state as JSON
gt stack --json
# Returns:
# - Branch metadata (name, parent, children)
# - PR associations
# - Commit SHAs and messages
# - Sync status (ahead/behind trunk)
```
### PR Submission Status
```bash
# Check if branches need submission
gt stack
# Shows branches with:
# - "needs submit" → changes not pushed to PR
# - "needs restack" → parent branch updated
# - "ready to merge" → approved, passing CI
```
## Data Gathering
### Stack Structure
Extract hierarchical branch relationships:
```typescript
interface StackNode {
branch: string;
prNumber?: number;
prStatus?: 'draft' | 'open' | 'ready' | 'merged';
commitCount: number;
parent?: string;
children: string[];
isCurrent: boolean;
needsRestack: boolean;
needsSubmit: boolean;
}
async function getStackStructure(): Promise<StackNode[]> {
// Parse gt log output or gt stack --json
const output = await exec('gt log');
// Extract:
// - Branch names and hierarchy
// - PR numbers (from "#123" markers)
// - Status indicators (✓ ⏳ ⏸)
// - Commit counts (from "(N)" markers)
// - Current branch (◆ marker)
return parseStackTree(output);
}
```
### PR Integration
Graphite automatically links branches to PRs:
```typescript
// Get PR metadata for stack
async function getStackPRs(branches: string[]): Promise<PRMetadata[]> {
// Option 1: Parse from gt log (includes basic status)
// Option 2: Query GitHub directly with PR numbers
// Option 3: Use gt pr status --json (if available)
const prNumbers = branches
.map(b => extractPRNumber(b))
.filter(Boolean);
// Fetch details from GitHub (see github.md)
return fetchPRDetails(prNumbers);
}
```
## Time Filtering
Graphite doesn't natively support time filtering, so filter results:
```typescript
async function getRecentStackActivity(since: string): Promise<StackActivity> {
// Get full stack
const stack = await getStackStructure();
// Parse time constraint
const cutoff = parseTimeConstraint(since); // "-24h" → Date
// Filter by git commit timestamps
for (const node of stack) {
const commits = await exec(`git log ${node.branch} --since="${cutoff}" --format="%H %s %cr"`);
node.recentCommits = parseCommits(commits);
}
// Only show branches with activity
return stack.filter(n => n.recentCommits.length > 0);
}
```
## Presentation Templates
### Stack Tree Format
```
📊 GRAPHITE STACK
{current_branch_name}
{tree visualization from gt log}
Stack Summary:
Branches: {total} ({open} with PRs)
Ready to merge: {ready_count}
Needs attention: {needs_restack + needs_submit}
```
### Stack-Aware PR Grouping
Organize PRs by stack position (bottom to top):
```
🔀 PULL REQUESTS (Stack-Aware)
Stack: {stack_name}
├─ PR #123: [feature/auth-refactor] Refactor authentication
│ CI: ✓ 3/3 passing | Reviews: ✓ 2 approved
│ Updated: 3 hours ago
│ └─ Ready to merge ✓
├─ PR #122: [feature/add-jwt] Add JWT token support
│ CI: ⏳ 2/3 passing | Reviews: 👀 1 change requested
│ Updated: 5 hours ago
│ └─ Depends on: PR #121
└─ PR #121: [feature/update-middleware] Update auth middleware
CI: ✗ 1/3 failing | Reviews: ⏸ No reviews
Updated: 1 day ago
└─ Blocker: CI failing ◆◆
```
### Attention Indicators
Highlight stack-specific issues:
```
⚠️ STACK ATTENTION NEEDED
◆◆ PR #121: Blocking entire stack (failing CI)
◆ Branch feature/add-jwt: Needs restack (parent updated)
◇ Branch feature/auth-refactor: Needs submit (local changes)
```
## Cross-Referencing
### Link Stack to Issues
Match issue IDs in PR titles/bodies:
```typescript
function linkStackToIssues(stack: StackNode[], issues: Issue[]): void {
for (const node of stack) {
// Extract issue references from PR title
// Pattern: "BLZ-123: Feature title" or "[BLZ-123] Feature title"
const issueKeys = extractIssueKeys(node.prTitle);
// Find matching issues
node.relatedIssues = issues.filter(i => issueKeys.includes(i.key));
}
}
```
### Dependency Tracking
Show blocked/blocking relationships:
```typescript
interface StackDependencies {
branch: string;
blockedBy: string[]; // Parent branches not merged
blocking: string[]; // Child branches waiting
}
function analyzeStackDependencies(stack: StackNode[]): StackDependencies[] {
return stack.map(node => ({
branch: node.branch,
blockedBy: node.parent && !isReadyToMerge(node.parent) ? [node.parent] : [],
blocking: node.children.filter(child => isReadyToMerge(node) && !isReadyToMerge(child))
}));
}
```
## Best Practices
### Efficient Queries
Minimize git/Graphite calls:
1. Single `gt log` for stack structure
2. Single `git log --all --since` for commit history
3. Batch PR queries to GitHub (see github.md)
### State Caching
Cache stack state to avoid repeated parsing:
```typescript
interface StackCache {
timestamp: Date;
stack: StackNode[];
ttl: number; // milliseconds
}
function getCachedStack(ttl = 60000): StackNode[] | null {
const cache = loadCache();
if (cache && Date.now() - cache.timestamp.getTime() < ttl) {
return cache.stack;
}
return null;
}
```
### Error Handling
Handle common Graphite errors:
```typescript
try {
const stack = await exec('gt log');
} catch (error) {
if (error.message.includes('not a git repository')) {
return null; // Gracefully skip Graphite section
}
if (error.message.includes('graphite not initialized')) {
// Suggest: gt repo init
return null;
}
throw error; // Unexpected error
}
```
## Integration Points
### With GitHub (see github.md)
Combine Graphite stack structure with GitHub PR details:
```typescript
async function enrichStackWithGitHub(stack: StackNode[]): Promise<void> {
const prNumbers = stack
.map(n => n.prNumber)
.filter(Boolean);
const prDetails = await fetchGitHubPRs(prNumbers); // See github.md
for (const node of stack) {
const pr = prDetails.find(p => p.number === node.prNumber);
if (pr) {
node.ciStatus = pr.ciStatus;
node.reviewStatus = pr.reviewStatus;
node.updatedAt = pr.updatedAt;
}
}
}
```
### With Linear (see linear.md)
Link Linear issues to stack branches:
```typescript
async function linkStackToLinear(stack: StackNode[]): Promise<void> {
// Extract all issue keys from PR titles
const issueKeys = stack
.flatMap(n => extractIssueKeys(n.prTitle || ''))
.filter(Boolean);
// Fetch Linear issues
const issues = await fetchLinearIssues({ keys: issueKeys });
// Annotate stack nodes
for (const node of stack) {
const keys = extractIssueKeys(node.prTitle || '');
node.linearIssues = issues.filter(i => keys.includes(i.identifier));
}
}
```
## Common Patterns
### Stack Health Score
Calculate stack quality metrics:
```typescript
interface StackHealth {
score: number; // 0-100
issues: string[];
readyToMerge: number;
needsWork: number;
}
function calculateStackHealth(stack: StackNode[]): StackHealth {
let score = 100;
const issues: string[] = [];
const needsRestack = stack.filter(n => n.needsRestack).length;
const needsSubmit = stack.filter(n => n.needsSubmit).length;
const failingCI = stack.filter(n => n.ciStatus === 'failing').length;
const readyToMerge = stack.filter(n => n.prStatus === 'ready').length;
score -= needsRestack * 10; // -10 per restack needed
score -= needsSubmit * 5; // -5 per submit needed
score -= failingCI * 20; // -20 per failing CI
if (needsRestack) issues.push(`${needsRestack} branches need restack`);
if (needsSubmit) issues.push(`${needsSubmit} branches need submit`);
if (failingCI) issues.push(`${failingCI} PRs with failing CI`);
return {
score: Math.max(0, score),
issues,
readyToMerge,
needsWork: needsRestack + needsSubmit + failingCI
};
}
```
### Stack Timeline
Show activity timeline across stack:
```
📅 STACK TIMELINE (Last 24 hours)
2 hours ago │ PR #123 approved by @reviewer
3 hours ago │ feature/auth-refactor: Pushed 2 commits
5 hours ago │ PR #122: CI checks passing
1 day ago │ feature/add-jwt: Created PR
```
## CLI Reference
Essential Graphite commands for status reporting:
```bash
# Stack visualization
gt log # Visual tree
gt log --short # Compact format
gt log --json # Machine-readable
# Stack state
gt stack # Current stack info
gt stack --json # Structured output
# Branch operations (for context)
gt upstack # Show branches above
gt downstack # Show branches below
# PR operations (for context)
gt pr status # PR status for stack
gt submit --dry-run # Preview what would be submitted
```
## Troubleshooting
### Stack Not Showing
```bash
# Verify Graphite initialized
gt repo init
# Verify on a branch
git branch
# Check for trunk configuration
gt repo --show
```
### PR Associations Missing
```bash
# PRs might not be associated with branches
# Check with:
gt pr status
# Re-associate if needed:
gt pr submit
```
### Performance Issues
Large stacks (>20 branches) can slow down:
- Cache `gt log` output
- Limit depth with `gt log --depth 10`
- Filter to relevant branches only
- Consider pagination for display
@@ -0,0 +1,161 @@
# Implementation Patterns
Technical patterns for status report generation.
## Parallel Queries
Execute source queries concurrently:
```typescript
const [vcsData, prData, issueData, ciData] = await Promise.allSettled([
fetchVCSState(timeFilter),
fetchPRStatus(timeFilter),
fetchIssues(timeFilter),
fetchCIStatus(timeFilter)
]);
// Handle each result (success or failure)
// Skip sections where source unavailable
```
## Error Handling
Graceful degradation:
- Source unavailable → skip section, note in output
- Partial data → show available, note gaps
- API rate limits → use cached data, note staleness
- Auth failures → prompt for credentials or skip
## Caching Strategy
For expensive queries:
- Cache with timestamp
- Reuse if fresh (< 5 min)
- Allow bypass with flag
- Clear on explicit refresh
## Scripts
The `scripts/` directory contains Bun scripts for data gathering:
```
scripts/
├── sitrep.ts # Entry point - orchestrates gatherers
├── gatherers/
│ ├── graphite.ts # Graphite stack data
│ ├── github.ts # GitHub PRs, CI status
│ ├── linear.ts # Linear issues (via Claude CLI headless)
│ └── beads.ts # Beads local issues
└── lib/
├── time.ts # Time parsing utilities
└── types.ts # Shared type definitions
```
**Usage**:
```bash
./scripts/sitrep.ts # All sources, 24h default
./scripts/sitrep.ts -t 7d # All sources, last 7 days
./scripts/sitrep.ts -s github,beads # Specific sources only
./scripts/sitrep.ts --format=text # Human-readable output
```
**Output formats**: `json` (default, structured) | `text` (human-readable)
**Benefits**:
- Single command, parallel gathering
- Graceful degradation
- Consistent JSON schema
- Reduces agent tool calls 80%+
## Extensibility
### Adding New Sources
1. Create reference doc in `references/`
2. Define data schema
3. Implement query function with time filter
4. Add aggregation logic
5. Design presentation template
6. Update workflow docs
### Custom Aggregations
Optional sections when data available:
- Velocity metrics (PRs merged/day)
- Team activity (commits by author)
- Quality indicators (test coverage trends)
- Deployment frequency
### Tool-Specific Docs
Reference documents should cover:
- Optimal CLI/API calls
- Response parsing
- Rate limit handling
- Auth patterns
- Caching recommendations
## Context Awareness
Map repos to relevant filters:
```json
{
"mappings": [
{
"path": "/absolute/path/to/repo",
"filters": {
"issues": { "team": "TEAM-ID" },
"labels": ["repo-name"]
}
},
{
"path": "/path/with/*",
"pattern": true,
"filters": {
"issues": { "project": "PROJECT-ID" }
}
}
],
"defaults": {
"time_period": "7d",
"issue_limit": 10,
"pr_limit": 20
}
}
```
**Lookup strategy**:
1. Exact path match
2. Pattern match (wildcards)
3. Repo name extraction
4. Default filters
**Config location**: `~/.config/claude/status-reporting/config.json`
## Anti-Patterns
### Sequential Queries
**Problem**: Waiting for each source before next
**Why fails**: Slow, blocks on failures
**Instead**: `Promise.allSettled()` for parallel
### Rigid Source Requirements
**Problem**: Failing if expected source missing
**Why fails**: Breaks in different environments
**Instead**: Detect available, skip unavailable
### Absolute Timestamps Only
**Problem**: Raw dates without context
**Why fails**: Hard to scan for recency
**Instead**: Relative ("2 hours ago") with absolute in detail
### Unstructured Output
**Problem**: Dumping all data without organization
**Why fails**: Not scannable, misses insights
**Instead**: Templates with hierarchy and indicators
@@ -0,0 +1,669 @@
# Linear Integration
Tool-specific patterns for integrating Linear issue tracking into status reports via the **streamlinear MCP server** (`github:obra/streamlinear`).
> **Important**: This guide is specifically for the streamlinear MCP, not the official Linear MCP. The streamlinear server uses a single `mcp__linear__linear` tool with action-based dispatch rather than separate tools per operation.
## Overview
Linear provides issue tracking with team-based organization, project management, and rich metadata. Status reports should surface recently active issues relevant to current work context.
## Streamlinear MCP Tool
All Linear operations go through a single tool with an `action` parameter:
```typescript
// Search your active issues
await mcp__linear__linear({
action: 'search'
});
// Search with text query
await mcp__linear__linear({
action: 'search',
query: 'authentication bug'
});
// Search with filters
await mcp__linear__linear({
action: 'search',
query: {
team: 'BLZ',
state: 'In Progress',
assignee: 'me'
}
});
// Get issue details
await mcp__linear__linear({
action: 'get',
id: 'BLZ-123' // Also accepts URLs or UUIDs
});
// Update issue
await mcp__linear__linear({
action: 'update',
id: 'BLZ-123',
state: 'Done'
});
// Add comment
await mcp__linear__linear({
action: 'comment',
id: 'BLZ-123',
body: 'Fixed in commit abc123'
});
// Create issue
await mcp__linear__linear({
action: 'create',
title: 'Bug title',
team: 'BLZ',
body: 'Description here',
priority: 2
});
// Raw GraphQL for advanced queries
await mcp__linear__linear({
action: 'graphql',
graphql: 'query { teams { nodes { id key name } } }'
});
```
## Action Reference
| Action | Purpose | Key Parameters |
|--------|---------|----------------|
| `search` | Find issues | `query` (string or object with filters) |
| `get` | Issue details | `id` (identifier, URL, or UUID) |
| `update` | Change issue | `id`, `state`, `priority`, `assignee`, `labels` |
| `comment` | Add comment | `id`, `body` |
| `create` | New issue | `title`, `team`, `body`, `priority`, `labels` |
| `graphql` | Raw queries | `graphql`, `variables` |
| `help` | Full docs | (none) |
## Priority Values
| Value | Meaning |
|-------|---------|
| 0 | None |
| 1 | Urgent |
| 2 | High |
| 3 | Medium |
| 4 | Low |
## Data Gathering
### Issue Listing
```typescript
interface LinearIssue {
identifier: string; // "BLZ-123"
title: string;
state: {
name: string; // "In Progress", "Done", etc.
type: string; // "started", "completed", etc.
};
priority: number; // 0-4 (0=none, 1=urgent, 2=high, 3=normal, 4=low)
assignee?: {
name: string;
email: string;
};
labels: Array<{
name: string;
color: string;
}>;
createdAt: string;
updatedAt: string;
url: string;
}
async function fetchTeamIssues(teamKey: string): Promise<LinearIssue[]> {
const result = await mcp__linear__linear({
action: 'search',
query: { team: teamKey }
});
return result.issues;
}
async function fetchMyActiveIssues(): Promise<LinearIssue[]> {
const result = await mcp__linear__linear({
action: 'search'
});
return result.issues;
}
```
### Advanced Queries with GraphQL
For complex filtering not supported by the search action, use GraphQL:
```typescript
// Get all teams
async function listTeams(): Promise<Array<{id: string, key: string, name: string}>> {
const result = await mcp__linear__linear({
action: 'graphql',
graphql: 'query { teams { nodes { id key name } } }'
});
return result.teams.nodes;
}
// Get issues updated in last N days across all teams
async function fetchRecentIssues(daysBack: number = 7): Promise<LinearIssue[]> {
const result = await mcp__linear__linear({
action: 'graphql',
graphql: `
query {
viewer {
assignedIssues(
filter: { state: { type: { nin: ["completed", "canceled"] } } }
first: 30
orderBy: updatedAt
) {
nodes {
identifier
title
state { name type }
team { key }
priority
updatedAt
url
}
}
}
}
`
});
return result.viewer.assignedIssues.nodes;
}
// Filter by state type
async function fetchIssuesByStateType(
stateType: 'unstarted' | 'started' | 'completed' | 'canceled'
): Promise<LinearIssue[]> {
const result = await mcp__linear__linear({
action: 'graphql',
graphql: `
query($stateType: String!) {
issues(
filter: { state: { type: { eq: $stateType } } }
first: 50
) {
nodes {
identifier
title
state { name type }
team { key }
priority
}
}
}
`,
variables: { stateType }
});
return result.issues.nodes;
}
```
### Context-Aware Filtering
Map repository to Linear team/project:
```typescript
interface LinearContext {
filterBy: 'team' | 'project' | 'query';
team?: string; // Team key (e.g., "BLZ")
project?: string;
query?: string;
}
interface RepoMapping {
path: string;
pattern?: boolean; // If true, path supports wildcards
linear: LinearContext;
}
interface LinearConfig {
mappings: RepoMapping[];
defaults: {
daysBack: number;
limit: number;
};
}
```
Example configuration:
```json
{
"mappings": [
{
"path": "/Users/mg/Developer/outfitter/blz",
"linear": {
"filterBy": "team",
"team": "BLZ"
}
},
{
"path": "/Users/mg/Developer/*",
"pattern": true,
"linear": {
"filterBy": "query",
"query": "outfitter"
}
}
],
"defaults": {
"daysBack": 7,
"limit": 10
}
}
```
### Context Resolution
```typescript
async function resolveLinearContext(cwd: string, config: LinearConfig): Promise<LinearContext | null> {
// Try exact path match first
for (const mapping of config.mappings) {
if (!mapping.pattern && mapping.path === cwd) {
return mapping.linear;
}
}
// Try pattern match
for (const mapping of config.mappings) {
if (mapping.pattern) {
const regex = new RegExp('^' + mapping.path.replace(/\*/g, '.*') + '$');
if (regex.test(cwd)) {
return mapping.linear;
}
}
}
// Fallback: query-based search using repo name
const repoName = await getRepoName(cwd);
if (repoName) {
return {
filterBy: 'query',
query: repoName.split('/')[1] // Extract short name from "owner/repo"
};
}
return null;
}
```
## Presentation Templates
### Issue Section
```
LINEAR ISSUES (Recent Activity - {team_name})
{count} issues updated in last {period}
{issue_identifier}: {title} [{state}]
Priority: {priority_label} | Assignee: {assignee_name}
Labels: {label_list}
Updated: {relative_time}
{issue_url}
```
### Priority Formatting
```typescript
function formatPriority(priority: number): string {
const labels: Record<number, string> = {
0: 'None',
1: 'Urgent',
2: 'High',
3: 'Medium',
4: 'Low'
};
return labels[priority] || 'None';
}
```
### Example Output
```
LINEAR ISSUES (Recent Activity - BLZ Team)
5 issues updated in last 7 days
BLZ-162: Implement authentication middleware [In Progress]
Priority: High | Assignee: Alice Smith
Labels: backend, security
Updated: 3 hours ago
https://linear.app/outfitter/issue/BLZ-162
BLZ-161: Fix user validation bug [Done]
Priority: Urgent | Assignee: Bob Jones
Labels: bug, backend
Updated: 5 hours ago
https://linear.app/outfitter/issue/BLZ-161
BLZ-158: Update dependencies [Todo]
Priority: Low | Assignee: Unassigned
Labels: maintenance
Updated: 2 days ago
https://linear.app/outfitter/issue/BLZ-158
```
## Cross-Referencing
### Link Issues to PRs
Extract issue references from PR titles/bodies:
```typescript
function extractIssueReferences(text: string): string[] {
// Pattern: "BLZ-123" or "[BLZ-123]" or "BLZ-123:"
const pattern = /\[?([A-Z]{2,}-\d+)\]?:?/g;
const matches = text.matchAll(pattern);
return Array.from(matches, m => m[1]);
}
async function linkIssuesToPRs(
issues: LinearIssue[],
prs: GitHubPR[]
): Promise<Map<string, GitHubPR[]>> {
const issueMap = new Map<string, GitHubPR[]>();
for (const issue of issues) {
const relatedPRs = prs.filter(pr => {
const refs = extractIssueReferences(pr.title + ' ' + pr.body);
return refs.includes(issue.identifier);
});
if (relatedPRs.length > 0) {
issueMap.set(issue.identifier, relatedPRs);
}
}
return issueMap;
}
```
### Annotate Issues with PR Status
```
LINEAR ISSUES (with PR Status)
BLZ-162: Implement authentication middleware [In Progress]
Priority: High | Assignee: Alice Smith
PRs: #156 (Approved, CI passing)
Updated: 3 hours ago
BLZ-161: Fix user validation bug [Done]
Priority: Urgent | Assignee: Bob Jones
PRs: #155 (CI failing, changes requested)
Updated: 5 hours ago
```
## State Matching
The streamlinear MCP supports fuzzy state matching:
```typescript
// These all work:
await mcp__linear__linear({ action: 'update', id: 'BLZ-123', state: 'done' });
await mcp__linear__linear({ action: 'update', id: 'BLZ-123', state: 'Done' });
await mcp__linear__linear({ action: 'update', id: 'BLZ-123', state: 'in prog' });
await mcp__linear__linear({ action: 'update', id: 'BLZ-123', state: 'In Progress' });
```
## Error Handling
### MCP Availability
```typescript
async function checkLinearMCPAvailable(): Promise<boolean> {
try {
await mcp__linear__linear({ action: 'search' });
return true;
} catch (error) {
console.warn('Linear MCP not available:', error.message);
return false;
}
}
```
### Graceful Degradation
```typescript
async function fetchLinearIssuesSafe(
context: LinearContext | null
): Promise<LinearIssue[] | null> {
if (!context) {
console.log('No Linear context for current repo');
return null;
}
const available = await checkLinearMCPAvailable();
if (!available) {
console.log('Linear MCP not available, skipping issue section');
return null;
}
try {
if (context.filterBy === 'team' && context.team) {
return await fetchTeamIssues(context.team);
} else if (context.filterBy === 'query' && context.query) {
const result = await mcp__linear__linear({
action: 'search',
query: context.query
});
return result.issues;
}
return await fetchMyActiveIssues();
} catch (error) {
console.error('Failed to fetch Linear issues:', error);
return null;
}
}
```
## Configuration Management
### Config File Location
Store mapping config in skill directory or user config:
```
~/.config/claude/status-reporting/linear-config.json
```
Or project-specific:
```
.claude/linear-mapping.json
```
### Loading Configuration
```typescript
async function loadLinearConfig(): Promise<LinearConfig> {
const configPaths = [
// User config
path.join(os.homedir(), '.config/claude/status-reporting/linear-config.json'),
// Project config
path.join(process.cwd(), '.claude/linear-mapping.json')
];
for (const configPath of configPaths) {
if (await fileExists(configPath)) {
const content = await Bun.file(configPath).text();
return JSON.parse(content);
}
}
// Return defaults
return {
mappings: [],
defaults: {
daysBack: 7,
limit: 10
}
};
}
```
## Best Practices
### Team Key vs Team Name
Use team keys (e.g., "BLZ") rather than full names:
- Keys are shorter and less prone to typos
- The streamlinear MCP expects keys in query filters
- Keys are visible in issue identifiers (BLZ-123)
Get team keys:
```typescript
const result = await mcp__linear__linear({
action: 'graphql',
graphql: 'query { teams { nodes { id key name } } }'
});
// Returns: [{ id: "uuid", key: "BLZ", name: "BLZ Team" }, ...]
```
### Relative Time Display
```typescript
function formatRelativeTime(isoDate: string): string {
const date = new Date(isoDate);
const now = new Date();
const diff = now.getTime() - date.getTime();
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 60) return `${minutes} minutes ago`;
if (hours < 24) return `${hours} hours ago`;
if (days < 7) return `${days} days ago`;
return date.toLocaleDateString();
}
```
### Issue Prioritization
Show high-priority and urgent issues first:
```typescript
function sortIssuesByPriority(issues: LinearIssue[]): LinearIssue[] {
return issues.sort((a, b) => {
// Lower number = higher priority (1=urgent, 2=high, 3=normal, 4=low)
// 0=none goes to end
const priorityA = a.priority === 0 ? 99 : a.priority;
const priorityB = b.priority === 0 ? 99 : b.priority;
if (priorityA !== priorityB) {
return priorityA - priorityB;
}
// Same priority: sort by updated time (most recent first)
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
});
}
```
## Integration Points
### With GitHub (see github.md)
Correlate Linear issues with GitHub PRs:
```typescript
async function correlateLinearWithGitHub(
issues: LinearIssue[],
prs: GitHubPR[]
): Promise<void> {
for (const issue of issues) {
// Find PRs referencing this issue
const relatedPRs = prs.filter(pr => {
const refs = extractIssueReferences(pr.title + ' ' + (pr.body || ''));
return refs.includes(issue.identifier);
});
if (relatedPRs.length > 0) {
issue.relatedPRs = relatedPRs;
}
}
}
```
### With Graphite (see graphite.md)
Show Linear issues alongside stack:
```typescript
async function annotateStackWithLinear(
stack: StackNode[],
issues: LinearIssue[]
): Promise<void> {
for (const node of stack) {
if (!node.prTitle) continue;
const refs = extractIssueReferences(node.prTitle);
node.linearIssues = issues.filter(issue =>
refs.includes(issue.identifier)
);
}
}
```
## Troubleshooting
### Linear MCP Not Found
Verify the streamlinear MCP server is configured in `~/.claude.json`:
```json
{
"mcpServers": {
"linear": {
"type": "stdio",
"command": "npx",
"args": ["-y", "github:obra/streamlinear"]
}
}
}
```
Ensure `LINEAR_API_TOKEN` is set in your environment.
### No Issues Returned
```typescript
// Debug: Check available teams
const teams = await mcp__linear__linear({
action: 'graphql',
graphql: 'query { teams { nodes { id key name } } }'
});
console.log('Available teams:', teams);
// Debug: Try broader search
const allIssues = await mcp__linear__linear({
action: 'search',
query: ''
});
console.log('Total issues accessible:', allIssues.length);
```
### Authentication Issues
The streamlinear MCP reads `LINEAR_API_TOKEN` from environment. Verify it's set:
```bash
echo $LINEAR_API_TOKEN
```
Generate a new token at: <https://linear.app/settings/api>
@@ -0,0 +1,106 @@
# Presentation Templates
Output formats and section templates for status reports.
## Output Structure
```
=== STATUS REPORT: {repo-name} ===
Generated: {timestamp}
{Time filter: "Last 24 hours" if applicable}
{VCS_SECTION}
{PR_SECTION}
{ISSUE_SECTION}
{CI_SECTION}
```
## Visual Indicators
**Status**:
- `✓` success, passing, approved
- `✗` failure, failed, rejected
- `⏳` in-progress, pending
- `⏸` paused, draft
- `🔴` blocker, critical
**Progress** (use `░▓`):
- `▓▓▓░░` — 3/5 checks passing
**Severity** (use `◇◆`):
- `◇` minor, informational
- `◆` moderate, needs attention
- `◆◆` severe, blocking
## Section Templates
### VCS Section (Stack-Aware)
```
📊 {VCS_NAME} STACK
{visual tree with branch relationships}
├─ {branch}: {status} [{commit_count} commits]
│ PR #{num}: {pr_status} | CI: {ci_status}
│ Updated: {relative_time}
```
### VCS Section (Standard)
```
📊 VERSION CONTROL
Current branch: {branch}
Status: {clean | modified | ahead X, behind Y}
Recent commits: {count} in last {period}
```
### PR Section
```
🔀 PULL REQUESTS ({open_count} open)
PR #{num}: {title} [{state}]
Author: {author} | Updated: {relative_time}
CI: {status_indicator} {pass}/{total} checks
Reviews: {status_indicator} {approved}/{total} reviewers
{blocker indicator if applicable}
```
### Issue Section
```
📋 ISSUES (Recent Activity)
{issue_key}: {title} [{status}]
Priority: {priority} | Assignee: {assignee}
Updated: {relative_time}
{link}
```
### CI Section
```
🔧 CI/CD ({total} runs)
Success: {success_count} | Failed: {failed_count} | In Progress: {pending_count}
{if failures exist:}
Recent Failures:
{workflow_name}: {error_summary}
{link to run}
```
### Attention Section
Highlight action-needed items at top:
```
⚠️ ATTENTION NEEDED
◆◆ PR #123: CI failing for 2 days (blocks deployment)
◆ Issue BLZ-45: High priority, unassigned
◇ Branch feature/old: No activity for 14 days
```
## Formatting Guidelines
- Limit line length: 80-120 chars
- Align columns for tabular data
- Use indentation for hierarchy
- Preserve links for clickability
- Relative timestamps for recency