← 返回技能库 /Productivity and Collaboration

linear-claude-skill

Managing Linear issues, projects, and teams. Use when working with Linear tasks, creating issues, updating status, querying projects, or managing team workflows.

来源:官方 版本:3.4.1 作者:Ryan Smith <ryan@smithhorn.ca> 2 次浏览

Linear

Tools and workflows for managing issues, projects, and teams in Linear.


⚠️ Tool Availability (READ FIRST)

This skill supports multiple tool backends. Use whichever is available:

  1. MCP Tools (mcp__linear) - Use if available in your tool set
  2. Linear CLI (linear command) - Always available via Bash
  3. Helper Scripts - For complex operations

If MCP tools are NOT available, use the Linear CLI via Bash:

# View an issue
linear issues view ENG-123

# Create an issue
linear issues create --title "Issue title" --description "Description"

# Update issue status (get state IDs first)
linear issues update ENG-123 -s "STATE_ID"

# Add a comment
linear issues comment add ENG-123 -m "Comment text"

# List issues
linear issues list

Do NOT report "MCP tools not available" as a blocker - use CLI instead.


🔐 Security: Varlock Integration

CRITICAL: Never expose API keys in terminal output or Claude's context.

Safe Commands (Always Use)

# Validate LINEAR_API_KEY is set (masked output)
varlock load 2>&1 | grep LINEAR

# Run commands with secrets injected
varlock run -- npm run query -- "query { viewer { name } }"

# Check schema (safe - no values)
cat .env.schema | grep LINEAR

Unsafe Commands (NEVER Use)

# ❌ NEVER - exposes key to Claude's context
linear config show
echo $LINEAR_API_KEY
printenv | grep LINEAR
cat .env

Setup for New Projects

  1. Create .env.schema with @sensitive annotation:
    bash # @type=string(startsWith=lin_api_) @required @sensitive LINEAR_API_KEY=

  2. Add LINEAR_API_KEY to .env (never commit this file)

  3. Configure MCP to use environment variable:
    json { "mcpServers": { "linear": { "env": { "LINEAR_API_KEY": "${LINEAR_API_KEY}" } } } }

  4. Use varlock load to validate before operations


Quick Start (First-Time Users)

1. Check Your Setup

Run the setup check to verify your configuration:

npm run setup

This will check:
- LINEAR_API_KEY is set and valid
- @linear/sdk is installed
- Linear CLI availability (optional)
- MCP configuration (optional)

2. Get API Key (If Needed)

If setup reports a missing API key:

  1. Open Linear in your browser
  2. Go to Settings (gear icon) -> Security & access -> Personal API keys
  3. Click Create key and copy the key (starts with lin_api_)
  4. Add to your environment:
# Option A: Add to shell profile (~/.zshrc or ~/.bashrc)
export LINEAR_API_KEY="lin_api_your_key_here"

# Option B: Add to Claude Code environment
echo 'LINEAR_API_KEY=lin_api_your_key_here' >> ~/.claude/.env

# Then reload your shell or restart Claude Code

3. Test Connection

Verify everything works:

npm run query -- "query { viewer { name } }"

You should see your name from Linear.

4. Common Operations

# Create issue in a project
npm run ops -- create-issue "Project" "Add rate limiting to auth endpoints" "Auth endpoints have no rate limiting, allowing brute-force attacks. Add configurable limits per endpoint with 429 responses when exceeded."

# Update issue status
npm run ops -- status Done ENG-123 ENG-124

# Edit issue title or description (fix typos, expand bodies)
npm run ops -- update-issue ENG-123 description --file /tmp/description.md --force
npm run ops -- update-issue ENG-123 title "Corrected title" --force
echo "New body from pipeline" | npm run ops -- update-issue ENG-123 description --stdin --force
npm run ops -- update-issue ENG-123 description "Short inline update" --strict=false

# Create sub-issue
npm run ops -- create-sub-issue ENG-100 "Sub-task" "Details"

# Update project status
npm run ops -- project-status "Phase 1" completed

# Show all commands
npm run ops -- help

See Project Management Commands for full reference.


Issue Creation Checklist (Required)

When creating a Linear issue, always complete these three steps — even if the user doesn't mention them.

  1. Detailed description with Acceptance Criteria. Every issue description MUST include an ## Acceptance Criteria section with at least 2 concrete, testable checklist items. See docs/issue-template.md for the canonical template plus a populated full example. The CLI create-issue / create-sub-issue will reject descriptions missing this structure; for MCP save_issue callers, validate the draft first with npm run ops -- validate-description --stdin (see below). If the user provides only a title, draft the description yourself using the template below.

Depth — default to the full six-section template. Unless the user's phrasing clearly signals brevity ("quick issue", "one-liner", "just the AC", "brief", "terse", "minimum", "short"), structure the body as Context → Problem → Proposal → Acceptance Criteria → Verification → Out of scope. The 120-char / 2-item floor is what the validator rejects, not what reviewers want. If the user gives you only a title, draft a verbose body from the full template — ask follow-up questions rather than shipping the floor. For trivial changes (typo fix, one-line config tweak), collapsing Problem into Context and dropping Verification is fine when the AC is self-evidently testable — collapse deliberately, not by default.

```markdown
## Context
Title: </p> <p><What is changing and why. 2-4 sentences. Link prior issues, docs, or incidents that motivate this.></p> <p>## Problem<br> <What specifically is broken, missing, or insufficient today. Name the file, flow, or behavior.></p> <p>## Proposal<br> <What you intend to do about it. High-level approach, not implementation line-by-line.></p> <p>## Acceptance Criteria<br> - [ ] <Concrete, testable outcome><br> - [ ] <Concrete, testable outcome></p> <p>## Verification<br> <How the AC will actually be checked. Manual steps, test command, or review instruction.></p> <p>## Out of Scope<br> - <What this issue does NOT cover — redirect to the follow-up or explain why it's deferred><br> ```</p> <p>Print the template on demand with: <code>npm run ops -- create-issue --template</code>. See <a href="docs/issue-template.md">docs/issue-template.md</a> for a fully populated example.</p> <ol start="2"> <li> <p><strong>Labels.</strong> Apply from the <a href="docs/labels.md">label taxonomy</a>:<br> - Exactly ONE type label (<code>feature</code>, <code>bug</code>, <code>refactor</code>, <code>chore</code>, <code>spike</code>)<br> - 1-2 domain labels (<code>backend</code>, <code>frontend</code>, <code>security</code>, <code>infrastructure</code>, etc.)<br> - Scope labels if relevant (<code>blocked</code>, <code>breaking-change</code>, <code>tech-debt</code>)</p> </li> <li> <p><strong>Project assignment.</strong> Assign to the appropriate project based on context (active sprint, feature area, or user instruction). If no project is obvious, ask the user. In batch/subagent context, use the project associated with the parent issue or the default initiative project.</p> </li> </ol> <p><strong>When updating</strong> an existing issue, preserve existing labels and project — only add missing labels or correct misassigned ones.</p> <blockquote> <p><strong>MCP tools.</strong> Before calling <code>mcp__linear__save_issue</code> (or any MCP issue-create tool), pipe the draft description through <code>validate-description --stdin</code> and only call <code>save_issue</code> if it exits 0:</p> <p>```bash<br> echo "$DRAFT_BODY" | npm run ops -- validate-description --stdin</p> <h1 id="exit-0-safe-to-call-save_issue">exit 0 → safe to call save_issue</h1> <h1 id="exit-5-fix-the-description-re-pipe-try-again">exit 5 → fix the description; re-pipe; try again</h1> <p>```</p> <p>The CLI already gates this for <code>create-issue</code> / <code>create-sub-issue</code>. MCP has no server-side gate — this pre-flight + the retroactive <code>npm run lint-issues</code> audit are the only enforcement for the MCP path. For longer drafts in a file, use <code>--file <path></code> instead of <code>--stdin</code>.</p> <p><strong>Depth ≠ validation.</strong> Validation passing (exit 0) only means the 120-char / 2-AC floor is met. Structure the body as the full six-section template (Context → Problem → Proposal → AC → Verification → Out of Scope) unless the user explicitly asked for brevity — see bullet #1 above.</p> <p><strong>Enforcement model.</strong> CLI + SDK paths are hard-gated; the MCP path is instruction + audit. A PreToolUse hook that intercepts <code>save_issue</code> was considered and rejected: it only fires when Claude Code is the runtime, install is per-user, and the payload shape is harness-version-dependent. Run <code>npm run lint-issues -- --since 24h</code> locally or in CI to catch instruction-layer drift retroactively.</p> </blockquote> <hr> <h2 id="project-planning-workflow">Project Planning Workflow</h2> <blockquote> <p>See <a href="#issue-creation-checklist-required">Issue Creation Checklist</a> — descriptions, labels, and project assignment are required for every issue.</p> </blockquote> <h3 id="create-issues-in-the-correct-project-from-the-start">Create Issues in the Correct Project from the Start</h3> <p><strong>Best Practice</strong>: When planning a new phase or initiative, create the project and its issues together in a single planning session. Avoid creating issues in a catch-all project and moving them later.</p> <h4 id="recommended-workflow">Recommended Workflow</h4> <ol> <li> <p><strong>Create the project first</strong>:<br> <code>bash npm run ops -- create-project "Phase X: Feature Name" "My Initiative"</code></p> </li> <li> <p><strong>Set project state to Planned</strong>:<br> <code>bash npm run ops -- project-status "Phase X: Feature Name" planned</code></p> </li> <li> <p><strong>Create issues directly in the project</strong> (use <code>--template</code> to print the canonical template first, or pass a multi-line description via heredoc):<br> ```bash<br> # Print the template to seed your description<br> npm run ops -- create-issue --template</p> </li> </ol> <p># Create the issue with a template-shaped description<br> npm run ops -- create-issue "Phase X: Feature Name" "Parent task" "$(cat <<'EOF'<br> ## Context<br> Implement the core feature with integration tests and documentation.</p> <p>## Acceptance Criteria<br> - [ ] All API endpoints return correct responses<br> - [ ] Test coverage >80% on new modules<br> EOF<br> )" --labels feature,backend</p> <p>npm run ops -- create-sub-issue ENG-XXX "Sub-task 1" "$(cat <<'EOF'<br> ## Context<br> Set up database schema and migrations for the new feature tables.</p> <p>## Acceptance Criteria<br> - [ ] Migration runs cleanly on a fresh database<br> - [ ] Rollback migration restores prior schema<br> EOF<br> )"<br> ```</p> <ol start="4"> <li><strong>Update project state when work begins</strong>:<br> <code>bash npm run ops -- project-status "Phase X: Feature Name" in-progress</code></li> </ol> <h4 id="why-this-matters">Why This Matters</h4> <ul> <li><strong>Traceability</strong>: Issues are linked to their project from creation</li> <li><strong>Metrics</strong>: Project progress tracking is accurate from day one</li> <li><strong>Workflow</strong>: No time wasted moving issues between projects</li> <li><strong>Organization</strong>: Linear views and filters work correctly</li> </ul> <h4 id="anti-pattern-to-avoid">Anti-Pattern to Avoid</h4> <p>❌ Creating issues in a "holding" project and moving them later:</p> <pre class="highlight"><code class="language-bash"># Don't do this create-issue "Phase 6A" "New feature" # Wrong project # Later: manually move to Phase X # Extra work </code></pre> <hr> <h2 id="project-management-commands">Project Management Commands</h2> <h3 id="project-status">project-status</h3> <p>Update a project's state in Linear. Accepts user-friendly terminology that maps to Linear's API.</p> <pre class="highlight"><code class="language-bash">npm run ops -- project-status <project-name> <state> </code></pre> <p><strong>Valid States:</strong><br> | Input | Description | API Value |<br> |-------|-------------|-----------|<br> | <code>backlog</code> | Not yet started | backlog |<br> | <code>planned</code> | Scheduled for future | planned |<br> | <code>in-progress</code> | Currently active | started |<br> | <code>paused</code> | Temporarily on hold | paused |<br> | <code>completed</code> | Successfully finished | completed |<br> | <code>canceled</code> | Will not be done | canceled |</p> <p><strong>Examples:</strong></p> <pre class="highlight"><code class="language-bash"># Start working on a project npm run ops -- project-status "Phase 8: MCP Decision Engine" in-progress # Mark project complete npm run ops -- project-status "Phase 8" completed # Partial name matching works npm run ops -- project-status "Phase 8" paused </code></pre> <h3 id="link-initiative">link-initiative</h3> <p>Link an existing project to an initiative.</p> <pre class="highlight"><code class="language-bash">npm run ops -- link-initiative <project-name> <initiative-name> </code></pre> <p><strong>Examples:</strong></p> <pre class="highlight"><code class="language-bash"># Link a project to an initiative npm run ops -- link-initiative "Phase 8: MCP Decision Engine" "Q1 Goals" # Partial matching works npm run ops -- link-initiative "Phase 8" "Q1 Goals" </code></pre> <h3 id="unlink-initiative">unlink-initiative</h3> <p>Remove a project from an initiative.</p> <pre class="highlight"><code class="language-bash">npm run ops -- unlink-initiative <project-name> <initiative-name> </code></pre> <p><strong>Examples:</strong></p> <pre class="highlight"><code class="language-bash"># Remove incorrect link npm run ops -- unlink-initiative "Phase 8" "Linear Skill" # Clean up test links npm run ops -- unlink-initiative "Test Project" "Q1 Goals" </code></pre> <p><strong>Error Handling:</strong><br> - Returns error if project is not linked to the specified initiative<br> - Returns error if project or initiative not found</p> <h3 id="complete-project-lifecycle-example">Complete Project Lifecycle Example</h3> <pre class="highlight"><code class="language-bash"># 1. Create project linked to initiative npm run ops -- create-project "Phase 11: New Feature" "Q1 Goals" # 2. Set state to planned npm run ops -- project-status "Phase 11" planned # 3. Create issues in the project npm run ops -- create-issue "Phase 11" "Parent task" "Description" npm run ops -- create-sub-issue ENG-XXX "Sub-task 1" "Details" # 4. Start work - update to in-progress npm run ops -- project-status "Phase 11" in-progress # 5. Mark issues done npm run ops -- status Done ENG-XXX ENG-YYY # 6. Complete project npm run ops -- project-status "Phase 11" completed # 7. (Optional) Link to additional initiative npm run ops -- link-initiative "Phase 11" "Q2 Goals" </code></pre> <hr> <h2 id="tool-selection">Tool Selection</h2> <p>Choose the right tool for the task:</p> <table> <thead> <tr> <th>Priority</th> <th>Tool</th> <th>When to Use</th> </tr> </thead> <tbody> <tr> <td>1</td> <td><strong>MCP (Official Server)</strong></td> <td>Most operations - PREFERRED</td> </tr> <tr> <td>2</td> <td><strong><code>lin</code> CLI</strong></td> <td>Fast-path for reads/status updates when installed (optional)</td> </tr> <tr> <td>3</td> <td><strong>Helper Scripts</strong></td> <td>Bulk operations, label taxonomy, project workflows</td> </tr> <tr> <td>4</td> <td><strong>SDK scripts</strong></td> <td>Complex operations (loops, conditionals)</td> </tr> <tr> <td>5</td> <td><strong>GraphQL API</strong></td> <td>Operations not supported by above</td> </tr> </tbody> </table> <h3 id="lin-cli-optional-fast-path"><code>lin</code> CLI (Optional Fast-Path)</h3> <p>If the <a href="https://github.com/aaronkwhite/linear-cli"><code>lin</code></a> Rust binary is installed, the skill uses it automatically for:<br> - Issue status updates (<code>status</code>, <code>done</code>, <code>wip</code>)<br> - Listing initiatives<br> - Searching issues (<code>search <query></code>)<br> - Listing issues (<code>list-issues [--team X] [--state Y]</code>)<br> - User info (<code>whoami</code>)</p> <p>All operations fall back silently to the SDK when <code>lin</code> is unavailable.</p> <p><strong>Install</strong> (optional):</p> <pre class="highlight"><code class="language-bash">brew install aaronkwhite/tap/lin # macOS (Homebrew) cargo install lincli # Any platform with Rust </code></pre> <p><strong>Disable</strong>: Set <code>LINEAR_USE_LIN=0</code> to skip <code>lin</code> even when installed.</p> <h3 id="mcp-server-configuration">MCP Server Configuration</h3> <p><strong>Use the official Linear MCP server</strong> at <code>mcp.linear.app</code>:</p> <pre class="highlight"><code class="language-json">{ "mcpServers": { "linear": { "command": "npx", "args": ["mcp-remote", "https://mcp.linear.app/sse"], "env": { "LINEAR_API_KEY": "your_api_key" } } } } </code></pre> <blockquote> <p><strong>WARNING</strong>: Do NOT use deprecated community servers. See <a href="troubleshooting.md">troubleshooting.md</a> for details.</p> </blockquote> <h3 id="mcp-reliability-official-server">MCP Reliability (Official Server)</h3> <table> <thead> <tr> <th>Operation</th> <th>Reliability</th> <th>Notes</th> </tr> </thead> <tbody> <tr> <td>Create issue</td> <td>✅ High</td> <td>Full support</td> </tr> <tr> <td>Update status</td> <td>✅ High</td> <td>Use <code>state: "Done"</code> directly</td> </tr> <tr> <td>List/Search issues</td> <td>✅ High</td> <td>Supports filters, queries</td> </tr> <tr> <td>Add comment</td> <td>✅ High</td> <td>Works with issue IDs</td> </tr> </tbody> </table> <h3 id="quick-status-update">Quick Status Update</h3> <pre class="highlight"><code class="language-bash"># Via MCP - use human-readable state names update_issue with id="issue-uuid", state="Done" # Via helper script (bulk operations) node scripts/linear-helpers.mjs update-status Done 123 124 125 </code></pre> <h3 id="helper-script-reference">Helper Script Reference</h3> <p>For detailed helper script usage, see <strong><a href="troubleshooting.md">troubleshooting.md</a></strong>.</p> <h3 id="parallel-agent-execution">Parallel Agent Execution</h3> <p>For bulk operations or background execution, use the <code>Linear-specialist</code> subagent:</p> <pre class="highlight"><code class="language-javascript">Task({ description: "Update Linear issues", prompt: "Mark ENG-101, ENG-102, ENG-103 as Done", subagent_type: "Linear-specialist" }) </code></pre> <p><strong>When to use <code>Linear-specialist</code> (parallel):</strong><br> - Bulk status updates (3+ issues)<br> - Project status changes<br> - Creating multiple issues<br> - Sync operations after code changes</p> <p><strong>When to use direct execution:</strong><br> - Single issue queries<br> - Viewing issue details<br> - Quick status checks<br> - Operations needing immediate results</p> <p>See <strong><a href="sync.md">sync.md</a></strong> for parallel execution patterns.</p> <h2 id="image-uploads">Image Uploads</h2> <h3 id="step-1-extract-the-image-from-conversation-context">Step 1: Extract the image from conversation context</h3> <p>Images shared inline in Claude Code are <strong>not</strong> saved to disk automatically — they live as base64 in the session JSONL. Use the extraction script:</p> <pre class="highlight"><code class="language-bash"># Find the current session JSONL ls -t ~/.claude/projects/<project-path>/*.jsonl | head -1 # Extract all inline images (saves to /tmp by default) npm run extract-image -- <path-to-session.jsonl> # Or specify a custom output directory npm run extract-image -- <path-to-session.jsonl> ~/Desktop </code></pre> <p>This saves images to <code>/tmp/shared-image-0.png</code>, <code>/tmp/shared-image-1.png</code>, etc.</p> <blockquote> <p><strong>Always verify</strong> the extracted image with the Read tool before uploading.</p> </blockquote> <h3 id="step-2-create-the-issue">Step 2: Create the issue</h3> <pre class="highlight"><code class="language-bash"># Standard approach npm run ops -- create-issue "Project Name" "Issue title" "Description" </code></pre> <blockquote> <p><strong>Note</strong>: If you need to target a specific team and <code>create-issue</code> picks the wrong one, use GraphQL with explicit <code>teamId</code>:</p> <p>```bash</p> <h1 id="get-the-projects-team">Get the project's team</h1> <p>npm run query -- 'query { projects(filter: { name: { containsIgnoreCase: "PROJECT NAME" } }) { nodes { id name teams { nodes { id name key } } } } }'</p> <h1 id="create-with-explicit-teamid">Create with explicit teamId</h1> <p>npm run query -- 'mutation { issueCreate(input: { teamId: "TEAM_UUID", projectId: "PROJECT_UUID", title: "Issue title", description: "Description" }) { success issue { id identifier url } } }'<br> ```</p> </blockquote> <h3 id="step-3-upload-the-image-and-attach-to-the-issue">Step 3: Upload the image and attach to the issue</h3> <pre class="highlight"><code class="language-bash">npm run upload-image -- /tmp/shared-image-0.png ENG-123 "Optional comment text" </code></pre> <p>The script will:<br> 1. Upload the file to Linear's S3 storage<br> 2. Post a comment on the issue with the image embedded as markdown</p> <p><strong>Supported formats</strong>: PNG, JPG/JPEG, GIF, WebP, SVG, PDF</p> <h3 id="known-pitfalls">Known pitfalls</h3> <table> <thead> <tr> <th>Problem</th> <th>Cause</th> <th>Fix</th> </tr> </thead> <tbody> <tr> <td><code>create-issue</code> picks wrong team</td> <td>Multiple teams in workspace</td> <td>Use GraphQL with explicit teamId (see Step 2)</td> </tr> <tr> <td><code>upload-image.ts</code> "Issue not found"</td> <td>Issue was deleted before attaching</td> <td>Ensure issue exists first</td> </tr> <tr> <td>Image not found on disk</td> <td>Shared inline, not as file</td> <td>Extract from session JSONL (Step 1)</td> </tr> </tbody> </table> <hr> <h2 id="critical-requirements">Critical Requirements</h2> <h3 id="issues-projects-initiatives">Issues → Projects → Initiatives</h3> <p><strong>Every issue MUST be attached to a project. Every project MUST be linked to an initiative.</strong></p> <table> <thead> <tr> <th>Entity</th> <th>Must Link To</th> <th>If Missing</th> </tr> </thead> <tbody> <tr> <td>Issue</td> <td>Project</td> <td>Not visible in project board</td> </tr> <tr> <td>Project</td> <td>Initiative</td> <td>Not visible in roadmap</td> </tr> </tbody> </table> <p>See <strong><a href="projects.md">projects.md</a></strong> for complete project creation checklist.</p> <hr> <h2 id="conventions">Conventions</h2> <h3 id="issue-status">Issue Status</h3> <ul> <li><strong>Assigned to me</strong>: Set <code>state: "Todo"</code></li> <li><strong>Unassigned</strong>: Set <code>state: "Backlog"</code></li> </ul> <h3 id="labels">Labels</h3> <p>Uses <strong>domain-based label taxonomy</strong> — see <a href="#issue-creation-checklist-required">Issue Creation Checklist</a> for required rules and <a href="docs/labels.md">docs/labels.md</a> for the full taxonomy.</p> <pre class="highlight"><code class="language-bash"># Validate labels npm run ops -- labels validate "feature,security" # Suggest labels for issue npm run ops -- labels suggest "Fix XSS vulnerability" </code></pre> <h2 id="sdk-automation-scripts">SDK Automation Scripts</h2> <p><strong>Use only when MCP tools are insufficient.</strong> For complex operations involving loops, mapping, or bulk updates, write TypeScript scripts using <code>@linear/sdk</code>. See <code>sdk.md</code> for:</p> <ul> <li>Complete script patterns and templates</li> <li>Common automation examples (bulk updates, filtering, reporting)</li> <li>Tool selection criteria</li> </ul> <p>Scripts provide full type hints and are easier to debug than raw GraphQL for multi-step operations.</p> <h2 id="graphql-api">GraphQL API</h2> <p><strong>Fallback only.</strong> Use when operations aren't supported by MCP or SDK.</p> <p>See <strong><a href="api.md">api.md</a></strong> for complete documentation including:<br> - Authentication and setup<br> - Example queries and mutations<br> - Timeout handling patterns<br> - MCP timeout workarounds<br> - Shell script compatibility</p> <p><strong>Quick ad-hoc query:</strong></p> <pre class="highlight"><code class="language-bash">npm run query -- "query { viewer { name } }" </code></pre> <h2 id="projects-initiatives">Projects & Initiatives</h2> <p>For advanced project and initiative management patterns, see <strong><a href="projects.md">projects.md</a></strong>.</p> <p><strong>Quick reference</strong> - common project commands:</p> <pre class="highlight"><code class="language-bash"># Create project linked to initiative npm run ops -- create-project "Phase X: Name" "My Initiative" # Update project status npm run ops -- project-status "Phase X" in-progress npm run ops -- project-status "Phase X" completed # Link/unlink projects to initiatives npm run ops -- link-initiative "Phase X" "My Initiative" npm run ops -- unlink-initiative "Phase X" "Old Initiative" </code></pre> <p><strong>Key topics in projects.md:</strong><br> - Project creation checklist (mandatory steps)<br> - Content vs Description fields<br> - Discovery before creation<br> - Codebase verification before work<br> - Sub-issue management<br> - Project status updates<br> - Project updates (status reports)</p> <hr> <h2 id="sync-patterns-bulk-operations">Sync Patterns (Bulk Operations)</h2> <p>For bulk synchronization of code changes to Linear, see <strong><a href="sync.md">sync.md</a></strong>.</p> <p><strong>Quick sync commands:</strong></p> <pre class="highlight"><code class="language-bash"># Bulk update issues to Done npm run ops -- status Done ENG-101 ENG-102 ENG-103 # Update project status npm run ops -- project-status "My Project" completed </code></pre> <h3 id="bulk-issue-import-bulk-createts">Bulk Issue Import (<code>bulk-create.ts</code>)</h3> <p>Create many issues in one team from a manifest directory, each with its own<br> markdown description and optional media files. Media is uploaded via Linear's<br> <code>fileUpload</code> API and embedded in the description (images inline, other files<br> as links).</p> <p>Use case: importing customer feedback, retrospective action items, or custdev<br> tickets where each issue needs screenshots or recordings attached.</p> <pre class="highlight"><code class="language-bash">LINEAR_API_KEY=xxx npx tsx scripts/bulk-create.ts \ --manifest ./feedback-2026-04 \ --config ./feedback-2026-04/config.json </code></pre> <p>Preview the manifest first without creating issues or uploading files:</p> <pre class="highlight"><code class="language-bash">npx tsx scripts/bulk-create.ts \ --manifest ./feedback-2026-04 \ --config ./feedback-2026-04/config.json \ --dry-run </code></pre> <p><strong>Manifest directory:</strong></p> <pre class="highlight"><code>feedback-2026-04/ tickets.json # [{ key, title, priority?, labels?, files? }, ...] config.json # { team_key, state_name?, default_priority? } desc-<key>.md # description markdown per ticket (optional) <media files> # referenced by each ticket's files[] </code></pre> <p><strong>Config resolves by name, not UUID:</strong></p> <ul> <li><code>team_key</code> → looked up via <code>findTeamByKey</code> (e.g. <code>"ENG"</code>)</li> <li><code>state_name</code> → optional workflow state (e.g. <code>"Triage"</code>)</li> <li>Ticket <code>labels</code> → label names resolved per team case-insensitively; unknown<br> names warn by default, or fail with <code>--strict</code></li> </ul> <hr> <h2 id="reference">Reference</h2> <table> <thead> <tr> <th>Document</th> <th>Purpose</th> </tr> </thead> <tbody> <tr> <td><a href="api.md">api.md</a></td> <td>GraphQL API reference, timeout handling</td> </tr> <tr> <td><a href="sdk.md">sdk.md</a></td> <td>SDK automation patterns</td> </tr> <tr> <td><a href="sync.md">sync.md</a></td> <td>Bulk sync patterns</td> </tr> <tr> <td><a href="projects.md">projects.md</a></td> <td>Project & initiative management</td> </tr> <tr> <td><a href="troubleshooting.md">troubleshooting.md</a></td> <td>Common issues, MCP debugging</td> </tr> <tr> <td><a href="docs/labels.md">docs/labels.md</a></td> <td>Label taxonomy</td> </tr> </tbody> </table> <p><strong>External:</strong> <a href="https://linear.app/docs/mcp.md">Linear MCP Documentation</a></p> </article> </div> <aside class="detail-side"> <div class="side-card"> <h4>文件结构 <span class="side-count">55</span></h4> <ul class="file-tree"> <li title="CHANGELOG.md (22992 B)"> <span class="file-icon">📄</span>CHANGELOG.md </li> <li title="LICENSE (1056 B)"> <span class="file-icon">📄</span>LICENSE </li> <li title="README.md (12870 B)"> <span class="file-icon">📄</span>README.md </li> <li title="SKILL.md (23278 B)"> <span class="file-icon">📄</span>SKILL.md </li> <li title="api.md (8345 B)"> <span class="file-icon">📄</span>api.md </li> <li title="docs/issue-template.md (5435 B)"> <span class="file-icon">📄</span>issue-template.md </li> <li title="docs/labels.md (6748 B)"> <span class="file-icon">📄</span>labels.md </li> <li title="eslint.config.js (611 B)"> <span class="file-icon">📄</span>eslint.config.js </li> <li title="hooks/post-edit.sh (2430 B)"> <span class="file-icon">📄</span>post-edit.sh </li> <li title="package-lock.json (264905 B)"> <span class="file-icon">📄</span>package-lock.json </li> <li title="package.json (2220 B)"> <span class="file-icon">📄</span>package.json </li> <li title="projects.md (19559 B)"> <span class="file-icon">📄</span>projects.md </li> <li title="release.config.js (884 B)"> <span class="file-icon">📄</span>release.config.js </li> <li title="scripts/__tests__/bulk-create.test.ts (5079 B)"> <span class="file-icon">📄</span>bulk-create.test.ts </li> <li title="scripts/__tests__/issue-description.test.ts (6962 B)"> <span class="file-icon">📄</span>issue-description.test.ts </li> <li title="scripts/__tests__/lin-cli.test.ts (3900 B)"> <span class="file-icon">📄</span>lin-cli.test.ts </li> <li title="scripts/__tests__/lint-issues.test.ts (2341 B)"> <span class="file-icon">📄</span>lint-issues.test.ts </li> <li title="scripts/__tests__/retry.test.ts (8415 B)"> <span class="file-icon">📄</span>retry.test.ts </li> <li title="scripts/__tests__/smoke.test.ts (7200 B)"> <span class="file-icon">📄</span>smoke.test.ts </li> <li title="scripts/__tests__/validate-description.test.ts (4429 B)"> <span class="file-icon">📄</span>validate-description.test.ts </li> <li title="scripts/build.mjs (1454 B)"> <span class="file-icon">📄</span>build.mjs </li> <li title="scripts/bulk-create.ts (12820 B)"> <span class="file-icon">📄</span>bulk-create.ts </li> <li title="scripts/create-initiative-update.ts (5906 B)"> <span class="file-icon">📄</span>create-initiative-update.ts </li> <li title="scripts/create-issue-with-project.ts (8168 B)"> <span class="file-icon">📄</span>create-issue-with-project.ts </li> <li title="scripts/create-project-update.ts (5661 B)"> <span class="file-icon">📄</span>create-project-update.ts </li> <li title="scripts/extract-image.ts (2284 B)"> <span class="file-icon">📄</span>extract-image.ts </li> <li title="scripts/lib/agent-selection.ts (8921 B)"> <span class="file-icon">📄</span>agent-selection.ts </li> <li title="scripts/lib/exit-codes.ts (357 B)"> <span class="file-icon">📄</span>exit-codes.ts </li> <li title="scripts/lib/index.ts (3217 B)"> <span class="file-icon">📄</span>index.ts </li> <li title="scripts/lib/initiative.ts (5111 B)"> <span class="file-icon">📄</span>initiative.ts </li> <li title="scripts/lib/issue-description.ts (7291 B)"> <span class="file-icon">📄</span>issue-description.ts </li> <li title="scripts/lib/labels.ts (7735 B)"> <span class="file-icon">📄</span>labels.ts </li> <li title="scripts/lib/lin-cli.ts (7428 B)"> <span class="file-icon">📄</span>lin-cli.ts </li> <li title="scripts/lib/linear-utils.ts (9505 B)"> <span class="file-icon">📄</span>linear-utils.ts </li> <li title="scripts/lib/project-template.ts (9025 B)"> <span class="file-icon">📄</span>project-template.ts </li> <li title="scripts/lib/retry.ts (4878 B)"> <span class="file-icon">📄</span>retry.ts </li> <li title="scripts/lib/taxonomy-data.ts (8281 B)"> <span class="file-icon">📄</span>taxonomy-data.ts </li> <li title="scripts/lib/taxonomy-validation.ts (10238 B)"> <span class="file-icon">📄</span>taxonomy-validation.ts </li> <li title="scripts/lib/taxonomy.ts (3685 B)"> <span class="file-icon">📄</span>taxonomy.ts </li> <li title="scripts/lib/verify.ts (6775 B)"> <span class="file-icon">📄</span>verify.ts </li> <li title="scripts/linear-api.mjs (12385 B)"> <span class="file-icon">📄</span>linear-api.mjs </li> <li title="scripts/linear-ops.ts (70803 B)"> <span class="file-icon">📄</span>linear-ops.ts </li> <li title="scripts/lint-issues.ts (7045 B)"> <span class="file-icon">📄</span>lint-issues.ts </li> <li title="scripts/phase-complete.ts (12671 B)"> <span class="file-icon">📄</span>phase-complete.ts </li> <li title="scripts/query.sh (498 B)"> <span class="file-icon">📄</span>query.sh </li> <li title="scripts/query.ts (3566 B)"> <span class="file-icon">📄</span>query.ts </li> <li title="scripts/run.sh (321 B)"> <span class="file-icon">📄</span>run.sh </li> <li title="scripts/setup.ts (8847 B)"> <span class="file-icon">📄</span>setup.ts </li> <li title="scripts/sync-skill-version.mjs (2062 B)"> <span class="file-icon">📄</span>sync-skill-version.mjs </li> <li title="scripts/sync.ts (5814 B)"> <span class="file-icon">📄</span>sync.ts </li> <li title="scripts/upload-image.ts (4498 B)"> <span class="file-icon">📄</span>upload-image.ts </li> <li title="sdk.md (6920 B)"> <span class="file-icon">📄</span>sdk.md </li> <li title="sync.md (7057 B)"> <span class="file-icon">📄</span>sync.md </li> <li title="troubleshooting.md (4752 B)"> <span class="file-icon">📄</span>troubleshooting.md </li> <li title="tsconfig.json (484 B)"> <span class="file-icon">📄</span>tsconfig.json </li> </ul> </div> <div class="side-card"> <h4>同分类推荐</h4> <ul class="related-list"> <li><a href="/skill/nanobanana-ppt-skills-2/">NanoBanana-PPT-Skills</a></li> <li><a href="/skill/automate-whatsapp/">automate-whatsapp</a></li> <li><a href="/skill/brainstorming/">brainstorming</a></li> <li><a href="/skill/dispatching-parallel-agents/">dispatching-parallel-agents</a></li> <li><a href="/skill/executing-plans/">executing-plans</a></li> <li><a href="/skill/frontend-slides/">frontend-slides</a></li> </ul> </div> </aside> </section> </main> <footer class="site-footer"> <div class="footer-inner"> <div class="footer-left"> <span>© 2026 <a href="/">Skills</a> · 技能库</span> </div> <div class="footer-right"> <a href="/?source=local">本地技能</a> <a href="/?source=official">官方技能</a> </div> </div> </footer> <button class="back-to-top" id="backToTop" aria-label="返回顶部" type="button"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg> </button> <script src="/static/js/main.js?v=1" defer></script> </body> </html>