Skip to content

feat(linear): add --linear to start a worktree from a Linear issue - #111

Merged
baldurpan merged 5 commits into
mainfrom
feature/107-linear-support
Oct 23, 2026
Merged

baldurpan merged 5 commits into
mainfrom
feature/107-linear-support

Conversation

@baldurpan

Copy link
Copy Markdown
Contributor

Closes #107

worktree branch --linear <ID> (-l) starts a worktree from a Linear issue, the way --github and --jira already do.

  • Client (src/integrations/linear.ts): one GraphQL query for identifier, title, url and label names; the personal API key goes in the bare Authorization header; every call is bounded at 15 s. Auth and rate-limit errors are read from errors[].extensions.code, where Linear puts them.
  • Branch name: <prefix><IDENTIFIER>-<slug>. The prefix comes from labels, since Linear has no issue type: bug → bugfix, chore/maintenance → chore, otherwise feature.
  • Config: worktree config asks for linear.apiKey; an empty answer keeps a stored key. lin_api_ keys are redacted from all output.
  • --json: issue gains {provider:"linear", key, url}. --linear is exclusive with --github/--jira; --assign --linear warns that assignment is not supported yet.
  • Docs: a new Linear guide, plus the branch, config and configuration pages, the README and the shipped skill.

Plan and phase ledger: #107. Four phases, one commit each, plus the release note (minor).

Still needs a person: #109 (run it once against a real Linear workspace) and #110 (confirm the label rules). Related, not caused here: #108 (worktree config prints stored tokens in clear).

Merging does not release this: it waits for the release pull request.

Phase 1 of #107. src/integrations/linear.ts fetches one issue by
identifier with a single GraphQL query (identifier, title, url, labels),
authenticates with the bare personal API key, bounds every request at
15 s, and derives the branch prefix from the issue's labels.
`linear.apiKey` joins CONFIG_NAMES and `lin_api_` keys are redacted from
every output route.

Auth and rate-limit errors are read from errors[].extensions.code, which
is where Linear puts them (a live 401 probe and the rate-limiting docs),
not extensions.type as the plan first said.

Gate 1: pnpm check, typecheck, build, test (1056), docs:test (69) and
the test-marker grep exit 0.
Gate 2: reviewer subagent, FAIL then PASS WITH NOTES after one loopback.
Refs #107
Phase 2 of #107. `worktree config` gains a Linear group: one confirm,
then the API key prompt with instructions for finding a personal key.
The stored key is never offered as a default or prefill, and an empty
answer leaves a stored key in place, as `github.token` does. `--names
linear.apiKey` skips the confirm, `--missing` lists the key while it is
unset, and a non-interactive run with no key names
`worktree config linear.apiKey <key>`.

Coder commit 5a82376 in the phase tree; the empty-answer rule replaced
its validate in-host after Gate 2's first pass.

Gate 1: pnpm check, typecheck, build, test (1066), docs:test (69) and
the test-marker grep exit 0.
Gate 2: reviewer subagent, PASS WITH NOTES, then PASS WITH NOTES on the
note fix.
Refs #107
Phase 3 of #107. `worktree branch --linear ENG-123` (`-l`) fetches the
issue once and derives `<prefix>ENG-123-<slug>` from it, the prefix from
the issue's labels. `--json` carries
`issue: {provider:"linear", key, url}`, the url as Linear returns it.
`--linear` is exclusive with `--github` and `--jira`. `--assign` with
it warns that assignment is not supported yet and carries on, and a
missing `linear.apiKey` exits 2 with `missing_value`.

Coder commit 1efb8a6 in the phase tree. Three assertions were added
in-host after Gate 2: one fetch only, `-l`, and the help's linear
`issue` shape.

Gate 1: pnpm check, typecheck, build, test (1077), docs:test (69) and
the test-marker grep exit 0.
Gate 2: reviewer subagent, PASS WITH NOTES.
Refs #107
Phase 4 of #107. A new Linear Issue Integration guide covers usage, how
branch names are built, the label -> prefix rule, getting a key and
setup. The branch, config and configuration pages, the README, the
shipped skill and the docs chat route map gain Linear beside Jira, and
the route list is regenerated by `worker:build-context`. The guides index
now lists the Jira guide too, which it never did.

No changeset: context/release.md has the note written once per feature,
by /feature-close.

Gate 1: pnpm check, typecheck, build, test (1077), docs:test (69) and
the test-marker grep exit 0.
Gate 2: reviewer subagent, PASS WITH NOTES; four doc inaccuracies it
found were fixed before this commit.
Refs #107
Retires #107. The one release note for the feature, as context/release.md
asks (once per feature), and the branch-local context/notes.md removed.
Owner checks left for a person: #109 (live run) and #110 (label rules).
@baldurpan baldurpan mentioned this pull request Oct 22, 2026

@craig-o-curtis craig-o-curtis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks. Approving.

What I checked:

  • Tests: pnpm verify passes locally (Biome, typecheck, 1077 tests). The "Unexpected subprocess calls" warnings also show up on main, so they are not from this PR.
  • Linear API: checked against Linear's developer docs and the SDK's schema.graphql.
    • The personal key goes in a bare Authorization header, with no Bearer.
    • issue(id: String!) accepts a shorthand like ENG-123, so the $id: String! variable type is right.
    • A rate-limited GraphQL request gets HTTP 400 with RATELIMITED in errors[].extensions.code. Keeping 429 as well does no harm.
  • Flags: -l doesn't clash with any other flag. The three issue sources exclude each other, and --assign with --linear warns instead of failing.
  • Secrets: lin_api_ keys are redacted. A missing key stops the run before a worktree is created, and an empty answer in worktree config keeps the stored key.

One small thing, not blocking: validateLinearCredentials is only used by its tests. That matches validateJiraCredentials, so it's fine to keep for consistency, or you can drop both in a later cleanup.

#109 (a run against a real workspace) and #110 (the label rules) are still open, as the description says.

@baldurpan

Copy link
Copy Markdown
Contributor Author

Thanks for the review. On validateLinearCredentials: I'm keeping it alongside validateJiraCredentials for now, as you suggest. If neither ever gets a caller, both can go together in a later cleanup.

@baldurpan
baldurpan merged commit 5eaf88f into main Oct 23, 2026
2 checks passed
@baldurpan
baldurpan deleted the feature/107-linear-support branch October 23, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

linear-support

2 participants