27 Commits

Author SHA1 Message Date
Peter Schilling aad5f2f32a Expose cycles in issue query, mine, and view
Issue tables gain a compact CYC column (only when a listed team has
cycles enabled): 'now' for the active cycle, +1/-1 from the API's
next/previous flags, +N/-N anchored on team.activeCycle, and #N when no
anchor exists (cooldown or pre-first-cycle). issue view annotates its
Cycle meta part with the same token, and issue JSON output now carries
the cycle flags and team anchor in raw GraphQL shape.

The shared cycle resolver understands now/next/previous and signed
offsets, so --cycle on query/mine/create/update and cycle view all
speak the relative vocabulary. issue update gains --clear-cycle
(explicit cycleId: null, mirroring --unassign). Also fixes issue query
--search silently dropping the --cycle filter.
2026-07-21 13:12:00 -07:00
Peter Schilling b3a41f7d8c Add team states command and list valid states on wrong --state
Discovering a team's valid workflow state names required a raw GraphQL query,
and passing a wrong `--state` to `issue create`/`issue update` failed with a
bare "Workflow state not found" and no hint at the valid options.

Add `linear team states [teamKey]` (table + `--json`) reusing the existing
getWorkflowStates helper, and make the wrong-state failure actionable: both
issue commands now fetch the states once, resolve against them, and on a miss
throw an error that lists the valid states and points at `linear team states`.
Resolution moves to a pure resolveWorkflowState + a shared
workflowStateNotFoundError factory so both call sites stay identical and the
matching logic is unit-testable; the fetched list is reused for the suggestion
(no second round-trip).

The reporter also hypothesized a raw TypeError for unknown teams; verified this
is false — team(id) is non-null in the schema and an unknown team already
yields a clean "Could not find referenced Team." error, so no null guard is
added. The `--state` help text was left unchanged to avoid churning the
(width-sensitive, globally stale) generated skill docs; the enriched error is
the load-bearing discovery path.
2026-07-17 14:06:50 -07:00
Peter Schilling 70976249ad Point config suggestions at the real linear config command
Three user-facing strings advertised a `linear configure` command that does
not exist — the interactive setup command is registered as `config`, so
`linear configure` failed with "Unknown command". The reporter hit this via
`linear team id` with no team configured.

Fix all three suggestions to name the canonical `config` command, and add
`configure` as an alias so the natural command people (and the CLI's own help
text) reach for just works instead of erroring. Keeping `config` canonical in
every string and doc while tolerating `configure` is more robust than a bare
text swap: the alias matches the exact instinct that produced the bug.

Also upgrade the bare `Error` thrown for an integer id with no team to a
`ValidationError` so it renders with the standard ✗ + suggestion treatment.

The root command moves from src/main.ts into src/cli.ts so it can be imported
by tests (to assert the alias resolves) without its complex inferred cliffy
type entering the published public API and tripping no-slow-types; main.ts
stays the entry point and only runs it under import.meta.main.
2026-07-17 11:39:18 -07:00
Ryan Schumacher dd39d9e269 fix(cli): accept both UUID and name for --project/--milestone (#229)
The CLI was inconsistent about whether --project and --milestone flags
accept a UUID, slug, or name depending on which command you called.
Several commands silently rejected one form with a misleading "not
found" error.

- Extend resolveProjectId to accept UUID, slug, or name uniformly.
- Add resolveMilestoneId that accepts a UUID directly, or a name when
--project is supplied so the milestone lookup can be scoped.
- Wire the resolvers into issue create/update/mine/query, milestone
create/update/list, and milestone view (which now also accepts an
optional --project for name-based lookup).
- Remove document create's duplicate local resolver in favor of the
shared one.
- Update --help to say "UUID, slug ID, or name" everywhere.

Closes #221

---------

Co-authored-by: Theo Gregory <theo@gregory.sh>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Schilling <code@schpet.com>
Co-authored-by: Bryan <bryandesigning@gmail.com>
Co-authored-by: Joseph <31323641+josephyooo@users.noreply.github.com>
Co-authored-by: Magnus Buvarp <magnus.buvarp@gmail.com>
Co-authored-by: Rengar Lee <Rengarlee@163.com>
Co-authored-by: Hyunsu Lim <hyunsu.lim01@gmail.com>
Co-authored-by: Luc Leray <luc.leray@gmail.com>
Co-authored-by: c-99-e <268417377+c-99-e@users.noreply.github.com>
Co-authored-by: Al Johri <al.johri@gmail.com>
Co-authored-by: Mihai Chiorean <mihai.v.chiorean@gmail.com>
Co-authored-by: Mihai Chiorean <mihai-chiorean@users.noreply.github.com>
Co-authored-by: Jeffrey Holm <jeff.holm@scale.com>
Co-authored-by: Paymahn Moghadasian <paymahn1@gmail.com>
Co-authored-by: Evan Jacobson <evanjacobson3@gmail.com>
Co-authored-by: schpetbot <bot@schpet.com>
Co-authored-by: Alex Broekhof <alex@quilt.com>
2026-07-11 14:58:31 -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 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 b4aea366f6 feat: add issue search command 2026-04-02 04:58:17 +00:00
Peter Schilling 4113c4d4fc test: make issue list output deterministic 2026-04-01 14:59:12 +00:00
Peter Schilling fc8530ad26 fix: improve git command error handling with CliError
- Use CliError instead of generic Error for all git command failures
- Add proper error checking to getCurrentBranch() and getRepoDir()
- Improve error handling in startVcsWork() git operations
- Add comprehensive tests for git error handling
- Ensure consistent error reporting across git utilities

Fixes #62
2026-02-17 20:49:58 -08:00
Ben Drucker 3582fc5700 feat: add api subcommand for raw GraphQL access (#121)
Adds a `linear api` subcommand for making raw GraphQL requests,
mirroring [`gh api`](https://cli.github.com/manual/gh_api) conventions.

## Changes

- Accepts a GraphQL query as a positional arg, from stdin with `-`, or
via auto-detected piped input
- `--variable key=value` for typed variable coercion (booleans, numbers,
null, `@file` for file reads, `@-` for stdin)
- `--variables-json '{"key": "value"}'` for passing all variables as a
JSON object (merged with `--variable`, which takes precedence)
- `--paginate` walks `pageInfo.endCursor` automatically and outputs
concatenated `nodes` array
- `--silent` suppresses response output while exit code still reflects
errors
- Pretty-prints JSON when stdout is a TTY, raw JSON otherwise for piping
to `jq`
- Exits with code 1 on HTTP errors (status >= 400) and GraphQL-level
errors
- Uses raw `fetch` so users see the exact server response including both
`data` and `errors` fields

## Testing

- Snapshot tests using `MockLinearServer` cover query resolution,
variable handling (type coercion, `@file`, `--variables-json`,
precedence), output modes, pagination (multi-page, single-page,
non-connection), auth errors, and `--silent` behavior for both
successful and HTTP error responses
- Manual testing against live Linear API: cycles, workflow states,
notifications (with `--paginate`), non-existent issue lookup, variable
type mismatch errors, stdin piping

## Related

- Closes #123
2026-02-09 09:36:55 -08:00
Peter Schilling 430ac0402d feat: add user-friendly error handling with LINEAR_DEBUG support
Fixes #116

Error messages are now clean and user-friendly by default. Stack traces
are only shown when LINEAR_DEBUG=1 is set, similar to RUST_BACKTRACE.

Changes:
- Add src/utils/errors.ts with error handling infrastructure:
  - CliError base class with user-facing messages and suggestions
  - NotFoundError for entity lookups
  - ValidationError for invalid input
  - AuthError for authentication issues
  - handleError() for consistent error display
  - extractGraphQLMessage() to parse Linear API errors
- Update all commands to use handleError() for consistent error display
- Error output goes to stderr with ✗ prefix
- GraphQL errors show userPresentableMessage when available

Example before:
  Error: Entity not found: Issue: {"response":{"data":null...

Example after:
  ✗ Issue not found: FAKE-9999
2026-02-01 22:51:12 -08:00
Peter Schilling 59dce3cf19 fix: use centralized shouldShowSpinner() for all spinner checks
Updated bulk.ts to use the centralized shouldShowSpinner() function instead
of directly checking Deno.stdout.isTerminal(). This ensures progress display
during bulk operations also respects the NO_COLOR environment variable.

Fixes #113
2026-02-01 20:38:23 -08:00
Peter Schilling af20b0f310 use --allow-all instead of fine grained permissions
this thing runs exec loosely so i don't think they afford me mcuh, and they frequently cause problems
2026-01-29 16:33:18 +00:00
Peter Schilling 908bae467f fix: error when --workspace flag specifies unknown workspace
Previously, --workspace would silently fall back to other credential
sources when the specified workspace wasn't found. Now it errors with
a helpful message suggesting `linear auth login` or `linear auth list`.

Also errors when both LINEAR_API_KEY env var and --workspace are set,
since these are conflicting ways to specify credentials.

Added error handling guidelines to CLAUDE.md to prevent silent failures.
2026-01-27 13:02:46 -08:00
Sam Gbafa cd45a8a6aa Add initiative, label, and bulk operation support (#95)
This PR adds several major features to linear-cli:

- **Initiative management**: Full CRUD support for initiatives including
list, view, create, archive, unarchive, update, and delete commands
- **Initiative-project linking**: Commands to add and remove projects
from initiatives
- **Label management**: List, create, and delete commands for labels
with team filtering
- **Project creation**: New `project create` command with interactive
mode and initiative linking
- **Team deletion**: New `team delete` command with confirmation
- **Bulk operations**: New utility supporting bulk operations across
commands (issue delete now supports multiple IDs)

## New Commands

### Initiatives
- `linear initiative list` - List all initiatives with filtering options
- `linear initiative view <id>` - View initiative details including
linked projects
- `linear initiative create` - Create new initiative (interactive or via
flags)
- `linear initiative archive <id>` - Archive an initiative
- `linear initiative unarchive <id>` - Unarchive an initiative  
- `linear initiative update <id>` - Update initiative properties
- `linear initiative delete <id>` - Delete an initiative (with
confirmation)
- `linear initiative add-project` - Link a project to an initiative
- `linear initiative remove-project` - Remove project from initiative

### Labels
- `linear label list` - List labels with optional team filter
- `linear label create` - Create a new label
- `linear label delete <id>` - Delete a label

### Projects
- `linear project create` - Create a new project with team, lead, dates,
status, and optional initiative linking

### Teams
- `linear team delete <id>` - Delete a team (with confirmation)
2026-01-21 08:45:54 -08:00
Kirt Lillywhite 1268b667fb feat: support loading config from user home directory
Add support for loading `.linear.toml` from user's home directory as a
fallback when no project-level config exists:
- Unix: `~/.config/linear/linear.toml` or `$XDG_CONFIG_HOME/linear/linear.toml`
- Windows: `%APPDATA%\linear\linear.toml`

Config precedence (highest to lowest):
1. CLI flags
2. Environment variables
3. Project config (`.linear.toml` in cwd or repo root)
4. User home config

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 13:28:32 -08:00
Peter Schilling 77dd899d47 change format of issue describe output (jj-vcs)
this also adds --references flag for non-closing linear magic words (jj-vcs)

Claude-session-id: 7d48da37-dc2f-4c72-9570-f4500f227643
2025-12-01 09:24:02 -08:00
Peter Schilling be2dd51073 Add jj-vcs support for issue detection
Enable finding the current Linear issue from jj commit trailers, matching
the existing git branch-based detection. Users can now work with either git
or jj version control systems.

- Extract Linear-issue trailer parsing into getJjLinearIssue() utility
- Create getCurrentIssueFromVcs() abstraction for VCS-agnostic issue lookup
- Update getIssueIdentifier() to use new VCS-aware detection
- Support both git branch names and jj commit trailers seamlessly

Claude-session-id: cf951f85-34da-4328-be9d-241ff092feea
2025-10-21 11:55:05 -07:00
Peter Schilling 4bb3bef0c7 change formatting rules: no prose wrap, no semi colons 2025-09-02 22:13:34 -07:00
Peter Schilling bd5e507041 pager leaves content visible after quitting
fixes CLI-55
2025-08-21 08:41:27 -07:00
Peter Schilling 76e2ba8ffd Fix issue create with parent, auto inherit project from parent 2025-08-19 15:17:26 -07:00
Peter Schilling 1edf814b48 CLI-47 Support referencing issues with integer only IDs (#52)
https://linear.app/schpet/issue/CLI-47/allow-integer-only-issue-ids-if-theres-a-team-set
2025-08-19 08:50:24 -07:00
Peter Schilling 592000f70a Various improvements (#51)
**Fixed:**
- state column is now dynamically sized with max 20 chars and
auto-truncation

**Changed:**
- linear issue list now sorts by workflow state first
- issue pr create no longer opens browser by default, added --web flag
- removed 'about' prefix from relative timestamps

**Added:**
- automatic paging for issue view command with --no-pager flag and pager
- pager support for issue list command with --no-pager option
2025-08-18 21:52:46 -07:00
Peter Schilling 3af23fd92e CLI-23 Add linear issue update non-interactive command, support same flags as create (#50)
https://linear.app/schpet/issue/CLI-23/add-linear-issue-update-non-interactive-command-support-same-flags-as
2025-08-17 07:18:21 -07:00
Peter Schilling c125a2e805 test issue-create 2025-08-14 19:51:32 -07:00