plugins/dev/skills/linear/SKILL.md
Manage Linear tickets with workflow automation. **ALWAYS use when** the user says 'create a ticket', 'update the ticket', 'move ticket to', 'search Linear', or wants to create tickets from thoughts documents, update ticket status, or manage the Linear workflow. Uses Linearis CLI.
npx skillsauth add coalesce-labs/catalyst linearInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are tasked with managing Linear tickets, including creating tickets from thoughts documents, updating existing tickets, and following a structured workflow using the Linearis CLI.
Before creating ANY ticket, apply the /catalyst-dev:gherkin-ticket standard. Every ticket must
open with a scannable use-case — an outcome-first title (<actor> should <outcome> [so that <benefit>], no internal-mechanism jargon, no [Component] prefix) and a body that leads with a
plain-English use case followed by tiered Gherkin (Given/When/Then) acceptance criteria. This is a
hard gate: do not draft a title or description without first consulting that skill. Component goes in
a Linear label, not the title.
First, verify that Linearis CLI is installed and configured:
if ! command -v linearis &> /dev/null; then
echo "❌ Linearis CLI not found"
echo ""
echo "Install with:"
echo " npm install -g linearis"
echo ""
echo "Configure with:"
echo " export LINEAR_API_TOKEN=your_token"
echo " # or create ~/.linear_api_token file"
exit 1
fi
Read team configuration from .catalyst/config.json:
CONFIG_FILE=".catalyst/config.json"
[[ ! -f "$CONFIG_FILE" ]] && CONFIG_FILE=".claude/config.json"
# Read team key (e.g., "ENG", "PROJ")
TEAM_KEY=$(jq -r '.catalyst.linear.teamKey // "PROJ"' "$CONFIG_FILE")
# Read team UUID — required for issues create and issues search (keys don't work)
# Use `linearis teams usage` to discover UUIDs — see /catalyst-dev:linearis
TEAM_UUID=$(jq -r '.catalyst.linear.teamUuid // empty' "$CONFIG_FILE")
if [ -z "$TEAM_UUID" ]; then
echo "WARNING: Could not resolve team UUID for $TEAM_KEY. issues create/search may target wrong team."
echo "Add teamUuid to .catalyst/config.json — see /catalyst-dev:linearis for lookup commands"
fi
# Read thoughts repo URL
THOUGHTS_URL=$(jq -r '.catalyst.linear.thoughtsRepoUrl // "https://github.com/org/thoughts/blob/main"' "$CONFIG_FILE")
Configuration in .catalyst/config.json:
{
"catalyst": {
"linear": {
"teamKey": "ENG",
"teamUuid": "<team-uuid>"
}
}
}
To find your team UUID, see /catalyst-dev:linearis for team discovery commands.
If tools are available, respond based on the user's request:
I can help you with Linear tickets. What would you like to do?
1. Create a new ticket from a thoughts document
2. Add a comment to a ticket (I'll use our conversation context)
3. Search for tickets
4. Update ticket status or details
5. Move ticket through workflow
Then wait for the user's input.
This workflow ensures alignment through planning before implementation:
Catalyst maps workflow phases to your Linear workspace states via stateMap in
.catalyst/config.json. Default mapping (matches standard Linear states):
| Workflow Phase | Default State | Config Key |
| ---------------- | ------------- | --------------------- |
| New tickets | Backlog | stateMap.backlog |
| Acknowledged | Todo | stateMap.todo |
| Research started | In Progress | stateMap.research |
| Planning started | In Progress | stateMap.planning |
| Implementation | In Progress | stateMap.inProgress |
| Verify phase | In Progress | stateMap.verifying |
| Review phase | In Progress | stateMap.reviewing |
| PR created | In Review | stateMap.inReview |
| Completed | Done | stateMap.done |
| Canceled | Canceled | stateMap.canceled |
Customization: Override any key to match your workspace. Set to null to skip that transition.
Note: These states must exist in your Linear workspace. The defaults match what Linear provides out of the box (plus "In Review" which is commonly added to the Started category).
Review and alignment happen at the plan stage (not PR stage) to move faster and avoid rework.
These commands automatically update ticket status using stateMap config:
/research-codebase → Moves ticket to stateMap.research (default: "In Progress")/create-plan → Moves ticket to stateMap.planning (default: "In Progress")/implement-plan → Moves to stateMap.inProgress (default: "In Progress")/create-pr → Moves to stateMap.inReview (default: "In Review")/merge-pr → Moves to stateMap.done (default: "Done")When referencing thoughts documents, always provide GitHub links:
thoughts/shared/... → {thoughtsRepoUrl}/repos/{project}/shared/...thoughts/{user}/... → {thoughtsRepoUrl}/repos/{project}/{user}/...thoughts/global/... → {thoughtsRepoUrl}/global/...Locate and read the thoughts document:
Analyze the document content:
Check for related context (if mentioned in doc):
Draft the ticket summary following the /catalyst-dev:gherkin-ticket standard. Present a
draft to the user:
## Draft Linear Ticket
**Title**: [outcome-first use-case sentence — <actor> should <outcome> [so that <benefit>];
no mechanism/file/symbol names, no [Component] prefix]
**Description**:
[short plain-English use case — who benefits and why — so a reader who didn't write it gets oriented]
[Gherkin acceptance criteria in a ```gherkin fenced block, at the right tier:
A = features/bugs (full Given/When/Then), B = bugs (Then states correct behavior + # CURRENTLY:),
C = pure chores (Context/Motivation/Outcome prose)]
## Technical notes
- [implementation detail, technical decisions, constraints — preserved, but BELOW the use case]
- [any specific requirements]
## References
- Source: `thoughts/[path]` ([View on GitHub](converted URL))
- Related code: [any file:line references]
---
Based on the document, this seems to be at the stage of: [ideation/planning/ready to implement]
Interactive refinement: Ask the user:
Note: Ticket will be created in "Backlog" status by default.
Create the Linear ticket using Linearis CLI:
Use linearis issues usage for create syntax, or see /catalyst-dev:linearis.
Important: --team only accepts UUIDs, not team keys/names (upstream bug:
czottmann/linearis#56). Team keys silently fall back to the workspace default. Use the
$TEAM_UUID from config above.
Linearis creates issues in the team's default backlog state. To set specific status or assignee, create first then update. Capture the created issue ID from the JSON output with jq.
Link genuine prerequisites as formal blockers (CTL-838): If you know of work that must
finish before this ticket can start, set it as a Linear blocked_by link now — do NOT rely
on mentioning the id in the description (Catalyst does not infer dependencies from prose; a
mention is not a dependency). See /catalyst-dev:linearis for syntax:
linearis issues update <NEW-TICKET> --blocked-by <PREREQ-TICKET>
Link only TRUE prerequisites (must reach Done/Canceled first). Do NOT link across teams for auto-sequencing — the daemon only works its own team, so a cross-team blocker only deadlocks. Missing a blocker is fine (phase-triage does a semantic second pass); a FALSE blocker stalls real work, so when in doubt leave it out.
Post-creation actions:
Add at the top of the document:
---
linear_ticket: [TEAM-123]
created: [date]
---
When user wants to add a comment to a ticket:
Determine which ticket:
linearis issues usage)Format comments for clarity:
File reference formatting:
thoughts/user/example.md([View](url))Comment structure example:
Implemented retry logic in webhook handler to address rate limit issues.
Key insight: The 429 responses were clustered during batch operations, so exponential backoff
alone wasn't sufficient - added request queuing.
Files updated:
- `src/webhooks/handler.ts` ([GitHub](link))
- `thoughts/shared/rate_limit_analysis.md` ([GitHub](link))
Add comment with Linearis (see linearis comments usage for syntax)
When moving tickets to a new status:
Get current status by reading the ticket (see linearis issues usage)
Suggest next status based on workflow:
State names come from stateMap in .catalyst/config.json:
Backlog → Todo (acknowledged)
Todo → In Progress (research/planning/implementation started)
In Progress → In Review (PR created)
In Review → Done (PR merged)
Teams with custom states can configure finer-grained transitions via stateMap.
Automatic status updates: When certain commands are run, automatically update ticket status
(state names read from stateMap config):
/research-codebase with ticket → Move to stateMap.research/create-plan with ticket → Move to stateMap.planning/implement-plan with ticket → Move to stateMap.inProgress/create-pr with ticket → Move to stateMap.inReview/merge-pr with ticket → Move to stateMap.doneManual status updates — use linearis issues usage for update syntax
Add comment explaining the transition — use linearis comments usage for syntax
When user wants to find tickets:
Gather search criteria:
Execute search using linearis issues usage for search/list syntax:
issues search for server-side query matchingissues list + jq for filtering by fields that search doesn't support--team requires a UUID on search (upstream bug: czottmann/linearis#56)Present results:
When these commands are run, check if there's a related Linear ticket and update it:
During /create-plan:
stateMap.planning (default: "In Progress")During /implement-plan:
stateMap.inProgress (default: "In Progress")During /create-pr:
stateMap.inReview (default: "In Review")During /merge-pr:
stateMap.done (default: "Done")# 1. Research and document
/catalyst-dev:research-codebase "authentication patterns"
# Saves to thoughts/shared/research/auth-patterns.md
# 2. Create ticket from research
/catalyst-dev:linear create thoughts/shared/research/auth-patterns.md
# Creates ticket in Backlog
# 3. Create plan
/catalyst-dev:create-plan
# Reads research, creates plan
# Ticket moves to stateMap.planning (default: "In Progress")
# 4. Implement
/catalyst-dev:implement-plan thoughts/shared/plans/2025-01-08-auth-feature.md
# Ticket moves to stateMap.inProgress (default: "In Progress")
# 5. Create PR
/catalyst-dev:create-pr
# Ticket moves to stateMap.inReview (default: "In Review")
# 6. Merge PR
/catalyst-dev:merge-pr
# Ticket moves to stateMap.done (default: "Done")
Add a progress comment, move the ticket forward using the state name from stateMap config, and
search for related tickets. Use linearis issues usage and linearis comments usage for exact
syntax.
For Linearis CLI syntax, see the linearis skill reference.
.catalyst/config.json for team settings and stateMap for state nameslinear.stateMap to match your Linear workspace statesstateMapThis command integrates seamlessly with the create-plan → implement-plan → validate-plan workflow while keeping Linear tickets in sync!
testing
Phase-agent that fixes a failing verify verdict so the pipeline self-heals instead of stalling to needs-human (CTL-653). Reads `${ORCH_DIR}/workers/<ticket>/verify.json`, fixes the `findings[]` (every severity:"high" plus the regression_risk drivers) directly via Edit/Write, commits the remediation, and emits `phase.remediate.complete.<ticket>`. The scheduler's router then re-dispatches `verify` to re-check (the verify⇄remediate cycle, cap 3). Dispatched as a `claude --bg` job by `phase-agent-dispatch`, which invokes it via slash command — hence `user-invocable: true`.
tools
--- name: phase-triage description: Phase agent that triages a Linear ticket — expands acronyms, classifies (feature/bug/docs/refactor/chore), identifies genuine blockers (a semantic second-pass over the backlog — NOT a prose scrape; CTL-838), estimates scope, writes triage.json, and posts a triage analysis comment to Linear. Triage completion is signaled by that comment plus the local triage.json — there is no `triaged` label. Emits phase.triage.complete.<TICKET> on success and phase.triage.fai
tools
Phase agent for the research step of the 9-phase orchestrator pipeline (CTL-450). Wraps /catalyst-dev:research-codebase and produces thoughts/shared/research/<date>-<ticket>.md, then emits phase.research.complete.<ticket>. Reads triage.json from the worker dir as its prior-phase artifact. Spawned via plugins/dev/scripts/phase-agent-dispatch, which invokes it via slash command — hence `user-invocable: true`.
development
Phase-agent wrapper that opens the pull request after implementation completes (CTL-449 Initiative 1 Phase 3). Delegates to `/catalyst-dev:create-pr` (which already auto-runs `describe-pr` and transitions Linear to `inReview`), then writes the PR number + URL into the phase signal file so the downstream `phase-monitor-merge` agent can read it without re-querying GitHub. Dispatched as a `claude --bg` job by `phase-agent-dispatch`, which invokes it via slash command — hence `user-invocable: true`.