This system keeps Linear and GitHub in sync automatically. Linear API queries go through the linear-sync:api subagent (foreground or background), while simple mutations use direct MCP tool calls from the main agent.
If no last_issue is present, skip option 1 and start with options 2-4 (renumber accordingly).
When the dev picks any issue (resume, existing, or new): If the issue is unassigned, auto-assign it via direct MCP call (mcp__<server>__update_issue) — see Execution Model, Tier 1. No need to ask — if they're working on it, they should own it.
Keep the kickoff brief and natural: "What are you working on today in ?"
Setup Wizard Rules
When a [LINEAR-SETUP] directive is injected:
- Always use AskUserQuestion for every choice. The dev never types workspace names, project IDs, or labels manually.
- Always offer "This repo doesn't use Linear" as an option. If chosen, opt the repo out via Read/Write tools (read state file, set
workspace: "none", write back) and move on.
- Use the
linear-sync:api subagent in foreground mode to fetch real data from Linear MCP (workspaces, teams, projects, labels). Include mcp_server and scripts_dir from hook context (or from state file if setting up a new workspace).
- Present fetched data as AskUserQuestion choices.
- For labels, suggest
repo:<repo-name> as the default.
- Detect the GitHub org from the repo's remote URL. Include it as
github_org in the committed config.
- Write
.claude/linear-sync.json to the repo root with the shared config.
- Write only the
workspace reference to the local state file for credential routing. Project, team, and label live in the repo-level config.
- The subagent will commit and push
.claude/linear-sync.json as part of the Link Repo task.
- After setup, confirm in one line and proceed with the session kickoff.
Project Rules
- Multiple repos can share the same project. Include "Create a new project" as the last option.
- If the dev picks "Create a new project", ask for a name, delegate to the subagent to create it.
Enforcement Rules (Linked Repos Only)
These conventions are mechanically enforced by hooks. They apply ONLY to repos with a valid workspace link.
| Action | Requirement | Enforcement |
|---|
git commit -m "..." | Issue ID in message (e.g., ENG-123: fix bug) | Hook blocks with exit 2 |
git checkout -b / git switch -c | Issue ID in branch name (e.g., ENG-123-fix-bug) | Hook blocks with exit 2 |
gh pr create --title "..." | Issue ID in PR title (e.g., ENG-123: Fix the bug) | Hook blocks with exit 2 |
git push | Cross-issue commit check | Hook allows with advisory context |
| Issue creation | Repo label auto-applied and auto-created if missing | Subagent handles |
| Issue creation | Check for duplicates first | Subagent search + AskUserQuestion |
| Issue creation | Infer priority from keywords | AskUserQuestion if detected |
| Issue creation | Offer cycle/sprint assignment | AskUserQuestion if active cycle |
| Issue picked / resumed | Auto-assign if unassigned | Direct MCP (update_issue) |
gh pr create (after success) | Offer a progress comment on the Linear issue | PostToolUse hook reminder + AskUserQuestion |
git push (final push) | Offer a progress comment on the Linear issue | PostToolUse hook reminder + AskUserQuestion |
| Session ending / major milestone | Offer a progress comment on the Linear issue | AskUserQuestion |
Linear Comments
IMPORTANT: You MUST offer a comment at every natural stopping point. Never post automatically, but always ask.
The PostToolUse hook automatically injects a [Linear Comment Reminder] after git push and gh pr create in linked repos. When you see this reminder, follow the comment flow below.
Natural stopping points (offer every time):
- After creating a PR — this is the most important one
- After a significant push when wrapping up a chunk of work
- When the dev says they're done or is ending the session
Smart comment drafts: Before drafting a comment, run git log main..HEAD --oneline (or the appropriate base branch) to gather the commit history for this branch. Use the commit messages to auto-draft a concise progress summary.
Use AskUserQuestion with a draft comment:
I can add a quick update to ENG-423:
"Added sliding window rate limiter (3 commits):
- Redis-backed per-user rate limiting
- Configurable window size and limits
- 94% test coverage
PR: #142"
1. Post this comment
2. Let me edit it
3. Skip
If they pick "edit", ask them what they'd like it to say. Never post without showing them exactly what will be posted first. If they skip, don't ask again until the next stopping point.
Posting comments: Use direct MCP call (mcp__<server>__create_comment) — see Execution Model, Tier 1. No subagent needed for simple mutations.
Issue Status
If the dev explicitly asks to change a status, do it. No pushback, no warnings. Use direct MCP call (mcp__<server>__update_issue) and confirm.
For automatic status changes, only act at these moments:
- "Start something new" — Set to In Progress on creation.
- Blocked commit recovery — Set to In Progress on creation.
Don't automatically change status beyond those two cases. The Linear <-> GitHub integration handles the rest.
Commit Message Formatting
Commits in linked repos must include an issue ID (e.g., ENG-123: description). Do NOT append Co-Authored-By lines to commit messages. Keep commit messages clean: just the issue ID prefix and a concise description.
Branch Naming
Auto-generate branch names from the issue ID and title. Slugify and truncate the description portion to keep the total branch name under 50 characters.
Examples:
alice/ENG-456-add-sliding-window-rate
alice/CP-89-fix-auth-timeout-mobile
Keep the meaningful words, drop filler. The dev never manually names branches in a linked repo.
Blocked Commit Recovery
When a commit, branch, or PR is blocked by the hook for missing an issue ID:
- Proactively offer to create a Linear ticket. Do not just report the error.
- Use AskUserQuestion to ask for a one-line description of the work.
- Delegate to
linear-sync:api subagent (foreground) to create the ticket. Include mcp_server and scripts_dir from hook context.
- Retry the original command with the new issue ID inserted.
- Do not make the dev start over or re-type their commit message.
Duplicate Issue Detection
Before creating any new issue:
- Delegate to
linear-sync:api subagent (background) with the "Search Issues" task. Include mcp_server and scripts_dir from hook context.
- If duplicates found, present them via AskUserQuestion with option to use existing or create anyway.
- If no duplicates, proceed with creation normally.
Blocking Issue Warnings
When an issue is fetched, check for blocker warnings. If blockers are present:
- Show the warning: "Heads up: this issue is blocked by ENG-100."
- Do NOT prevent the dev from working on it. It's advisory.
- Use AskUserQuestion: "Work on it anyway? / Switch to the blocking issue instead? / Pick a different issue?"
PR Auto-Description
When gh pr create is about to run and you have issue context:
- Draft the PR body using issue title, description, and acceptance criteria.
- Structure the PR body as:
## Summary
<1-2 sentence summary from issue context>
## Linear Issue
<ISSUE_ID>: <title>
## Changes
<bullet points from `git log main..HEAD --oneline`>
## Acceptance Criteria
<from issue description, or "See Linear issue" if none>
- The issue ID must still appear in the
--title.
Cycle/Sprint Auto-Assignment
When creating a new issue:
- After creation, delegate to
linear-sync:api subagent (foreground) to fetch the active cycle for the team. Include mcp_server and scripts_dir from hook context.
- If an active cycle exists, use AskUserQuestion to offer adding the issue to it.
- If no active cycle, skip silently.
- To add: use direct MCP call (
mcp__<server>__update_issue with cycleId) — no subagent needed.
Priority Inference
Scan descriptions for urgency signals:
| Keywords | Suggested Priority |
|---|
urgent, asap, critical, emergency, p0, hotfix, production down, outage | 1 (Urgent) |
important, high priority, p1, blocker, blocking, regression | 2 (High) |
bug, fix, broken, error, p2 | 3 (Medium) |
nice to have, low priority, p3, p4, when possible, minor, cleanup, chore, refactor | 4 (Low) |
If keywords detected, use AskUserQuestion to confirm. If no keywords match, create without priority.
Cross-Issue Commit Warning
If the commit guard hook injects a [CROSS-ISSUE-COMMITS] advisory on push, show it: "Advisory: This branch has commits referencing different issues (ENG-123, ENG-456). This is usually fine for related work, but you may want to split into separate branches if the work is unrelated." This is a warning, not a block.
Closed Issues Without PRs
If the session-start digest mentions closed issues without linked PRs, surface it briefly: "Note: ENG-123 was marked Done but has no linked PR." This is informational only.
Team Config Templates
If a .linear-sync-template.json exists in the repo root and the hook injects [LINEAR-TEMPLATE], use its values as defaults during the setup wizard. Present them for confirmation: "Found a team config template. Use these defaults? Workspace: X, Project: Y, Team: Z, Label: W". The dev can override any value.
Execution Model
CRITICAL — Workspace isolation: Every subagent delegation and every direct MCP call MUST use the correct workspace's MCP server. The session-start hook injects mcp_server and scripts_dir in its context — these MUST be forwarded to the subagent in every delegation prompt. Using the wrong server routes operations to the wrong Linear workspace. Never omit, never default.
Operations use five tiers to minimize context consumption and maximize performance:
Tier 0: Hooks (zero context cost)
- GitHub sync — PostToolUse hook runs
sync-github-issues.sh after git push / gh pr create in linked repos. Injects [Linear Comment Reminder] with the result.
- Notification digest — SessionStart hook pre-fetches active issues inline via
linear-api.sh. Result appears in [LINEAR-DIGEST] context.
Tier 1: Direct MCP (minimal context, no subagent)
For fire-and-forget mutations the main agent handles directly using mcp__<server>__<tool>:
- Auto-assign:
mcp__<server>__update_issue with assigneeId
- Add to cycle:
mcp__<server>__update_issue with cycleId
- Post comment:
mcp__<server>__create_comment
- Status change:
mcp__<server>__update_issue with stateId
- Quick fetch:
mcp__<server>__get_issue for compact inline results
Determine <server> from the mcp_server field in session-start context (e.g., mcp_server: linear-acme). NEVER default to linear — using the wrong server routes to the wrong workspace. If mcp_server is missing from hook context, read ~/.claude/linear-sync/state.json, find the workspace for the current repo, and use its mcp_server field. If still missing, delegate to the linear-sync:api subagent (foreground) to resolve it. These are auto-approved MCP tool calls — no permission prompts needed.
Tier 2: Background subagent (linear-sync:api)
For multi-step operations where the main agent doesn't need the result immediately. Subagent uses MCP tools internally, returns concise 1-3 line summaries:
- Fetch Issue Summary — issue details + blockers, parent (if a sub-issue), milestone, and open sub-issues
- Fetch My Issues — assigned in-progress issues list with parent, milestone, and open-sub-issue-count context inline
- Search Issues — duplicate detection before creation
Tier 3: Foreground subagent (linear-sync:api)
For operations where the main agent blocks on the result:
- Create Issue — need the ID to proceed with branch/commit
- Link Repo / Setup Wizard — interactive multi-step
- Fetch Active Cycle — need info for cycle assignment AskUserQuestion
Tier 4: Bash linear-api.sh (fallback only)
For operations MCP tools can't handle, or for hook scripts running outside agent context. The linear-api-allow.sh PreToolUse hook auto-approves these calls. The script auto-detects the workspace MCP server from .claude/linear-sync.json + state file when no server name is passed — but always pass the server name explicitly when you have it.
Fallback notes: The Bash tool escapes ! to \! even inside single quotes, breaking GraphQL non-null types like CommentCreateInput!. If you must use linear-api.sh, use printf on a separate line to construct queries containing !:
SCRIPTS_DIR="/path/to/scripts"
QUERY=$(printf 'mutation($input: CommentCreateInput%s) { commentCreate(input: $input) { comment { id } } }' '!')
bash "$SCRIPTS_DIR/linear-api.sh" server-name "$QUERY" '{"input": {"issueId": "...", "body": "..."}}'
CRITICAL: Put each variable assignment and the bash call on separate lines — never chain with &&. The auto-approve hook validates each line independently and rejects &&/||/; chains. Using && will cause the user to see a manual approval prompt.
When delegating to the subagent, always include the mcp_server and scripts_dir from the session-start hook context in your prompt so the subagent knows which MCP server to use and where linear-api.sh is for fallback. Example: "Fetch issue summary for ENG-123. mcp_server: linear-acme, scripts_dir: /path/to/scripts"
Parallel execution
Always batch independent operations in the same message. Claude can make multiple tool calls simultaneously when they don't depend on each other. Key patterns:
- Issue resume + auto-assign: Run the subagent fetch AND the
update_issue MCP call in the same message when both are ready.
- Multiple independent mutations: If you need to assign + add to cycle + change status, batch all three MCP mutations in one message.
- State update + MCP call: Read/Write to the state file can happen in parallel with an MCP mutation since they're independent operations.
Sequential only when dependent: A mutation that needs data from a query must wait for the query to complete first. But two independent operations should always run in parallel.
Read/Write tools (no Bash needed)
Use the Read and Write tools for state file updates — no permission prompts, no subagent overhead:
- Save last_issue: Read
~/.claude/linear-sync/state.json, update repos.<repo>.last_issue and repos.<repo>.last_issue_title, Write back
- Opt repo out: Read state file, set
repos.<repo>.workspace to "none", Write back
Companion Files
This skill orchestrates components at the plugin root (path injected by session-start hook as scripts_dir):
- Subagent:
agents/api.md — handles Linear API queries (foreground and background modes)
- Hooks:
hooks/scripts/linear-session-start.sh (session init + inline digest), linear-commit-guard.sh (commit/branch/PR enforcement), linear-prompt-check.sh (issue reference injection), linear-post-push-sync.sh (GitHub sync + comment reminder)
- Scripts:
scripts/linear-api.sh (API wrapper), scripts/sync-github-issues.sh (GH sync)
- State:
~/.claude/linear-sync/state.json (credential routing, session state)