linear-sdk-cli 0.2.4 → 0.3.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 +36 -16
- package/MIGRATING.md +7 -6
- package/README.md +120 -31
- package/package.json +5 -3
- package/skills/linear-sdk-cli/SKILL.md +43 -8
- package/skills/linear-sdk-cli/references/api.md +1 -1
- package/skills/linear-sdk-cli/references/attachment.md +1 -1
- package/skills/linear-sdk-cli/references/auth.md +26 -7
- package/skills/linear-sdk-cli/references/commands.md +1 -1
- package/skills/linear-sdk-cli/references/comment.md +1 -1
- package/skills/linear-sdk-cli/references/completion.md +1 -1
- package/skills/linear-sdk-cli/references/config.md +5 -1
- package/skills/linear-sdk-cli/references/cycle.md +1 -1
- package/skills/linear-sdk-cli/references/document.md +1 -1
- package/skills/linear-sdk-cli/references/favorite.md +1 -1
- package/skills/linear-sdk-cli/references/initiative-update.md +1 -1
- package/skills/linear-sdk-cli/references/initiative.md +1 -1
- package/skills/linear-sdk-cli/references/issue.md +1 -1
- package/skills/linear-sdk-cli/references/label.md +1 -1
- package/skills/linear-sdk-cli/references/milestone.md +1 -1
- package/skills/linear-sdk-cli/references/notification.md +1 -1
- package/skills/linear-sdk-cli/references/open.md +1 -1
- package/skills/linear-sdk-cli/references/organization.md +1 -1
- package/skills/linear-sdk-cli/references/project-update.md +1 -1
- package/skills/linear-sdk-cli/references/project.md +1 -1
- package/skills/linear-sdk-cli/references/roadmap.md +1 -1
- package/skills/linear-sdk-cli/references/schema.md +1 -1
- package/skills/linear-sdk-cli/references/state.md +1 -1
- package/skills/linear-sdk-cli/references/team.md +1 -1
- package/skills/linear-sdk-cli/references/user.md +1 -1
- package/skills/linear-sdk-cli/references/webhook.md +1 -1
- package/skills/linear-sdk-cli/references/whoami.md +1 -1
- package/skills/linear-sdk-cli/scripts/generate-docs.ts +3 -1
- package/src/client.ts +57 -2
- package/src/commands/meta.ts +240 -65
- package/src/config.ts +319 -16
- package/src/context.ts +2 -0
- package/src/index.ts +19 -0
- package/src/lib/introspect.ts +1 -0
- package/src/lib/options.ts +1 -0
- package/src/lib/output-shapes.ts +21 -2
- package/src/oauth.ts +582 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
3
|
+
Release Please generates this file from Conventional Commits. Feature and fix pull requests do
|
|
4
|
+
not edit it directly; their squash-commit titles are the release-note source of truth. This project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.3.1](https://github.com/zhendalf/linear-sdk-cli/compare/v0.3.0...v0.3.1) (2026-08-28)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **release:** prevent changelog drift ([e8019a0](https://github.com/zhendalf/linear-sdk-cli/commit/e8019a0cc59182579237f9ec83138259fcd0615b))
|
|
12
|
+
|
|
13
|
+
## [0.3.0](https://github.com/zhendalf/linear-sdk-cli/compare/v0.2.4...v0.3.0) (2026-08-28)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- **auth:** add browser OAuth login with PKCE (LIN-740) ([d5826e1](https://github.com/zhendalf/linear-sdk-cli/commit/d5826e1ebdc9015e876faa29b004f4953a190b43))
|
|
18
|
+
- **auth:** support OAuth access tokens ([#28](https://github.com/zhendalf/linear-sdk-cli/issues/28)) ([4fecb6c](https://github.com/zhendalf/linear-sdk-cli/commit/4fecb6c1a40533804ff2fde5ace689d39c70d4bc))
|
|
6
19
|
|
|
7
20
|
## [0.2.4](https://github.com/zhendalf/linear-sdk-cli/compare/v0.2.3...v0.2.4) (2026-08-24)
|
|
8
21
|
|
|
@@ -33,27 +46,39 @@ All notable changes to this project are documented here. The format is based on
|
|
|
33
46
|
### Features
|
|
34
47
|
|
|
35
48
|
- expand CLI workflows and terminal UX ([0fe31cc](https://github.com/zhendalf/linear-sdk-cli/commit/0fe31cca7e71d0fa6eee2b7c1461ecaf0a01103f))
|
|
49
|
+
- **comments:** add explicit, notification-capable mentions with repeatable `--mention`
|
|
50
|
+
([TES-738](https://linear.app/issue/TES-738)). Literal `@name` text remains Markdown; comment
|
|
51
|
+
commands resolve users exactly and prepend real Linear mention tokens.
|
|
36
52
|
|
|
37
53
|
### Bug Fixes
|
|
38
54
|
|
|
39
55
|
- **ci:** give release dispatch repository context ([#15](https://github.com/zhendalf/linear-sdk-cli/issues/15)) ([e3f51e6](https://github.com/zhendalf/linear-sdk-cli/commit/e3f51e600102416f3bd3212578b364a13a452e49))
|
|
40
56
|
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Replace ESLint with Oxlint while preserving the existing JavaScript and TypeScript checks.
|
|
60
|
+
- Use TypeScript 7 for type-checking without source or configuration compatibility changes.
|
|
61
|
+
|
|
41
62
|
## [0.1.3](https://github.com/zhendalf/linear-sdk-cli/compare/v0.1.2...v0.1.3) (2026-08-24)
|
|
42
63
|
|
|
43
64
|
### Bug Fixes
|
|
44
65
|
|
|
45
66
|
- **ci:** grant OIDC to automated publisher ([#7](https://github.com/zhendalf/linear-sdk-cli/issues/7)) ([acc6e4b](https://github.com/zhendalf/linear-sdk-cli/commit/acc6e4b9c4ab485ce39eeec7eb2c835892016e59))
|
|
46
67
|
|
|
47
|
-
## [
|
|
68
|
+
## [0.1.2](https://github.com/zhendalf/linear-sdk-cli/compare/v0.1.1...v0.1.2) (2026-08-24)
|
|
69
|
+
|
|
70
|
+
### Dependencies
|
|
71
|
+
|
|
72
|
+
- Upgrade the runtime and development dependencies ([#2](https://github.com/zhendalf/linear-sdk-cli/issues/2)).
|
|
73
|
+
|
|
74
|
+
### Continuous Integration
|
|
75
|
+
|
|
76
|
+
- Run verification on pull requests using the latest Bun release ([#3](https://github.com/zhendalf/linear-sdk-cli/issues/3)).
|
|
77
|
+
|
|
78
|
+
## [0.1.1](https://github.com/zhendalf/linear-sdk-cli/releases/tag/v0.1.1) (2026-08-24)
|
|
48
79
|
|
|
49
80
|
### Added
|
|
50
81
|
|
|
51
|
-
- **Explicit, notification-capable comment mentions (`--mention`, TES-738).** Ordinary `@name`
|
|
52
|
-
text remains literal Markdown. Comment-writing commands now accept repeatable
|
|
53
|
-
`--mention <name|email|me|id>`, resolve each user exactly, deduplicate repeats, and prepend real
|
|
54
|
-
Linear mention tokens as their own paragraph. The option is available on `issue comment`,
|
|
55
|
-
`comment add`, `comment reply`, and `comment update`, including the shared
|
|
56
|
-
`issue comment add/update` mounts.
|
|
57
82
|
- **Every command declares its `--json` output, and `linear commands <path>` prints it (TES-610).**
|
|
58
83
|
An agent driving the CLI cold guessed key names and filed false bugs (`.user` read as `.author`,
|
|
59
84
|
`.parent` as `.parentId`); the field names were knowable only from the source. Each node of
|
|
@@ -326,11 +351,6 @@ true` is sent unless `--no-default-template` (also accepted under the reference'
|
|
|
326
351
|
|
|
327
352
|
### Changed
|
|
328
353
|
|
|
329
|
-
- **Development linting now uses Oxlint instead of ESLint.** The migrated configuration preserves
|
|
330
|
-
the existing recommended JavaScript and TypeScript checks and underscore-prefixed unused-variable
|
|
331
|
-
exceptions while reducing the lint dependency tree and execution time.
|
|
332
|
-
- **TypeScript 7 now powers type-checking.** The native compiler replaces TypeScript 6 without
|
|
333
|
-
source or configuration compatibility changes in this project.
|
|
334
354
|
- **`--fields`, `--limit` and `--all` are refused on commands that never read them** (TES-637 (2),
|
|
335
355
|
TES-596). All twelve globals are registered on every command so they can sit anywhere on the
|
|
336
356
|
line, but `--fields` projects a _rendered_ result (a table or a detail block) and `--limit`/`--all`
|
|
@@ -835,7 +855,7 @@ column 36`), never an excerpt from a credential-bearing file, and strip control
|
|
|
835
855
|
- **`roadmap delete` alias is now `rm`** (was `del`), matching the `rm` delete alias used by
|
|
836
856
|
every other group.
|
|
837
857
|
|
|
838
|
-
## [0.1.0]
|
|
858
|
+
## [0.1.0] (2026-06-26)
|
|
839
859
|
|
|
840
860
|
Initial release. An ergonomic CLI for Linear built on `@linear/sdk`, with human-readable output
|
|
841
861
|
by default and a stable `--json` mode for scripts and agents.
|
package/MIGRATING.md
CHANGED
|
@@ -47,12 +47,13 @@ linear auth list # …lists it, Storage: keychain
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
If it does not — no keyring on this platform, or you used schpet's plaintext mode (`slug =
|
|
50
|
-
"lin_api_…"` inline in `credentials.toml`, which we deliberately do not read) —
|
|
51
|
-
|
|
52
|
-
`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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.
|
|
54
|
+
Because the API-key entry is shared, `auth logout` here removes it for both tools (and drops the
|
|
55
|
+
slug from schpet's list file, so neither reports a workspace whose key is gone). The API key is
|
|
56
|
+
**never** read from a project `.linear.toml`.
|
|
56
57
|
|
|
57
58
|
## 3. Config — same files, same keys
|
|
58
59
|
|
package/README.md
CHANGED
|
@@ -103,51 +103,133 @@ branch, so most issue commands let you omit the id entirely.
|
|
|
103
103
|
|
|
104
104
|
## Authentication
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
For a person at a terminal, `linear auth login` uses browser Authorization Code + PKCE and the
|
|
107
|
+
default `actor=user`. Existing personal API keys and invocation-scoped OAuth access tokens remain
|
|
108
|
+
supported. Explicit `--api-key` or `--access-token` flags override environment credentials;
|
|
109
|
+
without a credential flag the CLI reads `LINEAR_API_KEY` or `LINEAR_ACCESS_TOKEN`. If both kinds
|
|
110
|
+
are supplied at the same precedence level, the CLI fails rather than silently choosing a Linear
|
|
111
|
+
actor.
|
|
112
|
+
|
|
113
|
+
> **Credential trust boundary.** A secret is **never** read from a project-local
|
|
111
114
|
> `.linear.toml` — only non-secret settings live there — so a key can't be committed by accident.
|
|
112
115
|
> A project may select one of your already-stored credentials with `workspace = "<slug>"`, but it
|
|
113
|
-
> cannot provide or override the secret.
|
|
116
|
+
> cannot provide or override the secret. Browser OAuth credentials are keyring-only; an access
|
|
117
|
+
> token injected with `--access-token` or `LINEAR_ACCESS_TOKEN` remains invocation-scoped.
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
linear auth login # browser OAuth, read + write, actor=user
|
|
121
|
+
linear auth login --read-only # request only Linear's read scope
|
|
122
|
+
linear auth login --no-browser # print the URL; still waits on the loopback callback
|
|
123
|
+
printf '%s\n' "$LINEAR_API_KEY" | linear auth login --key - # personal API-key fallback
|
|
124
|
+
linear auth status # credential kind and source (value redacted)
|
|
125
|
+
linear auth logout --workspace acme # revoke OAuth (if used) and remove the profile
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Browser login generates a cryptographically random verifier and CSRF state, requires PKCE S256,
|
|
129
|
+
opens Linear, and listens temporarily on the registered `127.0.0.1` callback. It validates the
|
|
130
|
+
viewer and workspace before saving the access token, rotating refresh token, expiry, granted
|
|
131
|
+
scopes, OAuth client identity, and workspace identity. The whole record lives only in the system
|
|
132
|
+
keyring (`service = linear-cli`, `account = oauth:<workspace slug>`); the config file gets only
|
|
133
|
+
non-secret `keyring = true` and `oauth = true` markers. Access tokens refresh before expiry and
|
|
134
|
+
once after an authentication failure, with refresh-token rotation committed under the credential
|
|
135
|
+
store lock. `--admin` explicitly adds the admin scope; it is never implicit.
|
|
136
|
+
|
|
137
|
+
`--no-browser` is useful over SSH only when the machine running the CLI is also reachable as the
|
|
138
|
+
browser's loopback host; Linear does not currently document a device-code flow. Override a
|
|
139
|
+
separately registered public client's identity with `LINEAR_OAUTH_CLIENT_ID` and its exact callback
|
|
140
|
+
with `LINEAR_OAUTH_REDIRECT_URI`. No OAuth client secret is embedded, accepted, or stored.
|
|
141
|
+
|
|
142
|
+
For a personal API key, `auth login --key -` reads stdin, validates the viewer/workspace, stores the
|
|
143
|
+
key in the system keyring by default, and writes only a marker to the config. `--plaintext` is an
|
|
144
|
+
API-key-only compatibility option. API-key entries use `account = <workspace slug>`, matching
|
|
145
|
+
[schpet/linear-cli](https://github.com/schpet/linear-cli). Passing a key as `--key <value>` works but
|
|
146
|
+
warns because argv is visible to other processes. `auth migrate` moves legacy plaintext keys into
|
|
147
|
+
the keyring, and `auth token` exports stored API keys only; OAuth tokens are never printed.
|
|
148
|
+
|
|
149
|
+
### Apps, agents, and service accounts
|
|
150
|
+
|
|
151
|
+
Use an OAuth access token when the CLI runs inside an integration or agent:
|
|
114
152
|
|
|
115
153
|
```sh
|
|
116
|
-
linear
|
|
117
|
-
linear
|
|
118
|
-
linear auth status # where the key came from (value redacted)
|
|
119
|
-
linear auth migrate # move plaintext keys from the file into the keyring
|
|
120
|
-
linear auth token # print the resolved key (for scripting)
|
|
154
|
+
LINEAR_ACCESS_TOKEN="$access_token" linear whoami --json
|
|
155
|
+
LINEAR_ACCESS_TOKEN="$access_token" linear issue list --json
|
|
121
156
|
```
|
|
122
157
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
The application host owns OAuth installation, refresh-token or client-credentials exchange,
|
|
159
|
+
client-secret storage, and webhook processing; it injects only the current access token into each
|
|
160
|
+
CLI invocation. The CLI passes it to `LinearClient({ accessToken })`, which sends the required
|
|
161
|
+
Bearer authorization. Prefer the environment variable because a command-line token is visible to
|
|
162
|
+
other processes and shell history.
|
|
163
|
+
|
|
164
|
+
For a long-lived, single-workspace host, enable client credentials on the Linear OAuth app and keep
|
|
165
|
+
its client ID and secret in the host's secret manager. The package exports an expiry-aware provider
|
|
166
|
+
that caches Linear's 30-day app-actor token in memory, coalesces concurrent exchanges, renews five
|
|
167
|
+
minutes before expiry, and can be forced to renew after a `401`:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { ClientCredentialsTokenProvider } from "linear-sdk-cli";
|
|
171
|
+
|
|
172
|
+
const { LINEAR_CLIENT_ID: clientId, LINEAR_CLIENT_SECRET: clientSecret, ...childEnv } = process.env;
|
|
173
|
+
if (!clientId || !clientSecret) throw new Error("Linear OAuth credentials are missing");
|
|
174
|
+
|
|
175
|
+
const tokens = new ClientCredentialsTokenProvider({
|
|
176
|
+
clientId,
|
|
177
|
+
clientSecret,
|
|
178
|
+
scopes: ["read", "write"],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const { accessToken } = await tokens.getAccessToken();
|
|
182
|
+
const child = Bun.spawn(["linear", "issue", "list", "--json"], {
|
|
183
|
+
env: { ...childEnv, LINEAR_ACCESS_TOKEN: accessToken },
|
|
184
|
+
});
|
|
185
|
+
await child.exited;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Call `tokens.invalidate()` (or `getAccessToken({ forceRefresh: true })`) before one bounded retry
|
|
189
|
+
when Linear rejects the token with `401`. Client-credentials tokens do not have refresh tokens;
|
|
190
|
+
renewal is another authenticated exchange. A short-lived/serverless process should use a secure
|
|
191
|
+
shared token cache or central broker rather than minting a new 30-day token on every invocation.
|
|
192
|
+
|
|
193
|
+
For an app installed into multiple workspaces, use Authorization Code with `actor=app` instead and
|
|
194
|
+
store each installation's rotating refresh token encrypted in the host. That hosted app lifecycle
|
|
195
|
+
remains distinct from this CLI's browser PKCE login for a human `actor=user`.
|
|
196
|
+
|
|
197
|
+
For a distinct app identity, install the OAuth application with `actor=app`. Native Linear agents
|
|
198
|
+
can additionally request `app:mentionable` and `app:assignable` and subscribe to Agent Session
|
|
199
|
+
webhooks. App actors cannot request the `admin` scope. See Linear's
|
|
200
|
+
[OAuth guide](https://linear.app/developers/oauth-2-0-authentication),
|
|
201
|
+
[app-actor authorization](https://linear.app/developers/oauth-actor-authorization), and
|
|
202
|
+
[agent guide](https://linear.app/developers/agents).
|
|
203
|
+
|
|
204
|
+
Hosted app tokens supplied through `LINEAR_ACCESS_TOKEN` remain invocation-scoped. The only OAuth
|
|
205
|
+
tokens persisted by the CLI are human browser-login sessions, stored as an isolated per-workspace
|
|
206
|
+
keyring record; the CLI never persists or uses an OAuth client secret.
|
|
131
207
|
|
|
132
208
|
### Multiple workspaces
|
|
133
209
|
|
|
134
|
-
Credentials are stored per **workspace slug**.
|
|
135
|
-
|
|
210
|
+
Credentials are stored per **workspace slug**. Browser and API-key login both derive the slug from
|
|
211
|
+
the authenticated organization; `--workspace` validates that the result matches:
|
|
136
212
|
|
|
137
213
|
```sh
|
|
138
|
-
linear auth login --workspace acme #
|
|
139
|
-
linear auth login --workspace other-org # …and another
|
|
214
|
+
linear auth login --workspace acme # browser OAuth for "acme"
|
|
215
|
+
linear auth login --workspace other-org # …and another workspace
|
|
216
|
+
linear auth login --workspace acme --key - # personal API-key fallback via stdin
|
|
140
217
|
linear auth list # show configured workspaces + which is default
|
|
141
218
|
linear auth default acme # choose the default workspace
|
|
142
219
|
linear --workspace other-org issue list # use a specific workspace for one command
|
|
143
|
-
linear auth logout --workspace acme # remove one credential
|
|
220
|
+
linear auth logout --workspace acme # revoke OAuth, then remove one credential
|
|
144
221
|
```
|
|
145
222
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
223
|
+
If revocation is intentionally unavailable, `auth logout --local-only` removes only local state.
|
|
224
|
+
If browser login temporarily superseded an existing personal API-key profile for that workspace,
|
|
225
|
+
OAuth logout preserves and reactivates the API key instead of deleting it.
|
|
226
|
+
|
|
227
|
+
**Selection precedence** (strict): an explicit `--api-key` or `--access-token` flag bypasses the
|
|
228
|
+
environment and workspace selection entirely. Otherwise `LINEAR_API_KEY` or
|
|
229
|
+
`LINEAR_ACCESS_TOKEN` is absolute. With no invocation-scoped credential, the workspace is chosen
|
|
230
|
+
by `--workspace` → `LINEAR_WORKSPACE` env → project config `workspace` → `default_workspace` in the
|
|
231
|
+
user config. With one configured workspace it's used automatically; with several and no selection,
|
|
232
|
+
the CLI asks you to pick (via `--workspace`, project config, or `auth default`).
|
|
151
233
|
|
|
152
234
|
## Core concepts
|
|
153
235
|
|
|
@@ -475,8 +557,9 @@ keyring = true # secret lives in the OS keyring (service linear-
|
|
|
475
557
|
api_key = "lin_api_yyyyyyyy" # …or, with `auth login --plaintext`, in this 0600 file
|
|
476
558
|
```
|
|
477
559
|
|
|
478
|
-
Relevant environment variables: **`LINEAR_API_KEY`** (absolute —
|
|
479
|
-
**`LINEAR_WORKSPACE`** (selects a stored credential when
|
|
560
|
+
Relevant environment variables: **`LINEAR_API_KEY`** or **`LINEAR_ACCESS_TOKEN`** (absolute —
|
|
561
|
+
bypasses workspace selection) and **`LINEAR_WORKSPACE`** (selects a stored API-key credential when
|
|
562
|
+
no invocation-scoped credential is given).
|
|
480
563
|
|
|
481
564
|
### Shell completion
|
|
482
565
|
|
|
@@ -524,6 +607,7 @@ await createProgram().parseAsync(["node", "linear", "issue", "list", "--json"]);
|
|
|
524
607
|
bun run verify # typecheck + lint + unit/contract tests
|
|
525
608
|
bun run test:live # live integration tests (needs LINEAR_API_KEY + LINEAR_CLI_LIVE=1)
|
|
526
609
|
bun run test:live:admin # also runs admin-tier suites (e.g. team create/update)
|
|
610
|
+
bun run audit:changelog # verify package, tag, and generated release-note history
|
|
527
611
|
bun run audit:coverage # regenerate COVERAGE.md (add --update to re-baseline the snapshot)
|
|
528
612
|
bun run janitor # sweep leaked `clitest-` fixtures from the test workspace
|
|
529
613
|
```
|
|
@@ -532,6 +616,11 @@ Architecture is three layers — **commands** (commander wiring) → **services*
|
|
|
532
616
|
resource, the only place that touches the SDK) → **`@linear/sdk`**. The machine JSON envelope is
|
|
533
617
|
locked and contract-tested so output never silently drifts.
|
|
534
618
|
|
|
619
|
+
Release Please owns `CHANGELOG.md`. Pull requests use Conventional Commit titles and are squash
|
|
620
|
+
merged, giving each change one release-note entry. Do not edit the changelog in a feature or fix
|
|
621
|
+
pull request; use a `BEGIN_COMMIT_OVERRIDE` block in the pull request body when a change needs
|
|
622
|
+
several or specially worded release notes.
|
|
623
|
+
|
|
535
624
|
> Live integration tests run against a real workspace and share one API key; running the entire
|
|
536
625
|
> suite repeatedly can hit Linear's rate limit. Run a subset (a few files at a time) or re-run
|
|
537
626
|
> after a short pause if you see transient rate-limit failures.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linear-sdk-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Ergonomic command-line interface for Linear, built on @linear/sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,8 +61,10 @@
|
|
|
61
61
|
"test:contract": "bun test test/contract",
|
|
62
62
|
"test:live": "LINEAR_CLI_LIVE=1 bun test --timeout 60000 test/integration",
|
|
63
63
|
"test:live:admin": "LINEAR_CLI_LIVE=1 LINEAR_CLI_LIVE_ADMIN=1 bun test --timeout 60000 test/integration",
|
|
64
|
-
"verify": "bun run typecheck && bun run lint && bun run audit:coverage && bun run format:check && bun run test",
|
|
64
|
+
"verify": "bun run typecheck && bun run lint && bun run audit:changelog && bun run audit:coverage && bun run format:check && bun run test",
|
|
65
65
|
"skill:docs": "bun run skills/linear-sdk-cli/scripts/generate-docs.ts",
|
|
66
|
+
"audit:changelog": "bun run scripts/changelog-audit.ts",
|
|
67
|
+
"audit:pr-title": "bun run scripts/pr-title-audit.ts",
|
|
66
68
|
"audit:coverage": "bun run scripts/coverage-audit.ts",
|
|
67
69
|
"janitor": "bun run test/janitor.ts"
|
|
68
70
|
},
|
|
@@ -77,7 +79,7 @@
|
|
|
77
79
|
"packageManager": "bun@1.4.0",
|
|
78
80
|
"devDependencies": {
|
|
79
81
|
"@types/bun": "^1.4.0",
|
|
80
|
-
"@types/node": "^26.
|
|
82
|
+
"@types/node": "^26.4.0",
|
|
81
83
|
"oxfmt": "^0.65.0",
|
|
82
84
|
"oxlint": "^1.80.0",
|
|
83
85
|
"typescript": "^7.0.2"
|
|
@@ -35,23 +35,57 @@ lin --version
|
|
|
35
35
|
|
|
36
36
|
### Authentication
|
|
37
37
|
|
|
38
|
-
The CLI
|
|
38
|
+
The CLI supports distinct human, hosted-app, and personal-key lifecycles:
|
|
39
39
|
|
|
40
|
-
1.
|
|
40
|
+
1. **Human browser OAuth (default)** via Authorization Code + PKCE S256, `actor=user`:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
linear auth login # opens Linear; read + write scopes
|
|
44
|
+
linear auth login --read-only # read scope only
|
|
45
|
+
linear auth login --no-browser # prints URL; loopback callback still required
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The CLI validates the viewer/workspace and stores the access token, rotating refresh token,
|
|
49
|
+
expiry, scopes, client identity, and workspace identity only in the OS keyring. It refreshes
|
|
50
|
+
before expiry and once after an authentication failure. `--admin` is explicit; no client secret
|
|
51
|
+
is embedded. `auth logout` revokes this grant before removing it (`--local-only` skips
|
|
52
|
+
revocation). Remote/headless callers need the loopback host reachable; Linear has no documented
|
|
53
|
+
device-code flow.
|
|
54
|
+
|
|
55
|
+
2. **Invocation OAuth access token** via `--access-token <token>` or `LINEAR_ACCESS_TOKEN` — use
|
|
56
|
+
this for hosted Linear apps, agents, and service accounts. Prefer the environment variable so
|
|
57
|
+
the secret does not appear in argv:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
LINEAR_ACCESS_TOKEN=... linear whoami --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The host application owns OAuth installation, token refresh/client-credentials exchange,
|
|
64
|
+
client-secret storage, and webhooks. These tokens remain invocation-scoped.
|
|
65
|
+
Long-lived hosts can import `ClientCredentialsTokenProvider` from `linear-sdk-cli`; it caches
|
|
66
|
+
the 30-day app token in memory, renews before expiry, coalesces concurrent exchanges, and
|
|
67
|
+
supports invalidation or forced renewal for one bounded retry after a `401`. Keep the client
|
|
68
|
+
secret in the host's secret manager and inject only `LINEAR_ACCESS_TOKEN` into CLI children.
|
|
69
|
+
Serverless hosts need a secure shared token cache or broker so cold starts do not mint a new
|
|
70
|
+
token for every command.
|
|
71
|
+
|
|
72
|
+
3. **`--api-key <key>`** flag (per invocation) or **`LINEAR_API_KEY`** env var — best
|
|
41
73
|
for CI and ephemeral agent runs:
|
|
42
74
|
|
|
43
75
|
```bash
|
|
44
76
|
LINEAR_API_KEY=lin_api_... linear whoami --json
|
|
45
77
|
```
|
|
46
78
|
|
|
47
|
-
|
|
79
|
+
4. **Stored personal API-key credentials** via the explicit compatibility path:
|
|
48
80
|
|
|
49
81
|
```bash
|
|
50
|
-
linear auth login --key
|
|
51
|
-
linear auth login # prompts for the key (interactive only)
|
|
82
|
+
printf '%s\n' "$LINEAR_API_KEY" | linear auth login --key -
|
|
52
83
|
```
|
|
53
84
|
|
|
54
|
-
|
|
85
|
+
Passing `--key <value>` also works but exposes it in argv and produces a warning. API keys may
|
|
86
|
+
use `--plaintext`; browser OAuth is always keyring-only.
|
|
87
|
+
|
|
88
|
+
5. **Multiple workspaces** — store several OAuth or API-key credentials and select one per call with
|
|
55
89
|
`--workspace <slug>`; set a default with `linear auth default <slug>`:
|
|
56
90
|
|
|
57
91
|
```bash
|
|
@@ -60,8 +94,9 @@ The CLI needs a Linear API key. Three ways to provide one, in order of precedenc
|
|
|
60
94
|
linear issue list --workspace acme --json
|
|
61
95
|
```
|
|
62
96
|
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
An explicit credential flag overrides environment credentials. If both credential kinds occur at
|
|
98
|
+
the same precedence level, the CLI fails rather than silently choosing an actor. Inspect resolution
|
|
99
|
+
with `linear auth status`. `linear auth token` exports stored API keys only and never OAuth tokens.
|
|
65
100
|
|
|
66
101
|
## Agent best practices
|
|
67
102
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Generated from `linear commands --json`. `linear api --help` (or `<subcommand> --help`) is authoritative._
|
|
6
6
|
|
|
7
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
8
|
|
|
9
9
|
### `linear api`
|
|
10
10
|
|
|
@@ -6,7 +6,7 @@ Group alias: `at`
|
|
|
6
6
|
|
|
7
7
|
_Generated from `linear commands --json`. `linear attachment --help` (or `<subcommand> --help`) is authoritative._
|
|
8
8
|
|
|
9
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
9
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
10
10
|
|
|
11
11
|
### `linear attachment`
|
|
12
12
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Generated from `linear commands --json`. `linear auth --help` (or `<subcommand> --help`) is authoritative._
|
|
6
6
|
|
|
7
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
8
|
|
|
9
9
|
### `linear auth`
|
|
10
10
|
|
|
@@ -63,29 +63,39 @@ linear auth list [options]
|
|
|
63
63
|
```text
|
|
64
64
|
slug: string
|
|
65
65
|
isDefault: boolean
|
|
66
|
+
credentialType: string
|
|
66
67
|
storage: string
|
|
67
68
|
```
|
|
68
69
|
|
|
69
70
|
### `linear auth login`
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
Authenticate in the browser with OAuth, or store a personal API key
|
|
72
73
|
|
|
73
74
|
```
|
|
74
75
|
linear auth login [options]
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
| Option
|
|
78
|
-
|
|
|
79
|
-
| `--key <key>`
|
|
80
|
-
| `--plaintext`
|
|
78
|
+
| Option | Description |
|
|
79
|
+
| ---------------------- | --------------------------------------------------------------------- |
|
|
80
|
+
| `--key <key>` | use a personal API key ('-' reads it from stdin) |
|
|
81
|
+
| `--plaintext` | Store the key in the config file (0600) instead of the system keyring |
|
|
82
|
+
| `--no-browser` | print the authorization URL instead of opening it |
|
|
83
|
+
| `--read-only` | request read-only OAuth access |
|
|
84
|
+
| `--admin` | explicitly add the OAuth admin scope |
|
|
85
|
+
| `--timeout <seconds>` | seconds to wait for the loopback callback |
|
|
86
|
+
| `--client-id <id>` | OAuth client ID (defaults to the packaged CLI app) |
|
|
87
|
+
| `--redirect-uri <uri>` | registered HTTP loopback callback URI |
|
|
81
88
|
|
|
82
89
|
**Output (`--json`)**: a receipt object
|
|
83
90
|
|
|
84
91
|
```text
|
|
85
92
|
success: boolean
|
|
93
|
+
credentialType: string
|
|
86
94
|
workspace: string
|
|
87
95
|
user: {id: string, name: string, email: string}
|
|
88
96
|
storage: string
|
|
97
|
+
scopes?: string[]
|
|
98
|
+
expiresAt?: string
|
|
89
99
|
path: string
|
|
90
100
|
```
|
|
91
101
|
|
|
@@ -97,12 +107,18 @@ Remove a stored workspace credential (select with --workspace <slug>)
|
|
|
97
107
|
linear auth logout [options]
|
|
98
108
|
```
|
|
99
109
|
|
|
110
|
+
| Option | Description |
|
|
111
|
+
| -------------- | ------------------------------------------------- |
|
|
112
|
+
| `--local-only` | remove local credentials without OAuth revocation |
|
|
113
|
+
|
|
100
114
|
**Output (`--json`)**: a receipt object
|
|
101
115
|
|
|
102
116
|
```text
|
|
103
117
|
success: boolean
|
|
104
118
|
workspace: string
|
|
105
119
|
removed: boolean
|
|
120
|
+
revocation: string
|
|
121
|
+
fallbackCredentialType: string | null
|
|
106
122
|
```
|
|
107
123
|
|
|
108
124
|
### `linear auth migrate`
|
|
@@ -123,7 +139,7 @@ path: string
|
|
|
123
139
|
|
|
124
140
|
### `linear auth status`
|
|
125
141
|
|
|
126
|
-
Show where the
|
|
142
|
+
Show where the active credential is resolved from (value redacted)
|
|
127
143
|
|
|
128
144
|
```
|
|
129
145
|
linear auth status [options]
|
|
@@ -133,10 +149,13 @@ linear auth status [options]
|
|
|
133
149
|
|
|
134
150
|
```text
|
|
135
151
|
authenticated: boolean
|
|
152
|
+
credentialType: string | null
|
|
136
153
|
source: string
|
|
137
154
|
workspace: string | null
|
|
138
155
|
key: string
|
|
139
156
|
keyring: string | null
|
|
157
|
+
scopes: string[] | null
|
|
158
|
+
expiresAt: string | null
|
|
140
159
|
```
|
|
141
160
|
|
|
142
161
|
### `linear auth token`
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Generated from `linear commands --json`. `linear commands --help` (or `<subcommand> --help`) is authoritative._
|
|
6
6
|
|
|
7
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
8
|
|
|
9
9
|
### `linear commands`
|
|
10
10
|
|
|
@@ -6,7 +6,7 @@ Group alias: `cm`
|
|
|
6
6
|
|
|
7
7
|
_Generated from `linear commands --json`. `linear comment --help` (or `<subcommand> --help`) is authoritative._
|
|
8
8
|
|
|
9
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
9
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
10
10
|
|
|
11
11
|
### `linear comment`
|
|
12
12
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Generated from `linear commands --json`. `linear completion --help` (or `<subcommand> --help`) is authoritative._
|
|
6
6
|
|
|
7
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
8
|
|
|
9
9
|
### `linear completion`
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
_Generated from `linear commands --json`. `linear config --help` (or `<subcommand> --help`) is authoritative._
|
|
6
6
|
|
|
7
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
8
|
|
|
9
9
|
### `linear config`
|
|
10
10
|
|
|
@@ -19,6 +19,8 @@ linear config [options]
|
|
|
19
19
|
```text
|
|
20
20
|
apiKey: string
|
|
21
21
|
apiKeySource: string
|
|
22
|
+
accessToken: string
|
|
23
|
+
accessTokenSource: string
|
|
22
24
|
credentialWorkspace: string | null
|
|
23
25
|
team: string | null
|
|
24
26
|
workspace: string | null
|
|
@@ -88,6 +90,8 @@ linear config show [options]
|
|
|
88
90
|
```text
|
|
89
91
|
apiKey: string
|
|
90
92
|
apiKeySource: string
|
|
93
|
+
accessToken: string
|
|
94
|
+
accessTokenSource: string
|
|
91
95
|
credentialWorkspace: string | null
|
|
92
96
|
team: string | null
|
|
93
97
|
workspace: string | null
|
|
@@ -6,7 +6,7 @@ Group alias: `c`
|
|
|
6
6
|
|
|
7
7
|
_Generated from `linear commands --json`. `linear cycle --help` (or `<subcommand> --help`) is authoritative._
|
|
8
8
|
|
|
9
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
9
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
10
10
|
|
|
11
11
|
### `linear cycle`
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Group alias: `doc`
|
|
|
6
6
|
|
|
7
7
|
_Generated from `linear commands --json`. `linear document --help` (or `<subcommand> --help`) is authoritative._
|
|
8
8
|
|
|
9
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
9
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
10
10
|
|
|
11
11
|
### `linear document`
|
|
12
12
|
|
|
@@ -6,7 +6,7 @@ Group alias: `fav`
|
|
|
6
6
|
|
|
7
7
|
_Generated from `linear commands --json`. `linear favorite --help` (or `<subcommand> --help`) is authoritative._
|
|
8
8
|
|
|
9
|
-
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
9
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
10
10
|
|
|
11
11
|
### `linear favorite`
|
|
12
12
|
|