linear-sdk-cli 0.8.3 → 0.9.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,19 @@ 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.9.1](https://github.com/zhendalf/linear-sdk-cli/compare/v0.9.0...v0.9.1) (2026-10-22)
8
+
9
+ ### Bug Fixes
10
+
11
+ - **auth:** default to personal API-key login ([#98](https://github.com/zhendalf/linear-sdk-cli/issues/98)) ([0d57f7c](https://github.com/zhendalf/linear-sdk-cli/commit/0d57f7c741bc5e9099787271cde16f436cb21f13))
12
+
13
+ ## [0.9.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.8.3...v0.9.0) (2026-10-22)
14
+
15
+ ### Features
16
+
17
+ - add native release commands and folder project defaults ([#97](https://github.com/zhendalf/linear-sdk-cli/issues/97)) ([e382f52](https://github.com/zhendalf/linear-sdk-cli/commit/e382f52a8146ede0d9268d6c6aa557f9836e9274))
18
+ - **auth:** add resumable workspace API-key profile rename ([#95](https://github.com/zhendalf/linear-sdk-cli/issues/95)) ([9297b16](https://github.com/zhendalf/linear-sdk-cli/commit/9297b16e00928441124bffe291a0567e86c59f09))
19
+
7
20
  ## [0.8.3](https://github.com/zhendalf/linear-sdk-cli/compare/v0.8.2...v0.8.3) (2026-10-19)
8
21
 
9
22
  ### Bug Fixes
package/COVERAGE.md CHANGED
@@ -4,18 +4,19 @@ Generated by `bun run audit:coverage`. Total LinearClient members: **481**.
4
4
 
5
5
  | Status | Count | Meaning |
6
6
  | -------- | ----- | ----------------------------------------------- |
7
- | curated | 107 | Has a first-class CLI command |
7
+ | curated | 128 | Has a first-class CLI command |
8
8
  | raw-only | 146 | Reachable via `linear api` (no bespoke command) |
9
- | excluded | 228 | Out of scope (admin/integration/SDK plumbing) |
9
+ | excluded | 207 | 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
14
  ## Curated field capabilities
15
15
 
16
+ - Release projections — `ReleasePipeline.stages`, `Release.issues/links`, and release-note document content are selected through the authenticated SDK GraphQL transport, with stable command JSON shapes and offline schema validation.
16
17
  - 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
18
 
18
- ## Curated (107)
19
+ ## Curated (128)
19
20
 
20
21
  - `agentSession` — issue
21
22
  - `agentSessions` — issue
@@ -41,9 +42,14 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
41
42
  - `createIssue` — issue
42
43
  - `createIssueLabel` — label
43
44
  - `createIssueRelation` — issue
45
+ - `createIssueToRelease` — release
44
46
  - `createProject` — project
45
47
  - `createProjectMilestone` — milestone
46
48
  - `createProjectUpdate` — project-update
49
+ - `createRelease` — release
50
+ - `createReleaseNote` — release-note
51
+ - `createReleasePipeline` — release-pipeline
52
+ - `createReleaseStage` — release-stage
47
53
  - `createRoadmap` — roadmap
48
54
  - `createTeam` — team
49
55
  - `createWebhook` — webhook
@@ -63,6 +69,7 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
63
69
  - `deleteIssueRelation` — issue
64
70
  - `deleteProject` — project
65
71
  - `deleteProjectMilestone` — milestone
72
+ - `deleteReleaseNote` — release-note
66
73
  - `deleteRoadmap` — roadmap
67
74
  - `deleteTeam` — team
68
75
  - `deleteWebhook` — webhook
@@ -82,6 +89,7 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
82
89
  - `issueRelations` — issue
83
90
  - `issueSearch` — issue
84
91
  - `issueSubscribe` — issue
92
+ - `issueToReleaseDeleteByIssueAndRelease` — release
85
93
  - `issueUnsubscribe` — issue
86
94
  - `issues` — issue
87
95
  - `notification` — notification
@@ -96,6 +104,16 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
96
104
  - `projectStatuses` — project
97
105
  - `projectUpdates` — project-update
98
106
  - `projects` — project
107
+ - `release` — release
108
+ - `releaseComplete` — release
109
+ - `releaseNote` — release-note
110
+ - `releaseNotes` — release-note
111
+ - `releasePipeline` — release-pipeline
112
+ - `releasePipelines` — release-pipeline
113
+ - `releaseStage` — release-stage
114
+ - `releaseSync` — release
115
+ - `releaseUpdateByPipeline` — release
116
+ - `releases` — release
99
117
  - `roadmap` — roadmap
100
118
  - `roadmaps` — roadmap
101
119
  - `searchIssues` — issue
@@ -114,6 +132,10 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
114
132
  - `updateNotification` — notification
115
133
  - `updateProject` — project
116
134
  - `updateProjectMilestone` — milestone
135
+ - `updateRelease` — release
136
+ - `updateReleaseNote` — release-note
137
+ - `updateReleasePipeline` — release-pipeline
138
+ - `updateReleaseStage` — release-stage
117
139
  - `updateRoadmap` — roadmap
118
140
  - `updateTeam` — team
119
141
  - `updateWebhook` — webhook
@@ -125,7 +147,7 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
125
147
  - `workflowState` — state
126
148
  - `workflowStates` — state
127
149
 
128
- ## Excluded (228)
150
+ ## Excluded (207)
129
151
 
130
152
  - `agentActivities` — specialized/raw api only
131
153
  - `agentActivity` — specialized/raw api only
@@ -161,14 +183,9 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
161
183
  - `createIntegrationGithubCommit` — integration/import (raw api only)
162
184
  - `createIntegrationTemplate` — integration/import (raw api only)
163
185
  - `createIntegrationsSettings` — integration/import (raw api only)
164
- - `createIssueToRelease` — specialized/raw api only
165
186
  - `createNotificationSubscription` — specialized/raw api only
166
187
  - `createOrganizationInvite` — admin/enterprise (raw api only)
167
188
  - `createPushSubscription` — specialized/raw api only
168
- - `createRelease` — specialized/raw api only
169
- - `createReleaseNote` — specialized/raw api only
170
- - `createReleasePipeline` — specialized/raw api only
171
- - `createReleaseStage` — specialized/raw api only
172
189
  - `createTemplate` — specialized/raw api only
173
190
  - `createTimeSchedule` — specialized/raw api only
174
191
  - `createTriageResponsibility` — specialized/raw api only
@@ -207,7 +224,6 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
207
224
  - `deleteOrganizationInvite` — admin/enterprise (raw api only)
208
225
  - `deletePushSubscription` — specialized/raw api only
209
226
  - `deleteRelease` — specialized/raw api only
210
- - `deleteReleaseNote` — specialized/raw api only
211
227
  - `deleteReleasePipeline` — specialized/raw api only
212
228
  - `deleteTemplate` — specialized/raw api only
213
229
  - `deleteTimeSchedule` — specialized/raw api only
@@ -276,7 +292,6 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
276
292
  - `issueImportProcess` — integration/import (raw api only)
277
293
  - `issueTitleSuggestionFromCustomerRequest` — specialized/raw api only
278
294
  - `issueToRelease` — specialized/raw api only
279
- - `issueToReleaseDeleteByIssueAndRelease` — specialized/raw api only
280
295
  - `issueToReleases` — specialized/raw api only
281
296
  - `latestReleaseByAccessKey` — specialized/raw api only
282
297
  - `logout` — auth/session flows (out of scope)
@@ -291,22 +306,12 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
291
306
  - `pushSubscriptionTest` — specialized/raw api only
292
307
  - `rateLimitStatus` — SDK plumbing
293
308
  - `recentReleasesByAccessKey` — specialized/raw api only
294
- - `release` — specialized/raw api only
295
- - `releaseComplete` — specialized/raw api only
296
309
  - `releaseCompleteByAccessKey` — specialized/raw api only
297
- - `releaseNote` — specialized/raw api only
298
- - `releaseNotes` — specialized/raw api only
299
- - `releasePipeline` — specialized/raw api only
300
310
  - `releasePipelineByAccessKey` — specialized/raw api only
301
- - `releasePipelines` — specialized/raw api only
302
311
  - `releaseSearch` — specialized/raw api only
303
- - `releaseStage` — specialized/raw api only
304
312
  - `releaseStages` — specialized/raw api only
305
- - `releaseSync` — specialized/raw api only
306
313
  - `releaseSyncByAccessKey` — specialized/raw api only
307
- - `releaseUpdateByPipeline` — specialized/raw api only
308
314
  - `releaseUpdateByPipelineByAccessKey` — specialized/raw api only
309
- - `releases` — specialized/raw api only
310
315
  - `samlTokenUserAccountAuth` — auth/session flows (out of scope)
311
316
  - `semanticSearch` — specialized/raw api only
312
317
  - `slaConfigurations` — specialized/raw api only
@@ -341,10 +346,6 @@ Every member is classified — CI fails otherwise. `raw-only` members are still
341
346
  - `updateIntegrationsSettings` — integration/import (raw api only)
342
347
  - `updateIssueImport` — integration/import (raw api only)
343
348
  - `updateNotificationSubscription` — specialized/raw api only
344
- - `updateRelease` — specialized/raw api only
345
- - `updateReleaseNote` — specialized/raw api only
346
- - `updateReleasePipeline` — specialized/raw api only
347
- - `updateReleaseStage` — specialized/raw api only
348
349
  - `updateTemplate` — specialized/raw api only
349
350
  - `updateTimeSchedule` — specialized/raw api only
350
351
  - `updateTriageResponsibility` — specialized/raw api only
package/MIGRATING.md CHANGED
@@ -48,13 +48,21 @@ linear auth list # …lists it, Storage: keychain
48
48
 
49
49
  If it does not — no keyring on this platform, or you used schpet's plaintext mode (`slug =
50
50
  "lin_api_…"` inline in `credentials.toml`, which we deliberately do not read) — pipe the key to
51
- `linear auth login --key -`; `--plaintext` keeps it in our `0600` `config.toml` instead. Bare
52
- `linear auth login` now starts browser OAuth, so it never prompts for a personal API key. `auth
53
- migrate` moves plaintext credentials from our file into the keyring, same command name as theirs.
51
+ `linear auth login --key -`, or run bare `linear auth login` for a hidden personal API-key prompt.
52
+ Login requires a system keyring unless you explicitly pass `--plaintext` to use our `0600`
53
+ `config.toml` instead. `auth migrate` moves plaintext credentials from our file into the keyring,
54
+ same command name as theirs.
54
55
  Because the API-key entry is shared, `auth logout` here removes it for both tools (and drops the
55
56
  slug from schpet's list file, so neither reports a workspace whose key is gone). The API key is
56
57
  **never** read from a project `.linear.toml`.
57
58
 
59
+ Earlier versions defaulted to browser OAuth through a bundled client ID. That login flow and its
60
+ callback options have been removed. Existing stored OAuth user profiles still refresh and support
61
+ logout/revocation using their saved client ID. For new authentication, use a personal API key,
62
+ `LINEAR_ACCESS_TOKEN` / `--access-token` on commands (the issuing grant determines user/app identity),
63
+ or `auth login --app` with your own client ID and secret. Supplied access tokens remain temporary;
64
+ the CLI does not persist or refresh them. No hardcoded OAuth client ID is shipped.
65
+
58
66
  ## 3. Config — same files, same keys
59
67
 
60
68
  We look in every place schpet does, in its order. Walking up from the working directory, each
package/README.md CHANGED
@@ -70,9 +70,9 @@ retains their best ideas and provides a careful compatibility and migration path
70
70
 
71
71
  ## Install
72
72
 
73
- Requires [Bun](https://bun.sh) **1.1 or newer**. Authenticate with browser OAuth (the default), an
74
- invocation-scoped OAuth access token, or a personal API key. The CLI ships as TypeScript and runs
75
- directly on Bun — no build step, no bundle, no Node.
73
+ Requires [Bun](https://bun.sh) **1.1 or newer**. Authenticate with a personal API key (the default),
74
+ an invocation-scoped OAuth access token, or your app's client credentials. The CLI ships as
75
+ TypeScript and runs directly on Bun — no build step, no bundle, no Node.
76
76
 
77
77
  ```sh
78
78
  bun add --global linear-sdk-cli
@@ -103,7 +103,7 @@ bun run src/bin/linear.ts --help # or: bun run dev -- --help
103
103
  ## Quickstart
104
104
 
105
105
  ```sh
106
- linear auth login # browser OAuth; or set LINEAR_API_KEY
106
+ linear auth login # securely prompt for a personal API key
107
107
  linear whoami # confirm you're connected
108
108
 
109
109
  linear issue list --assignee me --state started # my in-progress work
@@ -118,9 +118,12 @@ branch, so most issue commands let you omit the id entirely.
118
118
 
119
119
  ## Authentication
120
120
 
121
- For a person at a terminal, `linear auth login` uses browser Authorization Code + PKCE and the
122
- default `actor=user`. Existing personal API keys and invocation-scoped OAuth access tokens remain
123
- supported. Explicit `--api-key` or `--access-token` flags override environment credentials;
121
+ For a person at a terminal, `linear auth login` prompts for a personal API key without echoing it.
122
+ Create a key in [Security & Access](https://linear.app/settings/account/security), choosing its
123
+ permissions and team access there. Workspace administrators may restrict member key creation.
124
+ No OAuth application installation, bundled client ID, or browser callback is required.
125
+
126
+ For commands, explicit `--api-key` or `--access-token` flags override environment credentials;
124
127
  without a credential flag the CLI reads `LINEAR_API_KEY`, `LINEAR_ACCESS_TOKEN`, or the pair
125
128
  `LINEAR_CLIENT_ID` / `LINEAR_CLIENT_SECRET`. Mixing credential types in the environment fails
126
129
  rather than silently choosing a Linear actor. Client credentials must be supplied together;
@@ -129,14 +132,13 @@ explicit credential flags still override all environment credentials.
129
132
  > **Credential trust boundary.** A secret is **never** read from a project-local
130
133
  > `.linear.toml` — only non-secret settings live there — so a key can't be committed by accident.
131
134
  > A project may select one of your already-stored credentials with `workspace = "<slug>"`, but it
132
- > cannot provide or override the secret. Browser OAuth credentials are keyring-only; an access
133
- > token injected with `--access-token` or `LINEAR_ACCESS_TOKEN` remains invocation-scoped.
135
+ > cannot provide or override the secret. Existing browser OAuth credentials remain keyring-only;
136
+ > a token injected with `--access-token` or `LINEAR_ACCESS_TOKEN` remains invocation-scoped.
134
137
 
135
138
  ```sh
136
- linear auth login # browser OAuth, read + write, actor=user
137
- linear auth login --read-only # request only Linear's read scope
138
- linear auth login --no-browser # print the URL; still waits on the loopback callback
139
- printf '%s\n' "$LINEAR_API_KEY" | linear auth login --key - # personal API-key fallback
139
+ linear auth login # hidden personal API-key prompt
140
+ printf '%s\n' "$LINEAR_API_KEY" | linear auth login --key - # explicit stdin input
141
+ linear auth login --app --client-id YOUR_CLIENT_ID --client-secret -
140
142
  linear auth status # credential kind and source (value redacted)
141
143
  linear auth logout --workspace acme # revoke OAuth (if used) and remove the profile
142
144
  ```
@@ -148,30 +150,27 @@ Linear provides no safe read-only scope introspection for them. `whoami --json`
148
150
  member's workspace role in `admin` and reports credential authorization separately under
149
151
  `authorization`; an admin member can still hold a credential that lacks the `admin` scope.
150
152
 
151
- Browser login generates a cryptographically random verifier and CSRF state, requires PKCE S256,
152
- opens Linear, and listens temporarily on the registered `127.0.0.1` callback. It validates the
153
- viewer and workspace before saving the access token, rotating refresh token, expiry, granted
154
- scopes, OAuth client identity, and workspace identity. The whole record lives only in the system
155
- keyring (`service = linear-cli`, `account = oauth:<workspace slug>`); the config file gets only
156
- non-secret `keyring = true` and `oauth = true` markers. Access tokens refresh before expiry and
157
- once after an authentication failure, with refresh-token rotation committed under the credential
158
- store lock. `--admin` explicitly adds the admin scope; it is never implicit.
159
-
160
- `--no-browser` is useful over SSH only when the machine running the CLI is also reachable as the
161
- browser's loopback host; Linear does not currently document a device-code flow. Override a
162
- separately registered public client's identity with `LINEAR_OAUTH_CLIENT_ID` and its exact callback
163
- with `LINEAR_OAUTH_REDIRECT_URI`. No OAuth client secret is embedded or used for browser login.
164
-
165
- For a personal API key, `auth login --key -` reads stdin, validates the viewer/workspace, stores the
166
- key in the system keyring by default, and writes only a marker to the config. `--plaintext` is an
167
- API-key-only compatibility option. API-key entries use `account = <workspace slug>`, preserving
168
- compatibility with existing keyring profiles. Passing a key as `--key <value>` works but warns
169
- because argv is visible to other processes. `auth migrate` moves legacy plaintext keys into the
170
- keyring, and `auth token` exports stored API keys only; OAuth tokens are never printed.
153
+ Both the prompt and `auth login --key -` validate the viewer/workspace before storing the key in
154
+ the system keyring (`service = linear-cli`, `account = <workspace slug>`), with only a marker in
155
+ the user config. If there is no system keyring, login requires explicit `--plaintext` to store
156
+ API-key or app credentials in the private user config (`0600`). Passing `--key <value>` also works
157
+ but warns because argv and shell history expose it. `auth migrate` moves legacy plaintext keys
158
+ into the keyring; `auth token` exports stored API keys only, never OAuth tokens.
159
+
160
+ JSON, `--no-input`, and redirected sessions never prompt. Supply `--key -` explicitly for stdin;
161
+ bare login does not import environment credentials. To authenticate a command without saving
162
+ credentials, set `LINEAR_API_KEY` or `LINEAR_ACCESS_TOKEN` instead. `--read-only` and `--scope`
163
+ apply only to `--app`; set personal API-key permissions in Linear when creating the key.
164
+
165
+ Browser login and its `--no-browser`, `--redirect-uri`, `--timeout`, and `--admin` options have
166
+ been removed. Existing stored OAuth user profiles still refresh using their own saved client ID,
167
+ retain scope reporting, and support revocation on logout. No embedded client ID is needed to use
168
+ these profiles. New personal logins use API keys.
171
169
 
172
170
  ### Apps, agents, and service accounts
173
171
 
174
- Use an OAuth access token when the CLI runs inside an integration or agent:
172
+ Use an OAuth access token when the CLI runs inside an integration or agent. The token may
173
+ represent a user or app; its issuing grant determines the identity and permissions:
175
174
 
176
175
  ```sh
177
176
  LINEAR_ACCESS_TOKEN="$access_token" linear whoami --json
@@ -256,7 +255,7 @@ access to the same user config. Switching storage removes the previous copy of t
256
255
  replacing an existing keyring profile requires access to that keyring.
257
256
 
258
257
  Use `--read-only` to request only `read`, or `--scope read,comments:create` for custom app scopes
259
- (default: `read,write`). App actors cannot request `--admin` or an `admin` scope. Linear must have
258
+ (default: `read,write`). App actors cannot request an `admin` scope. Linear must have
260
259
  client credentials enabled for the app. Changing scopes can revoke existing app tokens, so use
261
260
  the same scopes across hosts.
262
261
 
@@ -280,13 +279,13 @@ disable the app in Linear. Environment-only credentials must be unset separately
280
279
 
281
280
  ### Multiple workspaces
282
281
 
283
- Credentials are stored per **workspace slug**. Browser, app, and API-key login derive the slug from
282
+ Credentials are stored per **workspace slug**. App and API-key login derive the slug from
284
283
  the authenticated organization; `--workspace` validates that the result matches:
285
284
 
286
285
  ```sh
287
- linear auth login --workspace acme # browser OAuth for "acme"
286
+ linear auth login --workspace acme # prompt for an API key for "acme"
288
287
  linear auth login --workspace other-org # …and another workspace
289
- linear auth login --workspace acme --key - # personal API-key fallback via stdin
288
+ linear auth login --workspace acme --key - # personal API key via stdin
290
289
  linear auth list # show configured workspaces + which is default
291
290
  linear auth default acme # choose the default workspace
292
291
  linear config set team ENG --user --workspace acme # set its validated default team
@@ -294,10 +293,49 @@ linear --workspace other-org issue list # use a specific workspace for one co
294
293
  linear auth logout --workspace acme # revoke OAuth, then remove one credential
295
294
  ```
296
295
 
297
- Successful browser, app, 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.
296
+ Successful app 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.
298
297
 
299
298
  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.
300
299
 
300
+ After a workspace URL slug changes, migrate its stored **API-key profile** with:
301
+
302
+ ```sh
303
+ linear auth rename old-workspace new-workspace --json
304
+ # Or include additional known project configs without editing them:
305
+ linear auth rename old-workspace new-workspace --project-config /path/to/another/.linear.toml
306
+ ```
307
+
308
+ The command validates the exact stored key against Linear's current organization/slug, preserving
309
+ the credential, storage type, profile team, user workspace/default, and the reference CLI's shared
310
+ keyring list/default. It ignores environment credentials and refuses credential flags. API-key
311
+ profiles do not contain a historical organization ID; the unchanged key proves its own organization,
312
+ which is returned in the result. Configured destinations and unlisted keyring entries are collisions,
313
+ even if they contain the same key. Nothing is revoked, exported, or placed on argv.
314
+ Keyring moves use an atomic create-only operation on macOS Keychain, so another client's
315
+ destination created during the rename cannot be replaced. The Linux `secret-tool` backend lacks
316
+ this operation and keyring-backed renames are refused before changes; file-only profiles remain
317
+ supported when no source keyring entry needs moving.
318
+
319
+ Project files are never edited. JSON reports `checkedProjectConfigs` and `projectConfigUpdates`
320
+ with exact paths and old/new workspace values; human output lists the same required edits. Discovery
321
+ covers the active project config plus explicit `--project-config` paths. Check other repositories
322
+ separately, update their `workspace` selectors, and update `LINEAR_WORKSPACE` overrides. The reference
323
+ CLI's global `linear.toml` selector, if affected, is reported in `globalConfigUpdates` for manual editing.
324
+
325
+ If storage fails or the process stops partway through, repeat **the same rename command**. A
326
+ non-secret `api_key_profile_rename` marker in the private user config reserves the destination and
327
+ records recovery progress. The source is kept until a verified destination and committed metadata
328
+ exist. Recovery checks for conflicting changes and resumes the remaining steps. Do not log out,
329
+ replace, or migrate either profile while recovery is pending. Fix the reported store/permission
330
+ problem, then retry; do not delete the recovery marker or print credential-store contents.
331
+
332
+ OAuth user and app profiles (including dormant keyring records) are refused before validation or
333
+ mutation. User sessions have rotating refresh tokens and require coordination with in-flight
334
+ refreshes; app profiles contain organization-bound client credentials, and token exchanges with
335
+ different scopes can revoke existing app tokens. This command does not refresh, exchange, revoke,
336
+ or rename either OAuth lifecycle. It does not require browser authorization. See Linear's
337
+ [OAuth lifecycle documentation](https://linear.app/developers/oauth-2-0-authentication).
338
+
301
339
  If revocation is intentionally unavailable, `auth logout --local-only` removes only local state.
302
340
  If browser login temporarily superseded an existing personal API-key profile for that workspace,
303
341
  OAuth logout preserves and reactivates the API key instead of deleting it.
@@ -484,6 +522,104 @@ linear cv results 01234567-89ab-cdef-0123-456789abcdef --json
484
522
  # [{"type":"issue","id":"…","identifier":"ENG-42","name":"Fix login","url":"…"}]
485
523
  ```
486
524
 
525
+ ### Releases
526
+
527
+ `release`, `release-pipeline`, `release-stage`, and `release-note` provide native release tracking.
528
+ Linear currently requires a [Business or Enterprise workspace](https://linear.app/docs/releases.md);
529
+ Business supports up to 15 pipelines. Schema visibility or an empty list does not establish
530
+ entitlement to create pipelines, or permission to change them. Linear enforces plan, role, team,
531
+ and credential permissions; the CLI preserves `feature_not_accessible` and `forbidden` errors.
532
+
533
+ These commands use the CLI's usual OAuth or personal API credentials and `--workspace` selection.
534
+ Pipeline **access keys** are separate credentials for the official
535
+ [`linear-release`](https://github.com/linear/linear-release) CI tool. Do not put a pipeline key in
536
+ `LINEAR_API_KEY` or `LINEAR_ACCESS_TOKEN`. This CLI does not consume `LINEAR_ACCESS_KEY`, create
537
+ pipeline keys, or invoke/install the official tool automatically.
538
+
539
+ ```sh
540
+ linear release-pipeline list --json
541
+ linear release-pipeline view production --json # UUID or API slug; not display name
542
+ linear release-stage list production --all --json
543
+ linear release list --pipeline production --stage-type started --json
544
+ linear release view RELEASE_ID --json # release UUID or slug
545
+ linear release issues RELEASE_ID --all --json
546
+ linear release links RELEASE_ID --json
547
+ ```
548
+
549
+ Create a pipeline explicitly as `scheduled` or `continuous`. Creation includes Linear's default
550
+ stages. Add further `started` stages for verification/observation, or rename/reorder the defaults:
551
+
552
+ ```sh
553
+ linear release-pipeline create --name Production --type scheduled --json
554
+ linear release-pipeline update PIPELINE_ID --rollover-issues false --json
555
+ linear release-stage create --pipeline PIPELINE_ID --name Observe --type started \
556
+ --color '#5e6ad2' --position 3 --frozen true --json
557
+ linear release-stage update STAGE_ID --frozen false --json
558
+ ```
559
+
560
+ Stage types are immutable. Only `started` stages can be frozen, and Linear requires at least one
561
+ unfrozen started stage. Freezing excludes releases in that stage from automated sync; it is not a
562
+ deployment approval or a lock on manual membership edits. Pipeline updates support explicit
563
+ `true|false` for production, automatic notes, and issue rollover. Repeated `--include-path` and
564
+ `--team-id` replace the corresponding collections; `--clear-paths` and `--clear-teams` clear them.
565
+ Team associations use UUIDs, not the global default `--team`.
566
+
567
+ For a scheduled rollout, create the release with a full commit SHA, explicit version and stage,
568
+ then add the exact issue scope. `release create` otherwise defaults to a started stage on scheduled
569
+ pipelines and a completed stage on continuous pipelines:
570
+
571
+ ```sh
572
+ linear release create --pipeline PIPELINE_ID --name 'Release 1.2.3' \
573
+ --release-version 1.2.3 --commit-sha "$approved_sha" --stage PLANNED_STAGE_ID \
574
+ --target-date 2026-10-30 --description-file release-plan.md --json
575
+ linear release add-issue RELEASE_ID ENG-42 --json
576
+ linear release update RELEASE_ID --stage VERIFY_STAGE_ID --json
577
+ linear release-note create --pipeline PIPELINE_ID --release RELEASE_ID \
578
+ --title 'Verification' --body-file verification.md --json
579
+ ```
580
+
581
+ `release update <id>` edits that exact release, including SHA/version/dates or a stage UUID from
582
+ the same pipeline. `release update-version`, `sync`, and `complete` require **both** `--pipeline`
583
+ and `--release-version`; they never implicitly pick the latest release. `update-version` accepts a
584
+ stage **name**, matched by Linear, and attaches links without changing the version:
585
+
586
+ ```sh
587
+ linear release update-version --pipeline PIPELINE_ID --release-version 1.2.3 \
588
+ --stage Observe --link "CI=https://ci.example/run/123" --json
589
+ # Only after the external deployment, verification and observation gates pass:
590
+ linear release complete --pipeline PIPELINE_ID --release-version 1.2.3 \
591
+ --link "GitHub=https://github.com/acme/app/releases/tag/v1.2.3" --json
592
+ ```
593
+
594
+ Completion records Linear lifecycle state; it does not deploy, enforce service ordering, check
595
+ approvals, verify a running revision, or wait for observation. Completing a scheduled release may
596
+ move its open issues into the next release (creating one if needed), depending on pipeline settings.
597
+ When `complete --commit-sha` targets a version, Linear preserves an existing stored SHA. Set and
598
+ verify the intended SHA with `create`/`update` first. Stage transitions through `update` also trigger
599
+ Linear's lifecycle behavior, including rollover on completion.
600
+
601
+ For explicit, already-attributed data, `release sync --pipeline PIPELINE_ID --release-version 1.2.3
602
+ --commit-sha FULL_SHA --issue ENG-42 --issue ENG-43=OTHER_FULL_SHA` calls Linear's native sync API.
603
+ It does not scan Git. References accumulate; omitted issues are not removed. `--reverted-issue`
604
+ removes reverted attribution, and `release remove-issue RELEASE_ID ENG-42 --yes` removes exact
605
+ membership. Scheduled sync can assign the version to an unversioned started release or create a
606
+ release; continuous sync creates/completes a release immediately. Use sync at the appropriate point
607
+ in your deployment process, and read the returned release ID, version, SHA and stage.
608
+
609
+ Use the official `linear-release` when you need commit-range scanning, path/subject filters,
610
+ revert detection, branch/PR attribution, and pipeline-key CI authentication. It can work alongside
611
+ this CLI against the same pipeline; pass the same explicit release version. Native commands are
612
+ useful for preparation, exact membership, management, inspection, and subsequent lifecycle steps.
613
+ No Git attribution implementation or additional runtime dependency is duplicated here.
614
+
615
+ Release notes can cover repeated `--release <id>` values, or an inclusive **createdAt** range using
616
+ both `--from-release` and `--to-release`. These shapes are mutually exclusive and all releases must
617
+ belong to the note's pipeline. `release-note update` changes title, Markdown or scope; delete needs
618
+ confirmation (`--yes` noninteractively). Use `--body-file -` or `--description-file -` for stdin.
619
+ List commands emit bare arrays, views and mutations emit objects, and errors remain JSON on stderr.
620
+ `linear commands release sync --json` exposes options and output fields without credentials.
621
+ Archive/trash operations and other advanced fields remain available through `linear api`.
622
+
487
623
  ## Command overview
488
624
 
489
625
  Every group has `--help` with full options and (for the busy ones) an Examples section. Aliases
@@ -498,6 +634,10 @@ are shown in parentheses. For a machine-readable tree of _every_ command, run
498
634
  | **`project-update`** (`pu`) | `create` · `list` (project status updates, with `--health`) |
499
635
  | **`milestone`** (`m`) | `list` · `view` · `create` · `update` · `delete` |
500
636
  | **`cycle`** (`c`) | `list` · `view` · `current` · `create` · `update` |
637
+ | **`release`** | `list` · `view` · `create` · `update` · `sync` · `update-version` · `complete` · `issues` · `links` · `add-issue` · `remove-issue` |
638
+ | **`release-pipeline`** | `list` · `view` · `create` · `update` |
639
+ | **`release-stage`** | `list` · `view` · `create` · `update` |
640
+ | **`release-note`** | `list` · `view` · `create` · `update` · `delete` |
501
641
  | **`user`** (`u`) | `list` · `view` · `me` |
502
642
  | **`label`** (`lb`) | `list` · `create` · `update` · `delete` |
503
643
  | **`state`** (`st`) | `list` · `view` (workflow states) |
@@ -556,6 +696,11 @@ resolver semantics are designed, rather than acquiring a silent partial implemen
556
696
  | `attachment list` | `Issue.attachments` | yes | not exposed | not exposed |
557
697
  | `comment list`, `issue comment list`, `issue comments` | `Issue.comments` | yes | not exposed | not exposed |
558
698
  | `cycle list`, `team cycles` | `Team.cycles` | yes | not exposed | not exposed |
699
+ | `release list` | `Query.releases` | yes | `--include-archived` | `archivedAt` |
700
+ | `release-pipeline list` | `Query.releasePipelines` | yes | `--include-archived` | `archivedAt` |
701
+ | `release-stage list` | `ReleasePipeline.stages` | yes | `--include-archived` | `archivedAt` |
702
+ | `release-note list` | `Query.releaseNotes` | yes | `--include-archived` | `archivedAt` |
703
+ | `release issues`, `release links` | `Release.issues/links` | yes | not exposed | not exposed |
559
704
  | `custom-view list` | `Query.customViews` | yes | not exposed | `archivedAt` |
560
705
  | `custom-view results` | `CustomView.issues/projects/initiatives` | yes | not exposed | not exposed |
561
706
  | `document list` | `Query.documents` | yes | not exposed | not exposed |
@@ -772,10 +917,10 @@ profile slug where applicable:
772
917
  only, so a migrating user's defaults carry over
773
918
  7. no default team
774
919
 
775
- `workspace` and `sort` keep their existing flag → environment → project → user → legacy-global
920
+ `workspace`, `project`, and `sort` use flag → environment → project → user → legacy-global
776
921
  order. Workspace credential selection is independent and follows the order described above.
777
922
 
778
- Keys: `team` (or `team_id`), `workspace`, and `sort` (or `issue_sort`). You can write these from
923
+ Keys: `team` (or `team_id`), `workspace`, `project`, and `sort` (or `issue_sort`). You can write these from
779
924
  the CLI. Project and top-level setting edits preserve comments and layout. Profile updates preserve
780
925
  all TOML data and use the same locked, atomic rewrite as credential updates:
781
926
 
@@ -783,11 +928,26 @@ all TOML data and use the same locked, atomic rewrite as credential updates:
783
928
  linear config init # pick a team from a list → <git root>/.linear.toml
784
929
  linear config init --team TES # …or say which (scripts); --sort, --path, --force
785
930
  linear config set sort updated # edit one key in the project config in effect
931
+ linear config set project "My Project" # default Linear project for new issues (name or UUID)
786
932
  linear config set team ENG --user # legacy top-level user fallback (backward compatible)
787
933
  linear config set team ENG --user --workspace acme # validated default for the acme profile
788
934
  linear config # show the result, with each value's source
789
935
  ```
790
936
 
937
+ To associate a particular folder and its descendants with a Linear project, run
938
+ `linear config set project "My Project" --path .linear.toml` inside that folder. Without `--path`,
939
+ `config set` edits the discovered config, or creates one at the Git root (cwd outside Git).
940
+ Keep `team` and `workspace` in the same file when needed: discovery reads the nearest config
941
+ file, without merging ancestor files.
942
+
943
+ `issue create` (also `issue new`) uses this project automatically. `--project <name|id>` overrides
944
+ it; sub-issues inherit their parent's project ahead of the configured default. `--no-project`
945
+ creates outside any project, including parent and template defaults. `LINEAR_PROJECT` overrides
946
+ the configured value for an invocation. A configured project is resolved before creation, so
947
+ an unknown or ambiguous name fails without creating an issue; UUIDs avoid name ambiguity.
948
+ This default applies to issue creation; list, search, and update commands keep their explicit
949
+ project selection. Remove the `project` line from the config to clear the association.
950
+
791
951
  `config set` will not write `api_key` or anything else that belongs to the credential store — that
792
952
  is what `auth login` is for. A profile team is validated against the selected workspace whenever a
793
953
  credential is available. Login, adopt, OAuth refresh/fallback, and credential migration preserve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linear-sdk-cli",
3
- "version": "0.8.3",
3
+ "version": "0.9.1",
4
4
  "description": "Ergonomic command-line interface for Linear, built on @linear/sdk",
5
5
  "type": "module",
6
6
  "license": "MIT",