status-reporting
This skill should be used when checking project status, starting sessions, reviewing activity, or when "sitrep", "status report", or "what's changed" are mentioned.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install outfitter-dev-agents-status-reporting
Repository
Skill path: baselayer/skills/status-reporting
This skill should be used when checking project status, starting sessions, reviewing activity, or when "sitrep", "status report", or "what's changed" are mentioned.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: outfitter-dev.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install status-reporting into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/outfitter-dev/agents before adding status-reporting to shared team environments
- Use status-reporting for productivity workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: status-reporting
version: 1.0.0
description: This skill should be used when checking project status, starting sessions, reviewing activity, or when "sitrep", "status report", or "what's changed" are mentioned.
---
# Status Reporting
Gather -> aggregate -> present pattern for comprehensive project status across VCS, PRs, issues, CI.
<when_to_use>
- Starting work sessions (context refresh)
- Checking project/team activity
- Understanding PR/stack relationships
- Quick status overview before planning
- Reviewing recent changes across systems
- Understanding blockers
NOT for: deep-dive into specific items, real-time monitoring, single-source queries
</when_to_use>
<core_pattern>
**Three-phase workflow**:
1. **Gather** - collect from multiple sources
2. **Aggregate** - combine, filter, cross-reference by time/stack/status
3. **Present** - format for scanning with actionable insights
Key principles:
- Multi-source integration (VCS + code review + issues + CI)
- Time-aware filtering (natural language -> query params)
- Stack-aware organization (group by branch hierarchy)
- Scannable output (visual indicators, relative times)
- Actionable insights (highlight blockers, failures)
</core_pattern>
<workflow>
**Phase 1: Parse Constraints**
Extract time from natural language:
- "last X hours" -> `-Xh`
- "past X days" / "last X days" -> `-Xd`
- "yesterday" -> `-1d`
- "this morning" / "today" -> `-12h`
- "this week" -> `-7d`
- "since {date}" -> calculate days back
Default: 7 days if unspecified.
**Phase 2: Gather Data**
Run parallel queries for each available source:
1. **VCS State** - branch/stack structure, recent commits, working dir status
2. **Code Review** - open PRs, CI status, review decisions, activity
3. **Issues** - recently updated, status, priority, assignments
4. **CI/CD** - pipeline runs, success/failure, error summaries
Skip unavailable sources gracefully.
**Phase 3: Aggregate**
Cross-reference and organize:
- Group PRs by stack position (if stack-aware)
- Filter all by time constraint
- Correlate issues with PRs/branches
- Identify blockers (failed CI, blocking reviews)
- Calculate relative timestamps
**Phase 4: Present**
Format for scanning:
- Hierarchical sections (VCS -> PRs -> Issues -> CI)
- Visual indicators (`✓` `✗` `⏳` for status)
- Relative timestamps for recency
- Highlight attention-needed items
- Include links for deep-dive
See [templates.md](references/templates.md) for section formats.
</workflow>
<data_sources>
**VCS** - stack visualization, commit history, working dir state
- Stack-aware (Graphite, git-stack): hierarchical branch relationships
- Standard git: branch, log, remote tracking
**Code Review** - PRs/MRs, CI checks, reviews, comments
- Platforms: GitHub, GitLab, Bitbucket, Gerrit
**Issues** - recent updates, metadata, repo relationships
- Platforms: Linear, Jira, GitHub Issues, GitLab Issues
**CI/CD** - runs, success/failure, timing, errors
- Platforms: GitHub Actions, GitLab CI, CircleCI, Jenkins
Tool-specific: [graphite.md](references/graphite.md), [github.md](references/github.md), [linear.md](references/linear.md), [beads.md](references/beads.md)
</data_sources>
<aggregation>
**Cross-Referencing**:
1. PRs to branches (by name)
2. Issues to PRs (by ID in title/body)
3. CI runs to PRs (by number/SHA)
4. Issues to repos (by reference)
**Stack-Aware Organization**:
- Group PRs by hierarchy
- Show parent/child relationships
- Indicate current position
- Highlight blockers in stack order
**Filtering**:
- Time: apply to all sources, use most recent update
- Status: prioritize action-needed, open before closed
**Relative Timestamps**:
- < 1 hour: "X minutes ago"
- < 24 hours: "X hours ago"
- < 7 days: "X days ago"
- >= 7 days: "X weeks ago" or absolute
</aggregation>
<presentation>
**Visual Indicators**:
- `✓` success | `✗` failure | `⏳` pending | `⏸` draft | `🔴` blocker
- `▓▓▓░░` progress (3/5)
- `◇` minor | `◆` moderate | `◆◆` severe
**Output Structure**:
```
=== STATUS REPORT: {repo} ===
Generated: {timestamp}
{Time filter if applicable}
{VCS_SECTION}
{PR_SECTION}
{ISSUE_SECTION}
{CI_SECTION}
⚠️ ATTENTION NEEDED
{blockers and action items}
```
See [templates.md](references/templates.md) for detailed section templates.
</presentation>
<scripts>
Use `scripts/sitrep.ts` for automated gathering:
```bash
./scripts/sitrep.ts # All sources, 24h default
./scripts/sitrep.ts -t 7d # Last 7 days
./scripts/sitrep.ts -s github # Specific sources
./scripts/sitrep.ts --format=text
```
Outputs JSON (structured) or text (human-readable). Reduces agent tool calls 80%+.
See [implementation.md](references/implementation.md) for script structure and patterns.
</scripts>
<dependencies>
**Required**: VCS tool (git, gt, jj), shell access
**Optional** (graceful degradation):
- Code review CLI (gh, glab)
- Issue tracker MCP/API
- CI/CD platform API
Works with ANY available subset.
</dependencies>
<rules>
ALWAYS:
- Parse time constraints before queries
- Execute queries in parallel
- Handle missing sources gracefully
- Use relative timestamps
- Highlight actionable items
- Provide links for deep-dive
- Format for scanning
NEVER:
- Fail entirely if one source unavailable
- Block on slow queries (use timeouts)
- Expose credentials
- Dump raw data without organization
</rules>
<integration>
**As session starter**:
1. Generate report (understand state)
2. Identify attention-needed items
3. Plan work (prioritize by blockers)
4. Return periodically (track progress)
**Cross-skill references**:
- Failing CI -> [debugging-and-diagnosis](../debugging-and-diagnosis/SKILL.md)
- Before planning -> use report for context
- When blocked -> check dependencies
**Automation**: daily standup, pre-commit hooks, PR creation context
</integration>
<references>
Tool integrations:
- [graphite.md](references/graphite.md) - Graphite stack and PR queries
- [github.md](references/github.md) - GitHub CLI patterns
- [linear.md](references/linear.md) - Linear MCP integration
- [beads.md](references/beads.md) - Local issue tracking
Implementation:
- [templates.md](references/templates.md) - Output templates and formatting
- [implementation.md](references/implementation.md) - Patterns, scripts, anti-patterns
Examples:
- [EXAMPLES.md](EXAMPLES.md) - Usage examples and sample output
Formatting:
- [FORMATTING.md](../../shared/rules/FORMATTING.md) - Visual conventions
</references>
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/templates.md
```markdown
# 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
```
### references/graphite.md
```markdown
# 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
```
### references/github.md
```markdown
# 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
```
```
### references/linear.md
```markdown
# 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
```
### references/beads.md
```markdown
# 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
```
### references/implementation.md
```markdown
# 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
```
### ../debugging-and-diagnosis/SKILL.md
```markdown
---
name: debugging-and-diagnosis
version: 2.1.0
description: This skill should be used when encountering bugs, errors, failing tests, or unexpected behavior. Provides systematic debugging with evidence-based root cause investigation using a four-phase framework.
---
# Systematic Debugging
Evidence-based investigation -> root cause -> verified fix.
<when_to_use>
- Bugs, errors, exceptions, crashes
- Unexpected behavior or wrong results
- Failing tests (unit, integration, e2e)
- Intermittent or timing-dependent failures
- Performance issues (slow, memory leaks, high CPU)
- Integration failures (API, database, external services)
NOT for: obvious fixes, feature requests, architecture planning
</when_to_use>
<iron_law>
**NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST**
Never propose solutions or "try this" without understanding root cause through systematic investigation.
</iron_law>
<phases>
Track with TodoWrite. Phases advance forward only.
| Phase | Trigger | activeForm |
|-------|---------|------------|
| Collect Evidence | Session start | "Collecting evidence" |
| Isolate Variables | Evidence gathered | "Isolating variables" |
| Formulate Hypotheses | Problem isolated | "Formulating hypotheses" |
| Test Hypothesis | Hypothesis formed | "Testing hypothesis" |
| Verify Fix | Fix identified | "Verifying fix" |
**Situational** (insert when triggered):
- Iterate -> Hypothesis disproven, loops back with new hypothesis
**Workflow:**
- Start: "Collect Evidence" as `in_progress`
- Transition: Mark current `completed`, add next `in_progress`
- Failed hypothesis: Add "Iterate" task
- Quick fixes: If root cause obvious from error, skip to "Verify Fix" (still create failing test)
- Need more evidence: Add new evidence task (don't regress phases)
- Circuit breaker: After 3 failed hypotheses -> escalate
</phases>
<quick_start>
1. Create "Collect Evidence" todo as `in_progress`
2. Reproduce - exact steps to trigger consistently
3. Investigate - gather evidence about what's happening
4. Analyze - compare working vs broken, find differences
5. Test hypothesis - single specific hypothesis, minimal test
6. Implement - failing test first, then fix
7. Update todos on phase transitions
</quick_start>
<phase_1_root_cause>
Goal: Understand what's actually happening.
Transition: Mark complete when you have reproduction steps and initial evidence.
**Read error messages completely**
- Stack traces top to bottom
- Note file paths, line numbers, variable names
- Look for "caused by" chains
**Reproduce consistently**
- Document exact trigger steps
- Note inputs that cause vs don't cause
- Check if intermittent (timing, race conditions)
- Verify in clean environment
**Check recent changes**
- `git diff` - what changed?
- `git log --since="yesterday"` - recent commits
- Dependency updates
- Config/environment changes
**Gather evidence**
- Add logging at key points
- Print variable values at transformations
- Log function entry/exit with parameters
- Capture timestamps for timing issues
**Trace data flow backward**
- Where does bad value come from?
- Track through transformations
- Find first place it becomes wrong
Red flags (return to evidence gathering):
- "I think maybe X is the problem"
- "Let's try changing Y"
- "It might be related to Z"
- Starting to write code before understanding
</phase_1_root_cause>
<phase_2_pattern_analysis>
Goal: Learn from working code to understand broken code.
Transition: Mark complete when key differences identified.
**Find working examples**
- Search for similar functionality that works
- `rg "pattern"` for similar patterns
- Look for passing vs failing tests
- Check git history for when it worked
**Read references completely**
- Every line, not skimming
- Full context
- All dependencies/imports
- Configuration and setup
**Identify every difference**
- Line by line working vs broken
- Different imports?
- Different function signatures?
- Different error handling?
- Different data flow?
- Different configuration?
**Understand dependencies**
- Libraries/packages involved
- Versions in use
- External services
- Shared state
- Assumptions made
Questions to answer:
- Why does working version work?
- What's fundamentally different?
- Edge cases working version handles?
- Invariants working version maintains?
</phase_2_pattern_analysis>
<phase_3_hypothesis_testing>
Goal: Test one specific idea with minimal change.
Transition: Mark complete when specific, evidence-based hypothesis formed.
**Form single hypothesis**
- Template: "X is root cause because Y"
- Must explain all symptoms
- Must be testable with small change
- Must be based on evidence from phases 1-2
**Design minimal test**
- Smallest change to test hypothesis
- Change ONE variable
- Preserve everything else
- Make reversible
**Execute and verify**
- Apply change
- Run reproduction steps
- Observe carefully
- Document results
**Outcomes:**
- Fixed: Confirm across all cases, proceed to Verify Fix
- Not fixed: Mark complete, add "Iterate", form NEW hypothesis
- Partially fixed: Add "Iterate" for remaining issues
- Never: Random variations hoping one works
Bad hypotheses (too vague):
- "Maybe it's a race condition"
- "Could be caching or permissions"
- "Probably something with the database"
Good hypotheses (specific, testable):
- "Fails because expects number but receives string when API returns empty"
- "Race condition: fetchData() called before initializeClient() completes"
- "Memory leak: event listeners in useEffect never removed in cleanup"
</phase_3_hypothesis_testing>
<phase_4_implementation>
Goal: Fix root cause permanently with verification.
Transition: Root cause confirmed, ready for permanent fix.
**Create failing test**
- Write test reproducing bug
- Verify fails before fix
- Should pass after fix
- Captures exact broken scenario
**Implement single fix**
- Address identified root cause
- No additional "improvements"
- No refactoring "while you're there"
- Just fix the problem
**Verify fix**
- Failing test now passes
- Existing tests still pass
- Manual reproduction no longer triggers bug
- No new errors/warnings
**Circuit breaker**
If 3+ fixes tried without success: STOP
- Problem isn't hypothesis - problem is architecture
- May be using wrong pattern entirely
- Escalate or redesign
**After fixing:**
- Mark "Verify Fix" completed
- Add defensive validation
- Document root cause
- Consider similar bugs elsewhere
</phase_4_implementation>
<red_flags>
STOP and return to Phase 1 if you catch yourself:
- "Quick fix for now, investigate later"
- "Just try changing X and see"
- "I don't fully understand but this might work"
- "One more fix attempt" (already tried 2+)
- "Let me try a few different things"
- Proposing solutions before gathering evidence
- Skipping failing test case
- Fixing symptoms instead of root cause
ALL mean: STOP. Add new "Collect Evidence" task.
</red_flags>
<escalation>
When to escalate:
1. After 3 failed fix attempts - architecture may be wrong
2. No clear reproduction - need more context/access
3. External system issues - need vendor/team involvement
4. Security implications - need security expertise
5. Data corruption risks - need backup/recovery planning
</escalation>
<completion>
Before claiming "fixed":
- [ ] Root cause identified with evidence
- [ ] Failing test case created
- [ ] Fix addresses root cause only
- [ ] Test now passes
- [ ] All existing tests pass
- [ ] Manual reproduction no longer triggers bug
- [ ] No new warnings/errors
- [ ] Root cause documented
- [ ] Prevention measures considered
- [ ] "Verify Fix" marked completed
**Understanding the bug is more valuable than fixing it quickly.**
</completion>
<rules>
ALWAYS:
- Create "Collect Evidence" todo at session start
- Follow four-phase framework
- Update todos on phase transitions
- Create failing test before fix
- Test single hypothesis at a time
- Document root cause after fix
- Mark "Verify Fix" complete only after tests pass
NEVER:
- Propose fixes without understanding root cause
- Skip evidence gathering
- Test multiple hypotheses simultaneously
- Skip failing test case
- Fix symptoms instead of root cause
- Continue after 3 failed fixes without escalation
- Regress phases - add new tasks if needed
</rules>
<references>
- [playbooks.md](references/playbooks.md) - bug-type specific investigations
- [evidence-patterns.md](references/evidence-patterns.md) - diagnostic techniques
- [reproduction.md](references/reproduction.md) - reproduction techniques
- [integration.md](references/integration.md) - workflow integration, anti-patterns
- [FORMATTING.md](../../shared/rules/FORMATTING.md) - formatting conventions
</references>
```
### EXAMPLES.md
```markdown
# Status Reporting Examples
## Basic Usage
No time filter - defaults to 7 days:
```
User: "Give me a status report"
Agent: {parses as default 7-day window}
{gathers from available sources}
{presents structured report}
```
## Time-Constrained
Natural language parsing:
```
User: "Status report for last 24 hours"
Agent: {parses "last 24 hours" → "-24h"}
{applies to all source queries}
{presents filtered report with "Last 24 hours" header}
```
## Multi-Source Report
Full context gathering:
```
Agent gathers:
- Graphite stack (3 branches, 3 PRs)
- GitHub PR status (2 passing CI, 1 failing)
- Linear issues (5 updated recently)
- CI details (12 runs, 2 failures)
Agent presents:
- Stack visualization with PR status
- PR details with CI/review state
- Issue activity sorted by priority
- CI summary with failure links
- Attention section: 1 failing CI, 1 unassigned high-priority issue
```
## Graceful Degradation
Limited source availability:
```
Agent detects:
- git available (no Graphite)
- gh CLI available
- No Linear MCP
- No CI access
Agent presents:
- Standard git status (branch, commits)
- GitHub PR section (from gh CLI)
- Note: "Linear and CI sections unavailable"
```
## Sample Output
```
=== STATUS REPORT: my-project ===
Generated: 2024-01-15 14:30 UTC
Time filter: Last 24 hours
📊 GRAPHITE STACK
main
├─ feature/auth: ✓ synced [3 commits]
│ PR #42: Open | CI: ✓ 8/8 | Reviews: ⏳ 0/1
│ Updated: 2 hours ago
└─ feature/auth-refresh: ✓ synced [2 commits]
PR #43: Draft | CI: ⏳ running
Updated: 30 minutes ago
🔀 PULL REQUESTS (2 open)
PR #42: Add JWT authentication [Open]
Author: @dev | Updated: 2 hours ago
CI: ✓ 8/8 checks | Reviews: ⏳ awaiting review
PR #43: Token refresh flow [Draft]
Author: @dev | Updated: 30 minutes ago
CI: ⏳ 3/8 checks running
📋 ISSUES (3 updated)
AUTH-123: Implement refresh tokens [In Progress]
Priority: High | Assignee: @dev
Updated: 1 hour ago
AUTH-124: Add rate limiting [Todo]
Priority: Medium | Assignee: unassigned
Updated: 4 hours ago
🔧 CI/CD (5 runs)
Success: 3 | Failed: 1 | In Progress: 1
Recent Failures:
lint-check: ESLint found 2 errors
https://github.com/org/repo/actions/runs/123
⚠️ ATTENTION NEEDED
◆ PR #42: Awaiting review for 2 hours
◇ AUTH-124: High priority, unassigned
```
```
### ../../shared/rules/FORMATTING.md
```markdown
# Formatting Conventions
## Markdown in Instructions
Avoid `**bold**` and other emphasis markers in skill/instruction text unless explicitly formatting output. Claude doesn't need visual emphasis to understand importance — the words themselves convey it.
**Use markdown when**: formatting actual output, examples, or user-facing content
**Skip markdown when**: writing instructions, rules, or guidance for Claude
## Concision Principle
Sacrifice grammar for concision — drop articles, filler words, verbose phrases. But don't strip meaning or context. Goal is density, not minimalism. If removing a word makes the instruction ambiguous, keep it.
Good: "Ask one question, wait for response"
Bad: "Ask question wait response"
## Variables and Placeholders
**Variables** — all caps, no spaces, curly braces:
- `{VARIABLE}` — concrete placeholder to be replaced
- Examples: `{N}`, `{REASON}`, `{BAR}`, `{NAME}`, `{FILE_PATH}`
**Instructional prose** — lowercase, spaces inside braces:
- `{ description of what goes here }` — guidance for what to fill in
- Examples: `{ question text }`, `{ why it matters }`, `{ if needed }`
## XML Tags in Skills
Use XML tags for structural sections in skill files:
- `<when_to_use>` — trigger conditions
- `<confidence>` — confidence levels/tracking
- `<phases>` — workflow phases
- `<workflow>` — core process loop
- `<rules>` — always/never constraints
- `<references>` — links to supporting docs
Keep content inside tags terse. Sacrifice grammar for concision where meaning is preserved.
**GitHub rendering**: Add blank lines after opening tags and before closing tags. Without them, content renders incorrectly on GitHub.
```markdown
<!-- Good -->
<rules>
- First rule
- Second rule
</rules>
<!-- Bad — won't render properly on GitHub -->
<rules>
- First rule
- Second rule
</rules>
```
## Markdown Tables
Use markdown tables for structured data. Ensure the table is properly formatted with the correct number of columns and rows.
```markdown
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| Data 1 | Data 2 | Data 3 |
```
Ensure pipes are properly aligned with spaces surrounding text or hyphens, at least between the header and separator rows. If pipes are used within a cell, ensure they are properly escaped.
## Indicators
Prefer ASCII/Unicode over emoji for terminal output (Claude Code, CLI, interactive sessions). Emoji acceptable in docs or user-facing content where rendering is reliable.
### Progress
- `░` — empty (light shade)
- `▓` — filled (medium shade)
- Example: `▓▓▓░░` = 3/5
- Use for confidence, completion, capacity — anything with discrete levels
### Severity
Escalating:
- `◇` — minor/informational
- `◆` — moderate/warning
- `◆◆` — severe/blocking
- Use for pushback, risk, alerts, uncertainty levels
### Caveats
- `△` — incomplete/uncertain (warning triangle U+25B3)
- **Mid-stream**: `△` + description — flags issue for immediate attention
- **At delivery**: `△ Caveats` — summary section of gaps, unknowns, assumptions, concerns, deferred items
### Checkmarks
- `✓` — completed/decided (U+2713)
- Use for "Decisions Made:" lists, completed items, confirmed choices
- Example:
```text
Decisions Made:
✓ /simplify offers two modes: quick (skill) vs deep (agent)
✓ Agent returns: complexity identified + alternatives + escalation level
✓ Uses ◇/◆/◆◆ indicators from complexity-analysis skill
```
### Emphasis
Append to text:
- `★` — recommended/preferred
## Interactive Questions
For multi-option questions in skills:
- Use `EnterPlanMode` — enables keyboard navigation
- **Prose above tool**: context, "why it matters"
- **Inside tool**: options with inline recommendation marker
- Always include escape hatch: "5. Something else — { brief prompt }"
### Inline Recommendations
Mark recommended option inline with `[★]` + emphasized rationale:
```text
1. Google only [★] — simplest, highest coverage *good starting point, expand later*
2. Google + GitHub — covers consumer and developer users
3. Google + GitHub + Microsoft — comprehensive, more maintenance
```
Pattern: `N. Option name [★] — brief description *why recommended*`
- `[★]` visually distinguishes the recommendation
- `*italicized rationale*` provides quick reasoning
- Everything scannable in one place
## TodoWrite
Give todos friendly, context-specific descriptions instead of generic phase names. The description should tell the user what's actually happening.
**Prefer**:
```text
- [x] Consider skills to load
- [ ] Prep auth system requirements
- [ ] Explore authentication approaches
- [ ] Clarify platform and fallback needs
- [ ] Deliver implementation plan
```
**Avoid**:
```text
- [ ] Gather Context
- [ ] Synthesize Requirements
- [ ] Provide Deliverables
```
## Markdown Links
Use short aliases for readability. Keep paths intact.
Prefer: `[filename.md](path/to/filename.md)`
Avoid: `[path/to/filename.md](path/to/filename.md)`
```text
# Good
- [confidence.md](references/confidence.md)
- [FORMATTING.md](../../shared/rules/FORMATTING.md)
# Avoid
- [references/confidence.md](references/confidence.md)
- [../../shared/rules/FORMATTING.md](../../shared/rules/FORMATTING.md)
```
```
### scripts/sitrep.ts
```typescript
#!/usr/bin/env bun
/**
* sitrep.ts - Status report orchestrator
*
* Entry point for gathering status data from multiple sources.
* Runs gatherers in parallel, aggregates results, outputs JSON or text.
*
* Usage:
* ./sitrep.ts # All sources, 24h default
* ./sitrep.ts -t 7d # All sources, last 7 days
* ./sitrep.ts -s github,beads # Specific sources only
* ./sitrep.ts -t 24h -s graphite # Combined
* ./sitrep.ts --format=text # Human-readable output
*/
import { parseArgs } from "node:util";
import { formatTimeConstraint, toRelativeTime } from "./lib/time";
import type {
BeadsData,
GathererResult,
GitHubData,
GraphiteData,
LinearData,
SitrepResult,
} from "./lib/types";
const SOURCES = ["graphite", "github", "linear", "beads"] as const;
type Source = (typeof SOURCES)[number];
const { values } = parseArgs({
args: Bun.argv.slice(2),
options: {
time: { type: "string", short: "t", default: "24h" },
sources: { type: "string", short: "s" },
format: { type: "string", short: "f", default: "json" },
help: { type: "boolean", short: "h" },
},
});
if (values.help) {
console.log(`
sitrep.ts - Generate status report across multiple sources
Usage:
./sitrep.ts [options]
Options:
-t, --time <constraint> Time constraint (24h, 7d, 2w) [default: 24h]
-s, --sources <list> Comma-separated sources: graphite,github,linear,beads,all
[default: auto-detect available]
-f, --format <fmt> Output format: json, text [default: json]
-h, --help Show this help
Examples:
./sitrep.ts # All available sources, last 24 hours
./sitrep.ts -t 7d # Last 7 days
./sitrep.ts -s github,beads # Only GitHub and Beads
./sitrep.ts --format=text # Human-readable output
Sources:
graphite - Stack structure, branches, PR status (requires gt CLI)
github - Open PRs, CI status, workflow runs (requires gh CLI)
linear - Issues from Linear (requires Linear MCP in Claude settings)
beads - Local issues from .beads/ directory
`);
process.exit(0);
}
// Get script directory for running gatherers
const scriptDir = import.meta.dir;
async function runGatherer<T>(source: Source): Promise<GathererResult<T>> {
const gathererPath = `${scriptDir}/gatherers/${source}-gatherer.ts`;
const timeValue = values.time ?? "24h";
const args = [gathererPath, "-t", timeValue];
const proc = Bun.spawn(["bun", ...args], {
stdout: "pipe",
stderr: "pipe",
});
const stdout = await new Response(proc.stdout).text();
const stderr = await new Response(proc.stderr).text();
const exitCode = await proc.exited;
if (exitCode !== 0) {
return {
source,
status: "error",
error: stderr || `Gatherer exited with code ${exitCode}`,
timestamp: new Date().toISOString(),
};
}
try {
return JSON.parse(stdout) as GathererResult<T>;
} catch {
return {
source,
status: "error",
error: `Failed to parse gatherer output: ${stdout.slice(0, 200)}`,
timestamp: new Date().toISOString(),
};
}
}
function parseSources(): Source[] {
if (!values.sources || values.sources === "all") {
return [...SOURCES];
}
const requested = values.sources
.split(",")
.map((s) => s.trim().toLowerCase());
const valid: Source[] = [];
for (const s of requested) {
if (SOURCES.includes(s as Source)) {
valid.push(s as Source);
} else {
console.error(`Warning: Unknown source "${s}", skipping`);
}
}
return valid.length > 0 ? valid : [...SOURCES];
}
async function gatherAll(sources: Source[]): Promise<SitrepResult> {
const timestamp = new Date().toISOString();
// Run all gatherers in parallel
const promises = sources.map(async (source) => {
switch (source) {
case "graphite":
return { source, result: await runGatherer<GraphiteData>(source) };
case "github":
return { source, result: await runGatherer<GitHubData>(source) };
case "linear":
return { source, result: await runGatherer<LinearData>(source) };
case "beads":
return { source, result: await runGatherer<BeadsData>(source) };
}
});
const settled = await Promise.allSettled(promises);
// Build results object
const results: SitrepResult["results"] = {};
for (const item of settled) {
if (item.status === "fulfilled") {
const { source, result } = item.value;
results[source] = result;
}
}
return {
timeConstraint: values.time ?? "24h",
timestamp,
sources,
results,
};
}
// Text formatting helpers
function formatTextReport(result: SitrepResult): string {
const lines: string[] = [];
const timeLabel = formatTimeConstraint(result.timeConstraint);
lines.push(`SITREP — ${timeLabel}`);
lines.push(`Generated: ${new Date(result.timestamp).toLocaleString()}`);
lines.push("");
// Graphite section
if (result.results.graphite) {
const g = result.results.graphite;
if (g.status === "success" && g.data) {
const data = g.data as GraphiteData;
lines.push(
`📊 GRAPHITE (${data.stacks.length} stacks, ${data.branches.length} branches)`,
);
lines.push(` Current: ${data.currentBranch}`);
for (const branch of data.branches) {
const current = branch.isCurrent ? " ●" : "";
const pr = branch.prNumber ? ` PR #${branch.prNumber}` : "";
const status = branch.prStatus ? ` [${branch.prStatus}]` : "";
const flags: string[] = [];
if (branch.needsRestack) flags.push("needs restack");
if (branch.needsSubmit) flags.push("needs submit");
const flagStr = flags.length > 0 ? ` (${flags.join(", ")})` : "";
lines.push(` ${branch.name}${current}${pr}${status}${flagStr}`);
}
lines.push("");
} else if (g.status === "unavailable") {
lines.push(`📊 GRAPHITE: ${g.reason}`);
lines.push("");
}
}
// GitHub section
if (result.results.github) {
const gh = result.results.github;
if (gh.status === "success" && gh.data) {
const data = gh.data as GitHubData;
lines.push(`🔀 GITHUB (${data.openPRs.length} open PRs)`);
lines.push(` Repo: ${data.repo}`);
for (const pr of data.openPRs) {
const draft = pr.isDraft ? " [draft]" : "";
const ci = pr.statusCheckRollup?.state
? ` CI: ${pr.statusCheckRollup.state.toLowerCase()}`
: "";
const review = pr.reviewDecision
? ` Review: ${pr.reviewDecision.toLowerCase()}`
: "";
const time = toRelativeTime(pr.updatedAt);
lines.push(` #${pr.number}: ${pr.title}${draft}`);
lines.push(` ${ci}${review} — ${time}`);
}
if (data.recentRuns.length > 0) {
const failed = data.recentRuns.filter(
(r) => r.conclusion === "failure",
).length;
const passed = data.recentRuns.filter(
(r) => r.conclusion === "success",
).length;
lines.push(` Workflow runs: ${passed} passed, ${failed} failed`);
}
lines.push("");
} else if (gh.status === "unavailable") {
lines.push(`🔀 GITHUB: ${gh.reason}`);
lines.push("");
}
}
// Linear section
if (result.results.linear) {
const lin = result.results.linear;
if (lin.status === "success" && lin.data) {
const data = lin.data as LinearData;
lines.push(`📋 LINEAR (${data.issues.length} issues)`);
for (const issue of data.issues.slice(0, 10)) {
const assignee = issue.assignee ? ` @${issue.assignee.name}` : "";
const time = toRelativeTime(issue.updatedAt);
lines.push(` ${issue.identifier}: ${issue.title}`);
lines.push(` [${issue.state.name}]${assignee} — ${time}`);
}
lines.push("");
} else if (lin.status === "unavailable") {
lines.push(`📋 LINEAR: ${lin.reason}`);
lines.push("");
}
}
// Beads section
if (result.results.beads) {
const b = result.results.beads;
if (b.status === "success" && b.data) {
const data = b.data as BeadsData;
const { stats } = data;
lines.push(
`📝 BEADS (${stats.total} total, ${stats.open} open, ${stats.in_progress} active, ${stats.blocked} blocked)`,
);
if (data.inProgress.length > 0) {
lines.push(" In Progress:");
for (const issue of data.inProgress) {
const time = toRelativeTime(issue.updated_at);
lines.push(` ${issue.id}: ${issue.title} — ${time}`);
}
}
if (data.ready.length > 0) {
lines.push(" Ready to Work:");
for (const issue of data.ready.slice(0, 5)) {
const priority = ["", "🔴", "🟠", "🟡", "⚪"][issue.priority] || "";
lines.push(` ${priority} ${issue.id}: ${issue.title}`);
}
}
if (data.blocked.length > 0) {
lines.push(` Blocked (${data.blocked.length}):`);
for (const issue of data.blocked.slice(0, 3)) {
lines.push(` ⛔ ${issue.id}: ${issue.title}`);
}
}
if (data.recentlyClosed.length > 0) {
lines.push(` Recently Closed (${data.recentlyClosed.length}):`);
for (const issue of data.recentlyClosed.slice(0, 3)) {
const time = toRelativeTime(issue.closed_at || issue.updated_at);
lines.push(` ✓ ${issue.id}: ${issue.title} — ${time}`);
}
}
lines.push("");
} else if (b.status === "unavailable") {
lines.push(`📝 BEADS: ${b.reason}`);
lines.push("");
}
}
return lines.join("\n");
}
// Main execution
async function main() {
const sources = parseSources();
const result = await gatherAll(sources);
if (values.format === "text") {
console.log(formatTextReport(result));
} else {
console.log(JSON.stringify(result, null, 2));
}
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});
```