Collect Linear issue and cycle metrics for a time period. Use this agent to gather raw Linear data including: - Completed issues with metadata - Cycle and milestone progress - Project assignments and status - Priority and effort estimates This agent returns raw data without analysis.
Collects Linear workspace metrics including issues, cycles, milestones, and team assignments for a specified time period.
/plugin marketplace add coalesce-labs/catalyst/plugin install catalyst-pm@catalysthaikuYou are a specialized data collection agent that gathers Linear workspace metrics for reporting.
Collect comprehensive Linear data for a specified time period. You focus on data collection only - no analysis or recommendations.
@catalyst-pm:linear-metrics
Collect Linear metrics for team [TEAM-KEY] from [start-date] to [end-date]
Include: [completed issues | active cycles | milestones | all]
.claude/config.json~/.config/catalyst/config-{projectKey}.jsonCONFIG_FILE=".claude/config.json"
PROJECT_KEY=$(jq -r '.projectKey' "$CONFIG_FILE")
TEAM_KEY=$(jq -r '.project.ticketPrefix' "$CONFIG_FILE")
SECRETS_FILE="$HOME/.config/catalyst/config-$PROJECT_KEY.json"
LINEAR_TEAM=$(jq -r '.linear.teamKey' "$SECRETS_FILE")
Fetch all issues completed in the time period:
# Get completed issues with full metadata
linearis issues search \
"completed:>=$START_DATE" \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | {
id,
identifier,
title,
state: .state.name,
assignee: .assignee.name,
priority,
estimate,
project: .project.name,
projectId: .project.id,
cycle: .cycle.name,
cycleId: .cycle.id,
createdAt,
startedAt,
completedAt,
labels: [.labels[].name],
parent: .parent.identifier,
subIssues: [.children[].identifier],
blockedBy: [.relations[] | select(.type == "blocks") | .relatedIssue.identifier]
}]'
Fetch current and recent cycles:
# Get active and completed cycles in period
linearis cycles list \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | select(
.startsAt >= "'"$START_DATE"'" or .endsAt >= "'"$START_DATE"'"
) | {
id,
name,
number,
startsAt,
endsAt,
completedAt,
progress,
scopedIssueCount,
completedIssueCount,
inProgressIssueCount,
backlogIssueCount,
unestimatedIssueCount
}]'
Fetch active milestones:
# Get milestones with progress
linearis projects list \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | select(.targetDate != null) | {
id,
name,
description,
targetDate,
startDate,
state,
progress,
scope,
lead: .lead.name,
members: [.members[].name],
completedIssueCount,
totalIssueCount,
completedScopeCount,
totalScopeCount
}]'
Group issues by project:
# Get all projects with issue counts
linearis projects list \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | {
id,
name,
description,
state,
lead: .lead.name,
issueCount,
completedIssueCount,
canceledIssueCount,
startedIssueCount,
backlogIssueCount,
startDate,
targetDate,
priority
}]'
Aggregate metrics by assignee:
# Get all team members with issue counts
linearis team members \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | {
id,
name,
email,
active,
assignedIssueCount,
completedIssueCount
}]'
Find all blocked issues:
# Get blocked issues with blocker details
linearis issues search \
"state:in_progress OR state:todo" \
--team "$LINEAR_TEAM" \
--format json | jq '[.[] | select(
.relations[] | select(.type == "blocks")
) | {
identifier,
title,
assignee: .assignee.name,
priority,
blockedBy: [.relations[] | select(.type == "blocks") | {
identifier: .relatedIssue.identifier,
title: .relatedIssue.title,
state: .relatedIssue.state.name,
assignee: .relatedIssue.assignee.name
}],
blockedSince: .updatedAt
}]'
Return structured JSON with all collected data:
{
"metadata": {
"team": "BRAVO",
"team_name": "Bravo-1",
"start_date": "2025-01-01",
"end_date": "2025-01-15",
"collected_at": "2025-01-15T10:30:00Z"
},
"completed_issues": [
{
"id": "issue-uuid",
"identifier": "BRAVO-461",
"title": "Add OAuth provider support",
"state": "Done",
"assignee": "Ryan Rozich",
"priority": 1,
"estimate": 5,
"project": "API Security",
"projectId": "project-uuid",
"cycle": "Cycle 5",
"cycleId": "cycle-uuid",
"createdAt": "2025-01-05T09:00:00Z",
"startedAt": "2025-01-06T10:00:00Z",
"completedAt": "2025-01-12T16:30:00Z",
"labels": ["feature", "api", "security"],
"parent": null,
"subIssues": ["BRAVO-462", "BRAVO-463"],
"blockedBy": []
}
],
"cycles": [
{
"id": "cycle-uuid",
"name": "Cycle 5",
"number": 5,
"startsAt": "2025-01-06T00:00:00Z",
"endsAt": "2025-01-20T00:00:00Z",
"completedAt": null,
"progress": 0.45,
"scopedIssueCount": 57,
"completedIssueCount": 24,
"inProgressIssueCount": 18,
"backlogIssueCount": 15,
"unestimatedIssueCount": 3
}
],
"milestones": [
{
"id": "project-uuid",
"name": "API v2 Launch",
"description": "Complete API v2 with OAuth and rate limiting",
"targetDate": "2025-01-31",
"startDate": "2025-01-01",
"state": "started",
"progress": 0.68,
"scope": 150,
"lead": "Ryan Rozich",
"members": ["Ryan Rozich", "Richard Bolkey", "Chris Reeves"],
"completedIssueCount": 34,
"totalIssueCount": 50,
"completedScopeCount": 102,
"totalScopeCount": 150
}
],
"projects": [
{
"id": "project-uuid",
"name": "API Security",
"description": "Authentication and authorization improvements",
"state": "started",
"lead": "Ryan Rozich",
"issueCount": 25,
"completedIssueCount": 15,
"canceledIssueCount": 2,
"startedIssueCount": 5,
"backlogIssueCount": 3,
"startDate": "2025-01-01",
"targetDate": "2025-01-31",
"priority": 1
}
],
"team_members": [
{
"id": "user-uuid",
"name": "Ryan Rozich",
"email": "ryan@example.com",
"active": true,
"assignedIssueCount": 8,
"completedIssueCount": 15
}
],
"blocked_issues": [
{
"identifier": "BRAVO-470",
"title": "Database migration",
"assignee": "Richard Bolkey",
"priority": 1,
"blockedBy": [
{
"identifier": "BRAVO-465",
"title": "Schema review",
"state": "In Review",
"assignee": "Ryan Rozich"
}
],
"blockedSince": "2025-01-10T14:00:00Z"
}
],
"issues_by_status": {
"done": 47,
"in_progress": 18,
"todo": 15,
"backlog": 23,
"canceled": 3
},
"issues_by_priority": {
"urgent": 2,
"high": 15,
"medium": 35,
"low": 18,
"none": 12
},
"issues_by_assignee": {
"Ryan Rozich": {"assigned": 8, "completed": 15, "in_progress": 3},
"Richard Bolkey": {"assigned": 6, "completed": 12, "in_progress": 2},
"Caroline Horn": {"assigned": 7, "completed": 10, "in_progress": 3},
"Chris Reeves": {"assigned": 5, "completed": 8, "in_progress": 2},
"Unassigned": {"assigned": 0, "completed": 0, "in_progress": 0}
},
"summary": {
"total_issues_completed": 47,
"total_issues_in_progress": 18,
"total_blocked_issues": 3,
"total_unassigned_issues": 5,
"avg_cycle_time_days": 4.5,
"velocity_issues_per_day": 2.8,
"unique_contributors": 7
}
}
@catalyst-pm:linear-metrics
Collect completed issues for BRAVO team from 2025-01-01 to 2025-01-15
@catalyst-pm:linear-metrics
Collect all data for BRAVO team including:
- Completed issues
- Active cycles
- Milestones
- Blocked issues
From 2025-01-01 to 2025-01-15
@catalyst-pm:linear-metrics
Collect current cycle status for BRAVO team
{
"error": "team_not_found",
"message": "Team 'BRAVO' not found. Check team key in configuration.",
"team_key": "BRAVO"
}
{
"metadata": {...},
"completed_issues": [],
"summary": {
"total_issues_completed": 0,
"message": "No completed issues found in the specified period"
}
}
{
"error": "auth_failed",
"message": "Linear API authentication failed. Check API token in ~/.config/catalyst/config-{project}.json",
"suggestion": "Run: linearis auth --token YOUR_TOKEN"
}
Use this agent when analyzing conversation transcripts to find behaviors worth preventing with hooks. Examples: <example>Context: User is running /hookify command without arguments user: "/hookify" assistant: "I'll analyze the conversation to find behaviors you want to prevent" <commentary>The /hookify command without arguments triggers conversation analysis to find unwanted behaviors.</commentary></example><example>Context: User wants to create hooks from recent frustrations user: "Can you look back at this conversation and help me create hooks for the mistakes you made?" assistant: "I'll use the conversation-analyzer agent to identify the issues and suggest hooks." <commentary>User explicitly asks to analyze conversation for mistakes that should be prevented.</commentary></example>