@fredsvanelli/gsd-with-linear
v0.2.0
Published
Claude Code plugin that mirrors GSD's .planning/ as a Linear board (bidirectional sync).
Maintainers
Readme
gsd-with-linear
Visualize your GSD specs as Linear boards.

gsd-with-linear is a Claude Code plugin that mirrors and sync GSD's .planning/ folder as a Linear board:
- GSD milestones → Linear Milestones
- phases → Issues
- plans → Sub-issues (
wave-Nlabels; theNN-MM-PLAN.mdcontent is converted to Linear markdown in the description — frontmatter becomes a meta header + Must-haves,<objective>/<tasks>/<threat_model>blocks become sections) - quick tasks → Issues (
quicklabel) - pending todos → Backlog Issues (
todolabel) - debug sessions → Issues (
buglabel; terminal status in the frontmatter = Done, otherwise In Progress).
Bidirectional sync:
.planning/ is the source of truth for content; Linear sends back states
(plan Done ⇄ checkbox, todo Done ⇄ pending/resolved) and new issues (they become
todos in pending/).
Conflict: local wins, unless local hasn't changed since the last push.
⚠️ Requirement: the plugin depends on GSD being installed - it mirrors the
.planning/that GSD creates and maintains; without GSD there is nothing to sync. Theinit,status,pullandpushcommands warn and stop when they don't find.planning/in the cwd.Both GSD generations are supported: legacy
get-shit-doneroadmaps (em-dash separators,NN-MM-PLAN.mdchecklists) andgsd-core≥ 1.6 (hyphen separators,(planned)milestones,#### Phase Nunder milestone blocks,NN-MM:plan checklists,## Progresstable as the done signal,debug/resolved/, and plan completion viaSUMMARY.mdon disk). The opt-inphase_id_convention: "milestone-prefixed"(phases named1-01) is not supported.
Installation
Requirements: Node.js ≥ 18, Claude Code, and GSD.
Background auto-sync is supported on macOS (launchd) and Linux (systemd user
timer, with a cron fallback). On other platforms the plugin still syncs on every
.planning/ edit and session start, just without the 5-minute background polling.
Global (all your projects)
npm install -g @fredsvanelli/gsd-with-linear
claude plugin marketplace add "$(npm root -g)/@fredsvanelli/gsd-with-linear"
claude plugin install gsd-with-linear@gsd-with-linearLocal (single project)
npm install -D @fredsvanelli/gsd-with-linear
claude plugin marketplace add ./node_modules/@fredsvanelli/gsd-with-linear
claude plugin install gsd-with-linear@gsd-with-linearTo pin the plugin for the whole team, commit this to the project's
.claude/settings.json instead of running claude plugin install:
{
"extraKnownMarketplaces": {
"gsd-with-linear": {
"source": { "source": "directory", "path": "./node_modules/@fredsvanelli/gsd-with-linear" }
}
},
"enabledPlugins": { "gsd-with-linear@gsd-with-linear": true }
}ℹ️ Claude Code copies the plugin into its cache (
~/.claude/plugins/cache) at install time. After upgrading the npm package, refresh the copy withclaude plugin marketplace update gsd-with-linear.
Then, inside a GSD project, run /gsd-with-linear:init to connect it to Linear.
This also installs the background polling agent for your platform, creates the
agile issue templates in the Linear team (see Agile ticket templates)
and offers an optional patch to GSD's todo capture.
Credential
This plugin needs a LINEAR_API_KEY (Linear > (your project) > Settings > Security & access > Personal API keys) exported in the environment, or in a .env
(or .env.local, read as fallback) at the GSD project root.
Precedence: process env > .env > .env.local.
Commands
| Command | Description |
| --- | --- |
| /gsd-with-linear:help | Lists the available commands |
| /gsd-with-linear:init | Connects the plugin with your Linear project
| /gsd-with-linear:status | Show connection status, in sync or not, push/pull pending items, last sync attempt and automation state |
| /gsd-with-linear:pull | Manually pull updates from Linear |
| /gsd-with-linear:push | Manually send updates to Linear |
Board columns (agile flow)
Flow: Backlog → Todo → In Progress → UAT → Ready to ship → Code Review → Done.
Standard columns, mapped from the GSD status of each item:
| Backlog | Todo | In Progress | Done |
| --- | --- | --- | --- |
| Pending todos (todo label) and issues adopted from Linear. | Items whose GSD status is todo (not started yet). | Items whose GSD status is in_progress. | Items whose GSD status is done (checked plan, resolved todo, terminal debug status). |
The "UAT", "Ready to ship" and "Code Review" columns are created by the push on demand and fed automatically (phases of non-shipped milestones with completed plans; UAT prevails because it comes earlier in the flow):
| UAT | Ready to ship | Code Review |
| --- | --- | --- |
| GSD artifacts: <N>-VERIFICATION.md with status: human_needed and no approved HUMAN-UAT.md (or HUMAN-UAT partial). | HUMAN-UAT approved (passed/complete/resolved) but the phase has not entered any PR yet - neither an open PR via gh nor a PR recorded in STATE.md (a record without an open PR = PR already merged → Done). | Open PR on GitHub associated with the phase (human review pending), covering the primary repo and the sibling repos of cross-repo phases (**Repo:** fields). The PR link goes into the issue description. Requires a git repo + logged-in gh; without that the column simply stays empty. |
PR ↔ phase association (Code Review) is detected via gh pr list through a cascade of
signals: (1st) .planning/phases/<N-slug>/ paths in the PR diff - GSD commits the phase
artifacts on the branch, so the association is derived from data; (2nd) "Phase N shipped -
PR #M" record in STATE.md; (3rd) fallback: phase-N in the branch or "Phase N" in the
title. GSD's internal REVIEW.md does not count - it is a step of the automated
pipeline, not a board column.
These stages are report-only in the pull (resolving = approving the UAT / opening or merging the PR, not dragging the card).
Agile ticket templates
Tickets on the board follow the usual agile shapes, fed from data GSD already captures:
- Bugs (debug sessions): the issue description carries Expected / Actual /
Reproduction / Errors extracted from the
## Symptomssection that/gsd-debugwrites in the session file. Unfilled placeholders are omitted — a fresh session shows just the trigger. - Tasks (todos): the
## Problemsection becomes Scope,## Solutionbecomes Proposed solution (omitted while "TBD"), and an## Acceptance Criteriasection — when present — is rendered as Acceptance criteria. Free-form todos fall back to the raw body, as before.
init also creates two issue templates in the Linear team: Bug (GSD)
(Expected/Actual/Reproduction/Errors) and Task (GSD) (Scope/Acceptance Criteria).
Issues created on the board from Task (GSD) are adopted as todos carrying the same
sections, so the structure round-trips. Existing templates with those names are never
overwritten.
Optionally (asked during init, off by default), the plugin can patch GSD's global
todo capture (workflows/add-todo.md) so /gsd-add-todo also captures an
## Acceptance Criteria section at the source. This modifies the GSD installation for
all projects; GSD's updater backs the change up to gsd-local-patches/ and
/gsd-update --reapply merges it back after a GSD update. Re-run
node <scripts>/patch-gsd-todo-template.mjs anytime (idempotent), or reinstall GSD to
revert.
Task dependencies (chaining)
Each phase's Depends on: Phase N field becomes a native blocks relation in Linear (the depended-on phase "blocks" the dependent one → the card shows "Blocked by"). Diffed against the real relations graph (outside the issue hash); relations created by hand on the board are preserved (we never remove relations).
Automation (when each sync happens)
- Local edit (
ROADMAP.md,STATE.md,quick/**,todos/**,phases/**,debug/**) →PostToolUsehook runs pull → push (bursts coalesce). Phase artifacts (HUMAN-UAT, VERIFICATION, REVIEW-FIX) move the card right away, without waiting for polling. - Session start in a connected project →
SessionStarthook runs pull → push (changes made on the board while you were away land before you start). - Background (every 5 min, even without Claude Code open) → a platform agent
runs pull → push in each project listed in
projects.txt(fed byinit): launchd on macOS (com.gsd-with-linear.pull), a systemd user timer on Linux (gsd-with-linear-pull.timer; falls back to a crontab entry without systemd). Installed/refreshed byinitviasetup-automation.sh; disable withbash <scripts>/setup-automation.sh --remove.
Linear's MCP has no push/webhook - everything above is polling/local events.
Guarantees
- Idempotent:
`gsd-sync:<kind>=<key>:h=<hash>`marker at the bottom of every entity; re-run with no changes = 0 operations. - Never deletes: phases removed from the ROADMAP become orphans and are only reported.
- The hook never blocks or fails Claude's turn (always exit 0, sync in the background).
