linear-sdk-cli 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ Release Please generates this file from Conventional Commits. Feature and fix pu
4
4
  not edit it directly; their squash-commit titles are the release-note source of truth. This project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.5.1](https://github.com/zhendalf/linear-sdk-cli/compare/v0.5.0...v0.5.1) (2026-09-06)
8
+
9
+ ### Bug Fixes
10
+
11
+ - associate login with projects and make workspace defaults explicit ([#62](https://github.com/zhendalf/linear-sdk-cli/issues/62)) ([faf4028](https://github.com/zhendalf/linear-sdk-cli/commit/faf4028246a947dc13f674d75df2b1ba3c0ac0f3))
12
+ - **comment:** accept create as an add alias ([#57](https://github.com/zhendalf/linear-sdk-cli/issues/57)) ([c8146b9](https://github.com/zhendalf/linear-sdk-cli/commit/c8146b98f60b68b5cc8bd2063fdd49cc9cc91216))
13
+ - reduce label resolution requests and latency ([#64](https://github.com/zhendalf/linear-sdk-cli/issues/64)) ([d1300a5](https://github.com/zhendalf/linear-sdk-cli/commit/d1300a5379e99a466a6111e24fbcfa7b85f21c1f))
14
+
15
+ ## [0.5.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.4.0...v0.5.0) (2026-09-03)
16
+
17
+ ### Features
18
+
19
+ - **config:** support per-workspace default teams ([#50](https://github.com/zhendalf/linear-sdk-cli/issues/50)) ([06be757](https://github.com/zhendalf/linear-sdk-cli/commit/06be757ddb81652dea4da32434e63de773cb6760)), closes [#44](https://github.com/zhendalf/linear-sdk-cli/issues/44)
20
+ - **issue:** add native agent delegation ([#52](https://github.com/zhendalf/linear-sdk-cli/issues/52)) ([15055ff](https://github.com/zhendalf/linear-sdk-cli/commit/15055ff73d46f6ddf31679b7bb5f37fc6e3fddda)), closes [#45](https://github.com/zhendalf/linear-sdk-cli/issues/45)
21
+ - **issue:** set labels by group ([#54](https://github.com/zhendalf/linear-sdk-cli/issues/54)) ([4c471cd](https://github.com/zhendalf/linear-sdk-cli/commit/4c471cd23c9409ba395cf43514f9e251a42ef4d6)), closes [#46](https://github.com/zhendalf/linear-sdk-cli/issues/46)
22
+ - standardize archived-resource listing ([#53](https://github.com/zhendalf/linear-sdk-cli/issues/53)) ([735d1c8](https://github.com/zhendalf/linear-sdk-cli/commit/735d1c87894736de257b8888edb8500934e502f3)), closes [#49](https://github.com/zhendalf/linear-sdk-cli/issues/49)
23
+
7
24
  ## [0.4.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.3.3...v0.4.0) (2026-08-28)
8
25
 
9
26
  ### Features
package/COVERAGE.md CHANGED
@@ -1,16 +1,20 @@
1
1
  # SDK Coverage Audit
2
2
 
3
- Generated by `bun run audit:coverage`. Total LinearClient members: **472**.
3
+ Generated by `bun run audit:coverage`. Total LinearClient members: **473**.
4
4
 
5
5
  | Status | Count | Meaning |
6
6
  | -------- | ----- | ----------------------------------------------- |
7
7
  | curated | 107 | Has a first-class CLI command |
8
8
  | raw-only | 142 | Reachable via `linear api` (no bespoke command) |
9
- | excluded | 223 | Out of scope (admin/integration/SDK plumbing) |
9
+ | excluded | 224 | Out of scope (admin/integration/SDK plumbing) |
10
10
 
11
11
  Every member is classified — CI fails otherwise. `raw-only` members are still
12
12
  100% reachable through the raw GraphQL escape hatch; they simply lack a tailored command.
13
13
 
14
+ ## Curated field capabilities
15
+
16
+ - Issue delegation (Developer Preview) — `Issue.delegate`, `IssueCreateInput.delegateId`, `IssueUpdateInput.delegateId`, and eligible agent-user fields are covered by `issue delegate` and the delegation options on `issue create`/`issue update`.
17
+
14
18
  ## Curated (107)
15
19
 
16
20
  - `agentSession` — issue
@@ -121,7 +125,7 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
121
125
  - `workflowState` — state
122
126
  - `workflowStates` — state
123
127
 
124
- ## Excluded (223)
128
+ ## Excluded (224)
125
129
 
126
130
  - `agentActivities` — specialized/raw api only
127
131
  - `agentActivity` — specialized/raw api only
@@ -306,6 +310,7 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
306
310
  - `ssoUrlFromEmail` — auth/session flows (out of scope)
307
311
  - `suspendUser` — admin/enterprise (raw api only)
308
312
  - `template` — specialized/raw api only
313
+ - `templateSearch` — specialized/raw api only
309
314
  - `templates` — specialized/raw api only
310
315
  - `templatesForIntegration` — integration/import (raw api only)
311
316
  - `timeSchedule` — specialized/raw api only
package/MIGRATING.md CHANGED
@@ -124,6 +124,10 @@ Everything with no row is spelled identically.
124
124
 
125
125
  Passing both spellings at once (`--due` _and_ `--due-date`) is a usage error, not a coin flip.
126
126
 
127
+ This CLI previously taught `initiative list --archived`. Its canonical spelling is now
128
+ `--include-archived`, matching issue, project, and notification lists. The old spelling remains a
129
+ compatibility alias throughout `0.x`; passing both spellings together is a usage error.
130
+
127
131
  **Short flags are the one place we did not follow.** schpet 2.5's own tree assigns `-a` four
128
132
  meanings (`--app`, `--all`, `--assignee`, `--attach`), `-f` four, `-y` three, `-t` two (`--title`
129
133
  and `--team`). There is no consistent target to copy. Ours holds **one meaning per letter across
@@ -154,6 +158,8 @@ Every one is now either identical, or says so on stderr, or is a usage error. Kn
154
158
  under `--quiet`) that deactivated users are still excluded and names `--include-disabled`, and
155
159
  `--help` says so up front. Not adopted: making `--all` mean "and deactivated" on two commands
156
160
  would give one global two meanings.
161
+ The same rule applies to lifecycle history: `--all` never includes archived records. Use
162
+ `--include-archived --all` to widen lifecycle visibility and exhaust pagination independently.
157
163
  5. **`-f <file>` on `project create/update`, `document create/update`** — theirs: `--description-file` /
158
164
  `--content-file`. Ours: `-f` is `--fields`, and it used to be _dropped_ on those commands, so
159
165
  `project create --name X -f desc.md` created the project with no description and exited 0.
package/README.md CHANGED
@@ -64,7 +64,8 @@ retains their best ideas and provides a careful compatibility and migration path
64
64
  - **Git-aware** — the current issue is inferred from your branch (`tes-123-fix` → `TES-123`), so most issue commands let you drop the id.
65
65
  - **Git + GitHub workflow** — `issue start` checks out the branch and marks the issue started, `issue describe` prints a commit message with Linear-issue trailers, `issue pr` opens a GitHub PR — all linked back to the issue.
66
66
  - **Forgiving inputs** — refer to things the way you think of them: `TES-123`, team `TES`, `--assignee me`, `--cycle current`, state and label by name.
67
- - **Multi-workspace** — store credentials for several workspaces and switch with a global `--workspace`.
67
+ - **Multi-workspace** — store credentials and a non-secret default team per workspace, then switch
68
+ both contexts with a global `--workspace`.
68
69
  - **Complete & honest** — first-class commands for the core resource graph, a raw `linear api` for everything else, and a [measured coverage audit](#coverage) that CI keeps honest.
69
70
 
70
71
  ## Install
@@ -230,20 +231,28 @@ linear auth login --workspace other-org # …and another workspace
230
231
  linear auth login --workspace acme --key - # personal API-key fallback via stdin
231
232
  linear auth list # show configured workspaces + which is default
232
233
  linear auth default acme # choose the default workspace
234
+ linear config set team ENG --user --workspace acme # set its validated default team
233
235
  linear --workspace other-org issue list # use a specific workspace for one command
234
236
  linear auth logout --workspace acme # revoke OAuth, then remove one credential
235
237
  ```
236
238
 
239
+ Successful browser and API-key login also save `workspace = "<authenticated slug>"` in the discovered project config, preserving other settings and comments. If no config exists, login creates `.linear.toml` at the Git root (or cwd outside Git). Use `auth login --no-project` for credential-only login. Human output reports the association path; JSON includes `projectConfigPath` (null with `--no-project`). Credentials remain in the global credential store. Environment overrides still take precedence over this association.
240
+
241
+ Global defaults are optional: login, credential adoption, and logout never choose a new one. Use `linear auth default <slug>` to explicitly select a global fallback. Removing that workspace clears its default without promoting another workspace. Existing defaults, including defaults imported from the reference CLI, are preserved on upgrade because older files cannot distinguish a user choice from an automatically assigned default. To opt out of a legacy default, remove the top-level `default_workspace` from the user config and, if present, `default` from the reference CLI's `credentials.toml`; retain the workspace entries.
242
+
237
243
  If revocation is intentionally unavailable, `auth logout --local-only` removes only local state.
238
244
  If browser login temporarily superseded an existing personal API-key profile for that workspace,
239
245
  OAuth logout preserves and reactivates the API key instead of deleting it.
240
246
 
241
247
  **Selection precedence** (strict): an explicit `--api-key` or `--access-token` flag bypasses the
242
- environment and workspace selection entirely. Otherwise `LINEAR_API_KEY` or
243
- `LINEAR_ACCESS_TOKEN` is absolute. With no invocation-scoped credential, the workspace is chosen
244
- by `--workspace` → `LINEAR_WORKSPACE` env → project config `workspace` → `default_workspace` in the
245
- user config. With one configured workspace it's used automatically; with several and no selection,
246
- the CLI asks you to pick (via `--workspace`, project config, or `auth default`).
248
+ environment and stored-credential lookup. Otherwise `LINEAR_API_KEY` or `LINEAR_ACCESS_TOKEN` is
249
+ absolute. With no invocation-scoped credential, the workspace is chosen by `--workspace` →
250
+ `LINEAR_WORKSPACE` env → project config `workspace` → `default_workspace` in the user config. With
251
+ one configured workspace it's used automatically; with several and no selection, interactive
252
+ commands prompt for an invocation-only choice. Noninteractive and JSON commands fail clearly
253
+ and explain how to select a workspace. Auth repair and discovery commands remain available. An invocation credential does not
254
+ silently inherit the default or project profile's team; pair it with `--workspace` (or
255
+ `LINEAR_WORKSPACE`) naming a configured profile when that profile metadata should apply.
247
256
 
248
257
  ## Core concepts
249
258
 
@@ -267,10 +276,76 @@ A few ideas run through every command:
267
276
  linear issue create --title "Fix login redirect" --team TES -P 2 --assignee me
268
277
  linear issue list --assignee me --state started
269
278
  linear issue update TES-42 --state "In Review" --add-label backend
279
+ linear issue label TES-42 --set-group 'Team=QA'
280
+ linear issue delegate TES-42 Codex
270
281
  linear issue comment TES-42 "ready for another look"
271
282
  linear issue archive TES-42 --yes
272
283
  ```
273
284
 
285
+ **Agent delegation (Developer Preview)** — the assignee remains the human owner; the delegate is
286
+ the agent working on their behalf. Create/update accept `--delegate <name|id>` and
287
+ `--clear-delegate`; the focused command is convenient on a matching branch:
288
+
289
+ ```sh
290
+ linear issue create --title "Investigate flaky CI" --assignee me --delegate Codex
291
+ linear issue update TES-42 --delegate Codex
292
+ linear issue update TES-42 --clear-delegate
293
+ linear issue delegate TES-42 Codex
294
+ linear issue delegate --clear # issue inferred from the branch
295
+ linear issue delegate TES-42 Codex --dry-run --json
296
+ linear issue delegate TES-42 Codex --full-result --json
297
+ ```
298
+
299
+ Delegate resolution accepts an agent-user UUID, exact display name, or exact full name, preferring
300
+ case-sensitive matches. It rejects human, inactive, non-assignable, ambiguous, and team-ineligible
301
+ users before writing. `--dry-run` resolves every field and prints the exact nullable `delegateId`
302
+ input without a mutation; `--full-result` (hidden alias `--read-back`) returns the relationship
303
+ receipt plus the canonical issue read-back and verifies the resulting delegate. These modes are
304
+ currently the delegation slice only: on `issue create`/`update`, pair them with `--delegate` or
305
+ `--clear-delegate`.
306
+
307
+ Delegating can trigger an externally observable Agent Session/webhook. Clearing a delegate does
308
+ not claim to cancel a session that is already running. Linear for Agents is in Developer Preview;
309
+ an unsupported workspace/schema returns `feature_not_accessible`, while ordinary `issue view`
310
+ falls back safely and reports `delegate: null` if the preview field itself is unavailable.
311
+ See Linear's current [agent integration guide](https://linear.app/developers/agents) and
312
+ [issue-assignment guide](https://linear.app/docs/assigning-issues) for the platform semantics.
313
+
314
+ **Label-group replacement** — replace only the selected group's direct member while preserving
315
+ every unrelated issue label. Repeat `--set-group` to plan several groups into one relative-label
316
+ mutation:
317
+
318
+ ```sh
319
+ linear issue label TES-42 --set-group 'Team=QA'
320
+ linear issue label TES-42 \
321
+ --set-group 'Team=QA' \
322
+ --set-group 'Issue Type=Bug' \
323
+ --json
324
+ linear issue label TES-42 --set-group 'Team=QA' --dry-run --json
325
+ linear issue label TES-42 --set-group 'Team=QA' --full-result --json
326
+ ```
327
+
328
+ Both sides accept a UUID or an exact, case-sensitive name. The value is split on the first `=`:
329
+ shell-quote the whole assignment when either name contains spaces or shell metacharacters; a label
330
+ name may contain additional `=` characters without escaping. If the group name itself contains
331
+ `=`, use its UUID on the left. Group containers, archived labels, labels outside the issue team's
332
+ workspace/team/inherited scope, and labels that are not direct members of the named group are
333
+ rejected before writing. Repeating an identical assignment normalizes to one operation; conflicting
334
+ targets for the same resolved group are a usage error. `--set-group` cannot be mixed with the
335
+ existing `--add`/`--remove` mode.
336
+
337
+ The JSON receipt includes the resolved group/member IDs plus `changed` and `mutationSent`. An
338
+ already-satisfied request succeeds with both fields `false` and sends no mutation. `--dry-run`
339
+ returns the shared mutation-plan envelope and the exact `addedLabelIds`/`removedLabelIds` input;
340
+ `--full-result` adds the canonical issue read-back, verification, and each requested group's prior
341
+ members (including all members repaired from an inconsistent pre-existing state).
342
+
343
+ This is a read/modify/write operation: the CLI reads the current labels, then sends one update with
344
+ relative added/removed IDs, so unrelated concurrent label changes are not overwritten. Linear does
345
+ not expose a compare-and-swap precondition here, so another writer changing the same group between
346
+ the read and update can race; use `--full-result` when post-write verification matters. See Linear's
347
+ current [label documentation](https://linear.app/docs/labels) for group and inheritance semantics.
348
+
274
349
  **Git + GitHub PR** — turn an issue into commits and a pull request. The id is inferred from the
275
350
  branch everywhere below.
276
351
 
@@ -311,7 +386,9 @@ EOF
311
386
 
312
387
  ```sh
313
388
  linear project list --team TES
389
+ linear project list --include-archived --all --json
314
390
  linear project view "Q3 Launch"
391
+ linear initiative list --include-archived --json
315
392
  linear project-update create "Q3 Launch" --health onTrack --body "Beta is out to 10% of users."
316
393
  linear initiative-update create "Platform" --health atRisk --body-file update.md
317
394
  ```
@@ -355,33 +432,89 @@ Every group has `--help` with full options and (for the busy ones) an Examples s
355
432
  are shown in parentheses. For a machine-readable tree of _every_ command, run
356
433
  `linear commands --json`.
357
434
 
358
- | Group | What you can do |
359
- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
360
- | **`issue`** (`i`) | `view` · `list` · `mine` · `search` · `create` · `update` · `delete` · `archive`/`unarchive` · `start` (git branch) · `describe` · `pull-request`/`pr` · `assign` · `state` · `label` · `comment`/`comments` · `relation` · `subscribe`/`unsubscribe` · `id`/`title`/`url`/`branch` |
361
- | **`team`** (`t`) | `list` · `view` · `members` · `states` · `labels` · `cycles` · `create` · `update` |
362
- | **`project`** (`p`) | `list` · `view` · `create` · `update` · `archive` · `milestones` |
363
- | **`project-update`** (`pu`) | `create` · `list` (project status updates, with `--health`) |
364
- | **`milestone`** (`m`) | `list` · `view` · `create` · `update` · `delete` |
365
- | **`cycle`** (`c`) | `list` · `view` · `current` · `create` · `update` |
366
- | **`user`** (`u`) | `list` · `view` · `me` |
367
- | **`label`** (`lb`) | `list` · `create` · `update` · `delete` |
368
- | **`state`** (`st`) | `list` · `view` (workflow states) |
369
- | **`comment`** (`cm`) | `list` · `add` · `reply` · `update` · `delete` · `resolve`/`unresolve` |
370
- | **`document`** (`doc`) | `list` · `view` · `create` · `update` · `delete` |
371
- | **`attachment`** (`at`) | `list` · `create` · `delete` |
372
- | **`favorite`** (`fav`) | `list` · `add` · `remove` |
373
- | **`custom-view`** (`cv`) | `list` · `view` · `results` · `create` · `update` · `delete` |
374
- | **`initiative`** (`init`) | `list` · `view` · `create` · `update` · `archive` · `delete` |
375
- | **`initiative-update`** (`iu`) | `create` · `list` (initiative status updates, with `--health`) |
376
- | **`roadmap`** (`rm`) | `list` · `view` · `create` · `update` · `delete` &nbsp;<sup>†</sup> |
377
- | **`notification`** (`notif`) | `list` · `read`/`unread` · `read-all` · `archive` · `snooze` |
378
- | **`organization`** (`org`) | `view` · `members` · `invites` |
379
- | **`webhook`** (`wh`) | `list` · `view` · `create` · `update` · `delete` |
380
- | **top-level** | `whoami` · `auth` (`login` · `list` · `default` · `token` · `status` · `logout`) · `config` · `api` · `commands` · `schema` · `completion` |
435
+ | Group | What you can do |
436
+ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
437
+ | **`issue`** (`i`) | `view` · `list` · `mine` · `search` · `create` · `update` · `delete` · `archive`/`unarchive` · `start` (git branch) · `describe` · `pull-request`/`pr` · `assign` · `delegate` · `state` · `label` · `comment`/`comments` · `relation` · `subscribe`/`unsubscribe` · `id`/`title`/`url`/`branch` |
438
+ | **`team`** (`t`) | `list` · `view` · `members` · `states` · `labels` · `cycles` · `create` · `update` |
439
+ | **`project`** (`p`) | `list` · `view` · `create` · `update` · `archive` · `milestones` |
440
+ | **`project-update`** (`pu`) | `create` · `list` (project status updates, with `--health`) |
441
+ | **`milestone`** (`m`) | `list` · `view` · `create` · `update` · `delete` |
442
+ | **`cycle`** (`c`) | `list` · `view` · `current` · `create` · `update` |
443
+ | **`user`** (`u`) | `list` · `view` · `me` |
444
+ | **`label`** (`lb`) | `list` · `create` · `update` · `delete` |
445
+ | **`state`** (`st`) | `list` · `view` (workflow states) |
446
+ | **`comment`** (`cm`) | `list` · `add` · `reply` · `update` · `delete` · `resolve`/`unresolve` |
447
+ | **`document`** (`doc`) | `list` · `view` · `create` · `update` · `delete` |
448
+ | **`attachment`** (`at`) | `list` · `create` · `delete` |
449
+ | **`favorite`** (`fav`) | `list` · `add` · `remove` |
450
+ | **`custom-view`** (`cv`) | `list` · `view` · `results` · `create` · `update` · `delete` |
451
+ | **`initiative`** (`init`) | `list` · `view` · `create` · `update` · `archive` · `delete` |
452
+ | **`initiative-update`** (`iu`) | `create` · `list` (initiative status updates, with `--health`) |
453
+ | **`roadmap`** (`rm`) | `list` · `view` · `create` · `update` · `delete` &nbsp;<sup>†</sup> |
454
+ | **`notification`** (`notif`) | `list` · `read`/`unread` · `read-all` · `archive` · `snooze` |
455
+ | **`organization`** (`org`) | `view` · `members` · `invites` |
456
+ | **`webhook`** (`wh`) | `list` · `view` · `create` · `update` · `delete` |
457
+ | **top-level** | `whoami` · `auth` (`login` · `list` · `default` · `token` · `status` · `logout`) · `config` · `api` · `commands` · `schema` · `completion` |
381
458
 
382
459
  <sup>†</sup> Linear has **deprecated roadmaps** in favor of initiatives — reads still work, but the
383
460
  API rejects roadmap mutations with a deprecation notice. Use `initiative` for new work.
384
461
 
462
+ ### Archived resources
463
+
464
+ Linear hides archived resources from paginated connections by default. On curated listings that
465
+ expose lifecycle history, `--include-archived` widens the result to live plus historical records;
466
+ it is not an archived-only filter. Linear can return trashed resources in that widened set, so
467
+ project, initiative, and issue JSON rows expose both `archivedAt: string|null` and
468
+ `trashed: boolean`, and human tables label `(archived)` and `(trashed)` separately. Notification
469
+ rows expose `archivedAt` (the public schema has no `trashed` field for notifications).
470
+
471
+ `--all` remains pagination-only. The two flags are independent and composable:
472
+
473
+ ```sh
474
+ linear project list --include-archived --all --json
475
+ linear initiative list --include-archived --status Completed --json
476
+ linear issue search "migration" --include-archived --all --json
477
+ ```
478
+
479
+ `initiative list --archived` remains a compatibility alias for `--include-archived`; passing both
480
+ spellings is an explicit usage error. The alias is retained for the current compatibility window
481
+ (all `0.x` releases; removal no earlier than `1.0`) and is listed by help and
482
+ `linear commands --json`.
483
+
484
+ The following matrix audits every curated list/search surface against the installed
485
+ `@linear/sdk@92.0.0` types and the live public schema exposed by `linear schema`. “Schema support”
486
+ means that the exact connection used by the command accepts `includeArchived`; it does not by
487
+ itself add a CLI contract. This change standardizes the four resource families named by
488
+ the issue. The remaining schema-capable collections stay unchanged until their own output and
489
+ resolver semantics are designed, rather than acquiring a silent partial implementation.
490
+
491
+ | Curated command(s) | Connection audited | Schema support | CLI lifecycle option | Lifecycle fields in list JSON |
492
+ | ------------------------------------------------------ | --------------------------------------------- | -------------- | ---------------------------------------------- | ----------------------------- |
493
+ | `issue list`, `issue mine` | `Query.issues` | yes | `--include-archived` | `archivedAt`, `trashed` |
494
+ | `issue search` | `Query.searchIssues` | yes | `--include-archived` | `archivedAt`, `trashed` |
495
+ | `project list` | `Query.projects` | yes | `--include-archived` | `archivedAt`, `trashed` |
496
+ | `initiative list` | `Query.initiatives` | yes | `--include-archived` (`--archived` alias) | `archivedAt`, `trashed` |
497
+ | `notification list` | `Query.notifications` | yes | `--include-archived` | `archivedAt` |
498
+ | `attachment list` | `Issue.attachments` | yes | not exposed | not exposed |
499
+ | `comment list`, `issue comment list`, `issue comments` | `Issue.comments` | yes | not exposed | not exposed |
500
+ | `cycle list`, `team cycles` | `Team.cycles` | yes | not exposed | not exposed |
501
+ | `custom-view list` | `Query.customViews` | yes | not exposed | `archivedAt` |
502
+ | `custom-view results` | `CustomView.issues/projects/initiatives` | yes | not exposed | not exposed |
503
+ | `document list` | `Query.documents` | yes | not exposed | not exposed |
504
+ | `favorite list` | `Query.favorites` | yes | not exposed | not exposed |
505
+ | `initiative-update list` | `Initiative.initiativeUpdates` | yes | not exposed | not exposed |
506
+ | `issue agent-session list` | `Query.agentSessions` / `Issue.agentSessions` | yes | not exposed | not exposed |
507
+ | `label list`, `team labels` | `Query.issueLabels` / `Team.labels` | yes | not exposed | not exposed |
508
+ | `milestone list`, `project milestones` | `Project.projectMilestones` | yes | not exposed | not exposed |
509
+ | `project-update list` | `Project.projectUpdates` | yes | not exposed | not exposed |
510
+ | `roadmap list` | `Query.roadmaps` | yes | not exposed | not exposed |
511
+ | `state list`, `team states` | `Team.states` | yes | not exposed | not exposed |
512
+ | `team list` | `Query.teams` | yes | not exposed | not exposed |
513
+ | `team members`, `user list`, `organization members` | user connections | yes | not exposed (`--include-disabled` is separate) | not exposed |
514
+ | `organization invites` | `Query.organizationInvites` | yes | not exposed | not exposed |
515
+ | `webhook list` | `Query.webhooks` | yes | not exposed | not exposed |
516
+ | `auth list` | local credential registry (no API connection) | no | not applicable | not applicable |
517
+
385
518
  ### Global flags
386
519
 
387
520
  | Flag | Effect |
@@ -452,7 +585,8 @@ so prefer the `code` field for fine-grained handling and the exit code for coars
452
585
  - **`-q, --quiet`** — suppress success/status lines on stderr (errors still print).
453
586
  - **`-n, --limit <n>` / `--all`** — `--limit` caps results; `--all` exhausts pagination. With
454
587
  neither, the default cap is **50**. `--all` (and very large `--limit`) can be slow and
455
- rate-limit-prone on big workspaces.
588
+ rate-limit-prone on big workspaces. It never includes archived resources; combine it with
589
+ `--include-archived` where that option is available.
456
590
 
457
591
  ```sh
458
592
  # agent-safe: no prompts, no chatter, fail fast with a parseable error
@@ -563,41 +697,54 @@ Non-secret defaults live in `~/.config/linear/config.toml` (user-wide) or a proj
563
697
  file. **Secrets never go in a project file** — the API key is only ever read from the flag, the
564
698
  env, the user config, or the OS keyring.
565
699
 
566
- Precedence, highest first — `linear config` prints each value with the tier and file it came from:
700
+ Effective team precedence, highest first — `linear config` prints the tier, file, and selected
701
+ profile slug where applicable:
567
702
 
568
703
  1. the flag (`--team`, `--workspace`, `--sort`)
569
704
  2. the environment (`LINEAR_TEAM`/`LINEAR_TEAM_ID`, `LINEAR_WORKSPACE`, `LINEAR_ISSUE_SORT`)
570
705
  3. the **project config**: the first file found walking up from the working directory, checking in
571
706
  each directory `linear.toml`, then `.linear.toml`, then `.config/linear.toml`; compatible
572
707
  existing project configuration is picked up as-is
573
- 4. the **user config**, `~/.config/linear/config.toml` (`$XDG_CONFIG_HOME` honored)
574
- 5. the legacy **global config**, `~/.config/linear/linear.toml` — read for non-secret settings
708
+ 4. `team` on the selected `[workspaces."<slug>"]` profile
709
+ 5. the legacy top-level `team` in the **user config**, `~/.config/linear/config.toml`
710
+ (`$XDG_CONFIG_HOME` honored)
711
+ 6. the legacy **global config**, `~/.config/linear/linear.toml` — read for non-secret settings
575
712
  only, so a migrating user's defaults carry over
713
+ 7. no default team
714
+
715
+ `workspace` and `sort` keep their existing flag → environment → project → user → legacy-global
716
+ order. Workspace credential selection is independent and follows the order described above.
576
717
 
577
718
  Keys: `team` (or `team_id`), `workspace`, and `sort` (or `issue_sort`). You can write these from
578
- the CLI — comments and layout in an existing file are preserved, and the write is atomic:
719
+ the CLI. Project and top-level setting edits preserve comments and layout. Profile updates preserve
720
+ all TOML data and use the same locked, atomic rewrite as credential updates:
579
721
 
580
722
  ```sh
581
723
  linear config init # pick a team from a list → <git root>/.linear.toml
582
724
  linear config init --team TES # …or say which (scripts); --sort, --path, --force
583
725
  linear config set sort updated # edit one key in the project config in effect
584
- linear config set team ENG --user # …or in ~/.config/linear/config.toml
726
+ linear config set team ENG --user # legacy top-level user fallback (backward compatible)
727
+ linear config set team ENG --user --workspace acme # validated default for the acme profile
585
728
  linear config # show the result, with each value's source
586
729
  ```
587
730
 
588
731
  `config set` will not write `api_key` or anything else that belongs to the credential store — that
589
- is what `auth login` is for.
732
+ is what `auth login` is for. A profile team is validated against the selected workspace whenever a
733
+ credential is available. Login, adopt, OAuth refresh/fallback, and credential migration preserve
734
+ profile metadata. Logout reports whether removing a whole profile also removed its team metadata.
590
735
 
591
736
  ```toml
592
737
  # ~/.config/linear/config.toml
593
738
  default_workspace = "acme" # which stored credential is active
594
- team = "TES" # default team key
739
+ team = "TES" # optional legacy/global fallback team
595
740
  sort = "priority" # default issue-list sort: priority | updated | created
596
741
 
597
742
  [workspaces."acme"] # per-workspace credentials (hyphenated slugs are quoted)
598
743
  keyring = true # secret lives in the OS keyring (service linear-cli, account acme)
744
+ team = "ENG" # non-secret default team for this workspace
599
745
  [workspaces."other-org"]
600
746
  api_key = "lin_api_yyyyyyyy" # …or, with `auth login --plaintext`, in this 0600 file
747
+ team = "OPS"
601
748
  ```
602
749
 
603
750
  Relevant environment variables: **`LINEAR_API_KEY`** or **`LINEAR_ACCESS_TOKEN`** (absolute —
@@ -655,6 +802,15 @@ bun run audit:coverage # regenerate COVERAGE.md (add --update to re-baseline t
655
802
  bun run janitor # sweep leaked `clitest-` fixtures from the test workspace
656
803
  ```
657
804
 
805
+ The live delegation suite is additionally gated by `LINEAR_CLI_LIVE_AGENT_ID`, the UUID of an
806
+ explicit disposable-test agent. It can trigger that integration's Agent Session/webhook and is
807
+ never enabled by the general live-test flag alone.
808
+
809
+ The live label-group suite is additionally gated by `LINEAR_CLI_TEST_LABEL_GROUP` and
810
+ `LINEAR_CLI_TEST_LABEL_GROUP_MEMBER`, naming (or identifying by UUID) a pre-existing group and one
811
+ direct member usable by `LINEAR_CLI_TEST_TEAM`. It creates only a disposable issue; it does not
812
+ create, rename, move, or delete the shared label-group fixture.
813
+
658
814
  Architecture is three layers — **commands** (commander wiring) → **services** (one module per
659
815
  resource, the only place that touches the SDK) → **`@linear/sdk`**. The machine JSON envelope is
660
816
  locked and contract-tested so output never silently drifts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linear-sdk-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Ergonomic command-line interface for Linear, built on @linear/sdk",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -57,6 +57,7 @@
57
57
  "format": "oxfmt .",
58
58
  "format:check": "oxfmt --check .",
59
59
  "test": "bun test test/unit test/contract",
60
+ "test:performance": "bun test --timeout 15000 test/performance",
60
61
  "test:unit": "bun test test/unit",
61
62
  "test:contract": "bun test test/contract",
62
63
  "test:live": "LINEAR_CLI_LIVE=1 bun test --timeout 60000 test/integration",
@@ -69,8 +70,8 @@
69
70
  "janitor": "bun run test/janitor.ts"
70
71
  },
71
72
  "dependencies": {
72
- "@inquirer/prompts": "^8.7.0",
73
- "@linear/sdk": "^92.0.0",
73
+ "@inquirer/prompts": "^8.7.1",
74
+ "@linear/sdk": "^93.0.1",
74
75
  "commander": "^15.0.0",
75
76
  "graphql": "^17.0.2",
76
77
  "picocolors": "^1.1.1",
@@ -78,10 +79,10 @@
78
79
  },
79
80
  "packageManager": "bun@1.4.0",
80
81
  "devDependencies": {
81
- "@types/bun": "^1.4.0",
82
- "@types/node": "^26.4.0",
83
- "oxfmt": "^0.65.0",
84
- "oxlint": "^1.80.0",
82
+ "@types/bun": "^1.4.1",
83
+ "@types/node": "^26.4.1",
84
+ "oxfmt": "^0.66.0",
85
+ "oxlint": "^1.81.0",
85
86
  "typescript": "^7.0.2"
86
87
  }
87
88
  }
@@ -94,6 +94,8 @@ The CLI supports distinct human, hosted-app, and personal-key lifecycles:
94
94
  linear issue list --workspace acme --json
95
95
  ```
96
96
 
97
+ Login associates the authenticated workspace with the discovered project config, or creates `.linear.toml` at the Git root (cwd outside Git). Use `--no-project` for credential-only login; JSON reports `projectConfigPath` or null. Credentials stay global. Workspace selection is `--workspace` → `LINEAR_WORKSPACE` → project config → optional global default. A sole stored workspace is automatic; multiple unselected workspaces prompt only interactively and fail in JSON/noninteractive mode. Login/adoption/logout never assign a new default. Existing legacy defaults remain valid because their original intent is unknown; `auth default` is an explicit convenience.
98
+
97
99
  An explicit credential flag overrides environment credentials. If both credential kinds occur at
98
100
  the same precedence level, the CLI fails rather than silently choosing an actor. Inspect resolution
99
101
  with `linear auth status`. `linear auth token` exports stored API keys only and never OAuth tokens.
@@ -215,6 +217,14 @@ perfectly with a heredoc:
215
217
  - `comment add` / `comment update` / `comment reply` → `--body-file <path>`
216
218
  - `document create` / `document update` → `--content-file <path>`
217
219
 
220
+ Comments canonically use `add`, while resources such as attachments use `create`.
221
+ `create` is also accepted as a compatibility alias for comment `add`. For a file-backed
222
+ issue comment, copy this form:
223
+
224
+ ```bash
225
+ linear issue comment add LUMI-9340 --body-file comment.md --json
226
+ ```
227
+
218
228
  ```bash
219
229
  linear issue create --title "Onboarding bug" --team TES --no-input --json \
220
230
  --description-file - <<'EOF'
@@ -374,6 +384,7 @@ linear issue comment list
374
384
  linear issue comment update
375
385
  linear issue comments
376
386
  linear issue create
387
+ linear issue delegate
377
388
  linear issue delete
378
389
  linear issue describe
379
390
  linear issue id
@@ -69,7 +69,7 @@ storage: string
69
69
 
70
70
  ### `linear auth login`
71
71
 
72
- Authenticate in the browser with OAuth, or store a personal API key
72
+ Authenticate and select the workspace for this project
73
73
 
74
74
  ```
75
75
  linear auth login [options]
@@ -77,6 +77,7 @@ linear auth login [options]
77
77
 
78
78
  | Option | Description |
79
79
  | ---------------------- | --------------------------------------------------------------------- |
80
+ | `--no-project` | save credentials without changing the project workspace |
80
81
  | `--key <key>` | use a personal API key ('-' reads it from stdin) |
81
82
  | `--plaintext` | Store the key in the config file (0600) instead of the system keyring |
82
83
  | `--no-browser` | print the authorization URL instead of opening it |
@@ -97,6 +98,7 @@ storage: string
97
98
  scopes?: string[]
98
99
  expiresAt?: string
99
100
  path: string
101
+ projectConfigPath: string | null
100
102
  ```
101
103
 
102
104
  ### `linear auth logout`
@@ -119,6 +121,7 @@ workspace: string
119
121
  removed: boolean
120
122
  revocation: string
121
123
  fallbackCredentialType: string | null
124
+ teamMetadataRemoved: boolean
122
125
  ```
123
126
 
124
127
  ### `linear auth migrate`
@@ -22,6 +22,8 @@ linear comment [options]
22
22
 
23
23
  Add a comment to an issue (images uploaded with --attach render inline)
24
24
 
25
+ Aliases: `create`
26
+
25
27
  ```
26
28
  linear comment add [options] <issue> [body]
27
29
  ```
@@ -22,10 +22,11 @@ apiKeySource: string
22
22
  accessToken: string
23
23
  accessTokenSource: string
24
24
  credentialWorkspace: string | null
25
+ workspaceProfile: string | null
25
26
  team: string | null
26
27
  workspace: string | null
27
28
  sort: string
28
- origins: {team: {source: string, path?: string, key?: string}, workspace: {source: string, path?: string, key?: string}, sort: {source: string, path?: string, key?: string}}
29
+ origins: {team: {source: string, path?: string, key?: string, workspace?: string}, workspace: {source: string, path?: string, key?: string, workspace?: string}, sort: {source: string, path?: string, key?: string, workspace?: string}}
29
30
  userConfigPath: string
30
31
  projectConfigPath: string | null
31
32
  globalConfigPath: string | null
@@ -57,16 +58,16 @@ sort?: string
57
58
 
58
59
  ### `linear config set`
59
60
 
60
- Set one project setting (team, workspace, sort) in the project config
61
+ Set one project or user setting (team, workspace, sort)
61
62
 
62
63
  ```
63
64
  linear config set [options] <key> <value>
64
65
  ```
65
66
 
66
- | Option | Description |
67
- | --------------- | ------------------------------------------------------------ |
68
- | `--user` | write the user config (~/.config/linear/config.toml) instead |
69
- | `--path <file>` | write this file instead of the project config in effect |
67
+ | Option | Description |
68
+ | --------------- | ------------------------------------------------------------------------- |
69
+ | `--user` | write the user config (team + --workspace targets that workspace profile) |
70
+ | `--path <file>` | write this file instead of the project config in effect |
70
71
 
71
72
  **Output (`--json`)**: a receipt object
72
73
 
@@ -75,6 +76,7 @@ success: boolean
75
76
  path: string
76
77
  key: string
77
78
  value: string
79
+ workspace?: string
78
80
  ```
79
81
 
80
82
  ### `linear config show`
@@ -93,10 +95,11 @@ apiKeySource: string
93
95
  accessToken: string
94
96
  accessTokenSource: string
95
97
  credentialWorkspace: string | null
98
+ workspaceProfile: string | null
96
99
  team: string | null
97
100
  workspace: string | null
98
101
  sort: string
99
- origins: {team: {source: string, path?: string, key?: string}, workspace: {source: string, path?: string, key?: string}, sort: {source: string, path?: string, key?: string}}
102
+ origins: {team: {source: string, path?: string, key?: string, workspace?: string}, workspace: {source: string, path?: string, key?: string, workspace?: string}, sort: {source: string, path?: string, key?: string, workspace?: string}}
100
103
  userConfigPath: string
101
104
  projectConfigPath: string | null
102
105
  globalConfigPath: string | null