506 Commits

Author SHA1 Message Date
Joseph 9756aafe19 Guard document updates when inline comments exist (#235)
## Summary

This makes Linear document comments visible to agents/automation and
prevents `linear document update` from silently orphaning inline comment
anchors.

- include paginated document comments in `linear document view <id>
--json`
- guard Markdown content updates when active inline document comments
are present
- allow top-level document comments to pass through, since they do not
carry losable inline anchors
- add `--force` to opt back into the existing replacement behavior when
the caller intentionally accepts the risk


## Background / related work

Closes #230, which reports that document comments are currently
unreachable from the CLI and omitted from `document view --json`.

Related:

- #219 added richer `document view` behavior for downloaded inline
images; this PR keeps normal/raw rendered views lean and only expands
`--json` with comment metadata.
- #121 added the raw `linear api` GraphQL escape hatch. That is useful
for inspection, but it does not make `documentUpdate(content)` safe for
inline document comments.

## API limitation

This is intentionally a safety guard plus read fix, not a
comment-preserving writer.

From the schema used by this CLI:

- `Document.comments(...)` exposes document comments and
`Comment.quotedText`, which is enough to detect inline comments.
- `DocumentUpdateInput` only accepts Markdown `content` for document
body writes; it does not accept `contentState`/YJS/ProseMirror data or
comment anchor metadata.
- `CommentCreateInput`/`CommentUpdateInput` expose `quotedText`, but
live testing showed raw GraphQL `commentUpdate(quotedText: ...)` returns
success without recreating an inline anchor. Writing
`<linear-comment>`-style tags through `documentUpdate(content)` stores
literal text, not an anchor.

So the CLI cannot preserve or restore inline anchors through the public
GraphQL write path it uses. The best safe behavior here is to make
comments visible and convert silent data loss into an explicit stop.
`--force` remains the old replacement behavior behind an intentional
flag.

## Behavior

`linear document view <id> --json` now returns a flattened, paginated
`comments.nodes` list with fields useful to agents:

- `id`, `body`, `quotedText`, `documentContentId`
- timestamps / archive / resolution metadata
- `url`, `user`, and `parent.id`
- final `pageInfo`

`linear document update <id> --content...` now scans active document
comments page-by-page. It blocks only when it finds a comment with
`quotedText`, i.e. an inline comment anchor that can be detached by
replacing Markdown content. Top-level document comments with
`quotedText: null` do not block.

## Testing

- `deno task validate`
- `deno test --allow-all --quiet`

Full suite result locally: `338 passed`, `0 failed`, `5 ignored`.
2026-07-11 13:58:36 -07:00
Peter Schilling 27e10af35f Follow-up to #228: harden --all pagination and fix TZ-flaky snapshots
#228 fixes milestone view silently capping its issue list, adding --all to
paginate. Two corrections:

1. Fail loudly on inconsistent --all pagination. The loop guarded on
   `while (pageInfo.hasNextPage && pageInfo.endCursor)` and `if (next == null)
   break`, so if Linear advertised another page but returned no cursor (or the
   milestone vanished mid-pagination), --all would stop early and return a
   *partial* list with no error — reintroducing the exact silent-truncation bug
   this command exists to fix. Now throws CliError / NotFoundError instead, with
   a regression test.

2. Fix timezone-flaky snapshots. The new "Truncated" and "--all paginates" tests
   used midnight-UTC timestamps (2020-01-01T00:00:00Z) whose snapshots were
   generated in a US timezone, so `formatRelativeTime`'s toLocaleDateString
   fallback rendered 12/31/2019 there but 1/1/2020 under CI's UTC — failing CI
   (fork CI never ran, so it slipped through). Moved the fixtures to noon UTC so
   they render the same date in every timezone; verified passing under TZ=UTC and
   TZ=America/Los_Angeles.
2026-07-11 13:46:11 -07:00
Ryan Schumacher 701a395bc6 fix(milestone): surface truncation in view, add --all flag (#222)
milestone view silently capped its issues list at 10 items. A user with
12 issues would see only 10 and could miss issues entirely when
iterating over the visible output in a script. The cap was also
undocumented in --help.

- Request first: 50 issues with pageInfo on the connection.
- Add --all to paginate through every page when needed.
- Show a prominent truncation footer pointing at --all and at
  `linear issue query --milestone X --json` for full programmatic
  access.
- Document the default cap in --help.

Closes #222
2026-07-11 13:43:25 -07:00
Peter Schilling 43a629029f Follow-up to #216: add error-path tests for project create fields
PR #216 adds --content/--content-file plus priority/label/member/icon/color
to `linear project create`, with happy-path snapshot tests and a
mutual-exclusion unit test. This adds the missing failure-path coverage that
both blind-planning passes called for:

- invalid --priority is rejected with a ValidationError before any network call
- an unknown --label surfaces a NotFoundError
- an unknown --member surfaces a NotFoundError

These use a stubbed Deno.exit and capture stderr (mirroring the validation
tests in issue-query.test.ts), and run against a mock Linear server so they
never touch the real API. No product code changes — the contributor's feature
is correct as written; this only locks in the error behavior.
2026-07-11 13:16:10 -07:00
Bryan ad630c5d69 feat(project): support overview content on create (#216)
## Why

Linear projects have two text fields: a short description and a longer
project overview. The CLI only supported the short description, so
projects created from the terminal still needed a manual edit in Linear
to add goals, plans, specs, or launch notes.

This PR lets users create a project with its overview already filled in,
either from inline markdown or a markdown file. That makes `linear
project create` more useful for scripted project setup, templates, and
project specs kept in a repo.

It also exposes a few existing Linear create fields so users can set
priority, labels, members, icon, and color when creating the project
instead of doing a follow-up edit.

## What changed

- `linear project create` now accepts `--content` for inline project
overview markdown.
- `--content-file` reads the project overview from a markdown file.
- `--content` and `--content-file` are mutually exclusive.
- Project create can now set priority, labels, members, icon, and color.
- The command passes these values through `ProjectCreateInput` using
Linear field names.

## Checks

- `deno task codegen`
- `deno task check`
- `deno lint`
- `deno task test`
2026-07-11 13:13:23 -07:00
Peter Schilling f5d87b8884 Follow-up to #234: validate --public per-batch before any upload
PR #234 makes attachment uploads private by default and adds a --public
opt-in that errors (rather than silently downgrades) for non-image types.
Two gaps remained in `issue comment add`, both flagged independently while
blind-planning the fix:

- The public/type check only ran inside uploadFile, per file, at upload
  time. With `--public` and multiple attachments, an earlier valid image
  was uploaded *publicly* before a later non-image threw — a partial, and
  unwanted-public, upload. Pre-flight resolveMakePublic() for every
  attachment in the existing existence-validation loop so a mixed batch
  fails before anything is uploaded.
- `--public` with no `--attach` was a silent no-op. Per the repo's
  "explicit invalid input should error" convention, reject it.

Adds command-level tests for both validation paths (they short-circuit
before any network call, so no upload mock is needed).
2026-07-11 12:47:45 -07:00
Theo Gregory dc2dca42f1 fix(upload): default attachments to private, add --public opt-in
Image attachments uploaded via `issue attach` and `issue comment add
--attach` were sent with makePublic auto-detected to true for raster
images, producing a public.linear.app URL readable by anyone,
unauthenticated, with no way to opt out. This silently published
screenshots of internal data from private workspaces.

Default all uploads to private (uploads.linear.app), matching the Linear
web app. Add a --public flag to both commands to opt into a public URL,
which is only valid for raster images; requesting it for other types is
now an error rather than a silent downgrade. Print a warning whenever an
upload lands on a public URL.

Also document the attachment commands and their privacy behaviour in the
README (previously undocumented) and regenerate the skill reference.

Fixes #233

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:45:00 -07:00
Peter Schilling fc85b919cd feat(issue): show blocked indicator in mine and query output
Add a 'B' column (with ⊘) to 'linear issue mine' and 'linear issue query'
that flags issues with at least one active blocker — a blocked-by relation
whose blocker is not in a completed or canceled workflow state. The
inverseRelations connection is also surfaced in --json output.
2026-05-19 15:55:54 -07:00
Peter Schilling eb8a659f04 feat(document): download attached images in document view
Document view's rendered/raw output now downloads inline images and
Linear-upload links to the same /tmp cache used by issue view, so terminal
renderers and downstream tools see local file paths instead of remote URLs
that require auth.

Image helpers (extractImageInfo, extractLinearLinkInfo, replaceImageUrls,
getUrlHash, getLinearUploadHost, downloadMarkdownImages) move from
src/commands/issue/issue-view.ts to src/utils/markdown-images.ts so both
commands share one implementation. downloadIssueImages becomes
downloadMarkdownImages, which takes an array of markdown sources rather
than an issue-specific (description, comments) tuple.

Adds --no-download to document view (mirroring issue view) and reorders
the command so the download step runs after the --json early return but
before --raw, matching issue view's behavior where piped output also gets
local paths.

Also adds remark-gfm to the parse/stringify pipeline used by
replaceImageUrls so GFM constructs (task lists, tables, strikethrough)
survive the URL rewrite. Without it, remark-stringify would re-escape
`- [ ] todo` as `* \[ ] todo` whenever an image is rewritten, mangling
documents and issue descriptions that lean on GFM syntax.
2026-05-19 12:54:29 -07:00
schpetbot f7922d4121 ci: drop skill docs staleness check, keep generation smoke test (#218)
The skill docs check was failing because `Deno.consoleSize()` returns
different values depending on the runner's pseudo-terminal dimensions
(CI is wide, local terminals vary). Since the docs are for LLM
consumption and not correctness, dropping the `git diff` check and just
verifying generation doesn't crash.

Co-authored-by: Alex Broekhof <alex@quilt.com>
Co-authored-by: Peter Schilling <code@schpet.com>
2026-05-15 03:54:40 +00:00
Peter Schilling 027f25e30c chore: pin deno to 2.7.9 via mise.toml and ci workflow 2026-05-13 20:51:13 -07:00
Peter Schilling b3b9960436 fix: order issue view comments chronologically
Linear's comments connection returns newest-first and the GraphQL schema
doesn't expose a direction argument, so sort root comments ascending
client-side to match the order shown in Linear's UI.
2026-04-21 15:53:19 -07:00
Peter Schilling 90767cbd07 chore: add plugin version bump step to release skill 2026-04-03 13:03:47 -07:00
Peter Schilling e5fa8d28da chore: bump plugin version to 2.0.0 2026-04-03 12:56:16 -07:00
Peter Schilling a3e2960b7f chore: Release linear-cli version 2.0.0 v2.0.0 2026-04-03 11:41:56 -07:00
Peter Schilling 26c418f4b8 feat: split issue list into mine and query commands
Introduce 'issue mine' for personal work queue and 'issue query' for
structured retrieval with optional full-text search via --search.
Keep 'issue list' as a separate backwards-compatible command.
Remove standalone 'issue search' registration in favor of query --search.

query supports multi-team filtering, --all-teams, --json output,
--search-comments, and verifies GraphQL variables in tests.
2026-04-03 18:22:51 +00:00
Peter Schilling aba660998e refactor: preserve GraphQL payload shape in project update list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 8d11f4b6fa refactor: preserve GraphQL connection shape in project list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling ab1f64fb7a refactor: preserve GraphQL mutation shape in project create JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 4691061aad refactor: preserve GraphQL connection shape in label list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling d73884d9aa test: cover raw GraphQL JSON output in agent session view 2026-04-02 15:15:00 -07:00
Peter Schilling 8f1e406fee refactor: preserve GraphQL connection shape in agent session list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 2d2208ffd5 refactor: preserve GraphQL connection shape in issue comment list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 03a12b06bf refactor: preserve GraphQL payload shape in initiative update list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling ef925984dc refactor: preserve GraphQL payload shape in initiative view JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 11487b6097 refactor: preserve GraphQL connection shape in initiative list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling f460007a5f refactor: preserve GraphQL connection shape in document list JSON 2026-04-02 15:15:00 -07:00
Peter Schilling 7c7ad2ac1c docs: codify GraphQL-shaped JSON output policy 2026-04-02 15:15:00 -07:00
Peter Schilling f86356d435 fix: show issue documents in issue view
Fixes #183
2026-04-02 11:49:05 -07:00
Peter Schilling c4feb0866b fix: accept alphanumeric Linear issue team keys 2026-04-02 09:30:13 -07:00
Peter Schilling fb329435f3 fix: remove -w short flag from --workspace to resolve collision with --web
Also skip keyring integration test when D-Bus secrets service is unavailable
2026-04-02 09:07:13 -07:00
Peter Schilling c53feef022 docs: include full command tree in linear skill 2026-04-02 08:46:00 -07:00
Peter Schilling b4aea366f6 feat: add issue search command 2026-04-02 04:58:17 +00:00
schpetbot e23e9ff856 feat: show resolved thread metadata in issue view (breaking change) (#199)
## Summary
- include resolved-thread metadata and derived `threadId` in `linear
issue view --json`
- hide resolved threads by default in human-readable `issue view`, with
`--show-resolved-threads` to reveal them
- show root thread ids in rendered output and use OSC-8 hyperlinks for
them when terminal hyperlinks are enabled

## Testing
- deno check src/main.ts
- deno lint
- deno task test
- manual QA against CLI-team issues for default view,
`--show-resolved-threads`, JSON output, no-comments JSON, open-only
threads, OSC-8 behavior, and reply creation from printed thread ids

Co-authored-by: Peter Schilling <code@schpet.com>
2026-04-01 18:44:30 +00:00
Peter Schilling 1e51d2adbd test: disable colors in issue list assertion 2026-04-01 14:59:12 +00:00
Peter Schilling 713a6c2acd style: format issue list test for CI 2026-04-01 14:59:12 +00:00
Peter Schilling 4113c4d4fc test: make issue list output deterministic 2026-04-01 14:59:12 +00:00
Evan Jacobson 6fdd1e0da2 upgrade dependencies (#194) 2026-03-31 20:36:06 -07:00
Paymahn Moghadasian 4ac13293c0 feat: add agent-session list and view commands (#192) 2026-03-31 05:59:48 +00:00
Jeffrey Holm 08fcf6ce96 feat: add --created-after and --updated-after filters to issue list (#191) 2026-03-31 05:47:31 +00:00
Jeffrey Holm f9519f7807 feat: show assignee and priority in issue view (#190)
## Summary
- Add **assignee** and **priority** fields to the `issue view` command's
metadata line
- Priority uses the existing `getPriorityDisplay` function (same visual
format as `issue list`)
- Assignee shows `@displayName` or `Unassigned` when null
- Both GraphQL queries (`GetIssueDetailsWithComments` and
`GetIssueDetails`) updated with `assignee { name displayName }` and
`priority`

## Motivation
These are critical fields for triaging issues. Previously, `issue view`
showed project, milestone, and cycle but not who owns the issue or how
urgent it is.

## Example output
```
# ENG-123: Fix authentication bug

**Priority:** ▄▆█ | **Assignee:** @Jane Smith | **Project:** Platform Infrastructure Q1
```

## Test plan
- [x] Updated all 10 issue-view snapshot tests with mock data covering:
unassigned/assigned, all priority levels (0-4)
- [x] `deno check src/main.ts` passes
- [x] `deno lint` passes
- [x] All issue-view tests pass (`deno test --allow-all --filter "Issue
View"`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 05:28:52 +00:00
Mihai Chiorean e7851e3cb5 feat: add --label filter to issue list (#180)
## Summary

Adds `-l, --label <label>` flag to `issue list` for filtering issues by
label name. The flag is repeatable — when multiple labels are specified,
only issues matching **all** labels are returned.

- Uses `eqIgnoreCase` for case-insensitive label name matching
- Single label: `filter.labels = { some: { name: { eqIgnoreCase: "Bug" }
} }`
- Multiple labels: `filter.labels = { and: [{ some: { name: {
eqIgnoreCase: "Bug" } } }, ...] }`

### Usage

```bash
# Single label
linear issue list --label Bug

# Multiple labels (AND logic)
linear issue list --label Bug --label "High Priority"

# Combined with other filters
linear issue list --label Bug --state started --cycle active
```

## Test plan

- [x] Added snapshot test for `issue list --label Bug` with mock server
- [x] Updated help text snapshot with new `--label` flag
- [x] `deno fmt` clean
- [x] `deno lint` clean
- [x] All tests pass, existing tests unaffected

---------

Co-authored-by: Peter Schilling <code@schpet.com>
Co-authored-by: Mihai Chiorean <mihai-chiorean@users.noreply.github.com>
2026-03-31 05:04:45 +00:00
Mihai Chiorean 16ae8ae9ce feat: add --json flag to issue list, issue create, and cycle list (#179)
## Summary

Adds `--json` / `-j` flag to three commands that were missing structured
output support:

- **`issue list --json`** — outputs issues as a JSON array with `id`,
`identifier`, `title`, `priority`, `estimate`, `state`, `labels`,
`assignee`, and `updatedAt`
- **`issue create --json`** — outputs the created issue's `id`,
`identifier`, `url`, and `team` as JSON (non-interactive path only)
- **`cycle list --json`** — outputs cycles as a JSON array with `id`,
`number`, `name`, `startsAt`, `endsAt`, `completedAt`, `isActive`,
`isFuture`, `isPast`, and `status`

When `--json` is set, the spinner is suppressed and empty results output
`[]` instead of human-readable messages.

This is particularly useful for AI agents and automation that need to
parse CLI output programmatically. The `linear api` raw GraphQL command
works as a workaround today, but first-class `--json` support on
list/create commands is much more ergonomic.

Related: #127 (requests `--json` on project commands — this PR covers
issue and cycle commands using the same pattern)

## Test plan

- [x] Added snapshot test for `issue list --json` with mock server
- [x] Added snapshot test for `issue create --json` with mock server
- [x] Added snapshot test for `cycle list --json` with mock server
- [x] Updated help text snapshots for all three commands
- [x] `deno fmt` clean
- [x] `deno lint` clean
- [x] All new tests pass, existing tests unaffected
2026-03-31 04:35:22 +00:00
Al Johri 3ec5c08978 chore: regenerate skill docs for --project-label flag 2026-03-31 04:27:57 +00:00
Al Johri 16ebdfc69d feat: add --project-label flag to issue list command
Filter issues by project label name, showing issues from all projects
that have a given label. Uses Linear's native GraphQL project label
filtering rather than a two-step query.
2026-03-31 04:27:57 +00:00
c-99-e cb749d3dbd skill: improve SKILL.md with bunx fallback, required flags, and --no-pager caveat (#182)
A few practical improvements based on real usage friction:

- **Prerequisites**: Added `bunx linear` as a zero-install fallback for
when `linear` isn't on PATH
- **Discovering Options**: Added a note about commands with non-obvious
required flags — specifically that `issue list` requires `--sort` (valid
values: `manual`, `priority`) and `--team`, and that `--no-pager` is
only supported on `issue list`
2026-03-25 08:47:31 -07:00
Luc Leray a8ee13e999 regenerate skill docs for issue link command 2026-03-25 08:46:56 -07:00
Luc Leray 1b510720c5 style: fix deno fmt formatting in test 2026-03-25 08:46:56 -07:00
Luc Leray e38fc08ef2 feat: add issue link command to attach URLs to issues 2026-03-25 08:46:56 -07:00
Hyunsu Lim a7c4f75b5e feat: display issue state in issue view command
Show the issue state (e.g., Todo, In Progress, Done) in the metadata
line of `linear issue view`, positioned before Project/Milestone/Cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:30:06 -07:00