linear-sdk-cli 0.1.0 → 0.1.2
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 +46 -46
- package/COVERAGE.md +5 -5
- package/MIGRATING.md +69 -71
- package/README.md +88 -96
- package/package.json +8 -9
- package/skills/linear-sdk-cli/SKILL.md +3 -13
- package/skills/linear-sdk-cli/references/api.md +9 -9
- package/skills/linear-sdk-cli/references/auth.md +0 -18
- package/skills/linear-sdk-cli/references/commands.md +2 -2
- package/skills/linear-sdk-cli/references/config.md +11 -9
- package/skills/linear-sdk-cli/references/issue.md +12 -63
- package/skills/linear-sdk-cli/references/team.md +0 -16
- package/skills/linear-sdk-cli/scripts/generate-docs.ts +2 -6
- package/src/bin/linear.ts +6 -16
- package/src/cli.ts +1 -3
- package/src/commands/agent-session.ts +5 -13
- package/src/commands/api.ts +9 -15
- package/src/commands/attach.ts +4 -15
- package/src/commands/comment.ts +2 -6
- package/src/commands/completion.ts +6 -2
- package/src/commands/cycle.ts +1 -6
- package/src/commands/discover.ts +2 -6
- package/src/commands/document.ts +1 -4
- package/src/commands/initiative.ts +1 -5
- package/src/commands/issue.ts +88 -376
- package/src/commands/meta.ts +31 -139
- package/src/commands/milestone.ts +1 -4
- package/src/commands/notification.ts +6 -15
- package/src/commands/project.ts +4 -17
- package/src/commands/team.ts +0 -13
- package/src/commands/webhook.ts +2 -12
- package/src/config.ts +183 -500
- package/src/context.ts +4 -13
- package/src/git.ts +3 -28
- package/src/lib/body.ts +2 -26
- package/src/lib/errors.ts +7 -32
- package/src/lib/introspect.ts +7 -76
- package/src/lib/keyring.ts +9 -51
- package/src/lib/options.ts +12 -108
- package/src/lib/output-shapes.ts +9 -81
- package/src/lib/pagination.ts +17 -50
- package/src/lib/resolve.ts +7 -40
- package/src/lib/shape.ts +12 -16
- package/src/lib/upload.ts +1 -3
- package/src/output/format.ts +18 -96
- package/src/output/table.ts +7 -7
- package/src/services/agent-session.ts +3 -11
- package/src/services/attachment.ts +5 -14
- package/src/services/comment.ts +4 -12
- package/src/services/cycle.ts +4 -11
- package/src/services/document.ts +10 -29
- package/src/services/favorite.ts +9 -15
- package/src/services/initiative-update.ts +15 -16
- package/src/services/initiative.ts +10 -15
- package/src/services/issue.ts +18 -262
- package/src/services/label.ts +2 -12
- package/src/services/milestone.ts +5 -8
- package/src/services/notification.ts +2 -16
- package/src/services/organization.ts +18 -23
- package/src/services/project.ts +34 -55
- package/src/services/roadmap.ts +6 -7
- package/src/services/state.ts +3 -9
- package/src/services/team.ts +24 -67
- package/src/services/user.ts +2 -2
- package/src/services/webhook.ts +17 -28
- package/skills/linear-sdk-cli/references/open.md +0 -29
- package/src/commands/open.ts +0 -84
- package/src/lib/open.ts +0 -32
- package/src/output/color.ts +0 -52
- package/src/output/markdown.ts +0 -133
- package/src/output/pager.ts +0 -131
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linear-sdk-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Ergonomic command-line interface for Linear, built on @linear/sdk",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Eugene Beloded <belodedevgenij@gmail.com>",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "
|
|
10
|
+
"url": "https://github.com/zhendalf/linear-sdk-cli"
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://github.com/zhendalf/linear-sdk-cli#readme",
|
|
13
13
|
"bugs": {
|
|
@@ -55,31 +55,30 @@
|
|
|
55
55
|
"typecheck": "tsc --noEmit",
|
|
56
56
|
"lint": "eslint .",
|
|
57
57
|
"format": "prettier --write .",
|
|
58
|
-
"format:check": "prettier --check .",
|
|
59
58
|
"test": "bun test test/unit test/contract",
|
|
60
59
|
"test:unit": "bun test test/unit",
|
|
61
60
|
"test:contract": "bun test test/contract",
|
|
62
61
|
"test:live": "LINEAR_CLI_LIVE=1 bun test --timeout 60000 test/integration",
|
|
63
62
|
"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
|
|
63
|
+
"verify": "bun run typecheck && bun run lint && bun run test",
|
|
65
64
|
"skill:docs": "bun run skills/linear-sdk-cli/scripts/generate-docs.ts",
|
|
66
65
|
"audit:coverage": "bun run scripts/coverage-audit.ts",
|
|
67
66
|
"janitor": "bun run test/janitor.ts"
|
|
68
67
|
},
|
|
69
68
|
"dependencies": {
|
|
70
|
-
"@inquirer/prompts": "^8.
|
|
71
|
-
"@linear/sdk": "^
|
|
69
|
+
"@inquirer/prompts": "^8.6.0",
|
|
70
|
+
"@linear/sdk": "^91.0.0",
|
|
72
71
|
"commander": "^15.0.0",
|
|
73
72
|
"graphql": "^17.0.2",
|
|
74
73
|
"picocolors": "^1.1.1",
|
|
75
74
|
"smol-toml": "^1.8.0"
|
|
76
75
|
},
|
|
77
|
-
"packageManager": "bun@1.
|
|
76
|
+
"packageManager": "bun@1.4.0",
|
|
78
77
|
"devDependencies": {
|
|
79
78
|
"@eslint/js": "^10.0.1",
|
|
80
|
-
"@types/bun": "^1.
|
|
79
|
+
"@types/bun": "^1.4.0",
|
|
81
80
|
"@types/node": "^26.2.0",
|
|
82
|
-
"eslint": "^10.
|
|
81
|
+
"eslint": "^10.9.0",
|
|
83
82
|
"prettier": "^3.9.6",
|
|
84
83
|
"typescript": "^6.0.3",
|
|
85
84
|
"typescript-eslint": "^8.67.0"
|
|
@@ -24,13 +24,10 @@ If that fails, or prints `2.x` (that is `schpet/linear-cli`, a different tool th
|
|
|
24
24
|
`linear` name), install this one — it needs Bun ≥ 1.1:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
bun run dev -- --version
|
|
27
|
+
bun add -g linear-sdk-cli # installs `linear` and `lin`
|
|
28
|
+
lin --version # `lin` never collides with the other CLI
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
The npm package has not been published yet; use a source clone until its first release.
|
|
33
|
-
|
|
34
31
|
`lin` is the safe spelling when both might be present. If the user is coming from
|
|
35
32
|
`schpet/linear-cli`, their credentials and `.linear.toml` are picked up automatically — see
|
|
36
33
|
`MIGRATING.md` in the package; do not ask them to re-enter an API key before running
|
|
@@ -198,9 +195,7 @@ EOF
|
|
|
198
195
|
|
|
199
196
|
For short, single-line content, use inline `-d/--description` (issues/initiatives),
|
|
200
197
|
the positional `[body]` arg (comments: `linear comment add TES-42 "lgtm"`), or
|
|
201
|
-
`--body` (status updates). There is no `-b` short flag.
|
|
202
|
-
body containing literal `\n` sequences and points back to stdin; never encode a
|
|
203
|
-
multiline body by inserting `\n` inside an ordinary quoted argument.
|
|
198
|
+
`--body` (status updates). There is no `-b` short flag.
|
|
204
199
|
|
|
205
200
|
### Discovery — learn the surface without scraping help
|
|
206
201
|
|
|
@@ -240,7 +235,6 @@ linear attachment delete
|
|
|
240
235
|
linear attachment list
|
|
241
236
|
|
|
242
237
|
linear auth
|
|
243
|
-
linear auth adopt
|
|
244
238
|
linear auth default
|
|
245
239
|
linear auth list
|
|
246
240
|
linear auth login
|
|
@@ -357,8 +351,6 @@ linear notification read-all
|
|
|
357
351
|
linear notification snooze
|
|
358
352
|
linear notification unread
|
|
359
353
|
|
|
360
|
-
linear open
|
|
361
|
-
|
|
362
354
|
linear organization
|
|
363
355
|
linear organization invites
|
|
364
356
|
linear organization members
|
|
@@ -394,7 +386,6 @@ linear team
|
|
|
394
386
|
linear team create
|
|
395
387
|
linear team cycles
|
|
396
388
|
linear team delete
|
|
397
|
-
linear team id
|
|
398
389
|
linear team labels
|
|
399
390
|
linear team list
|
|
400
391
|
linear team members
|
|
@@ -441,7 +432,6 @@ prints the same). These are supplementary — `--help` on any command is authori
|
|
|
441
432
|
- [label](references/label.md) — Work with issue labels
|
|
442
433
|
- [milestone](references/milestone.md) — Work with project milestones
|
|
443
434
|
- [notification](references/notification.md) — Work with your notifications
|
|
444
|
-
- [open](references/open.md) — Open the workspace, an issue, team, project, or URL
|
|
445
435
|
- [organization](references/organization.md) — Inspect the current workspace
|
|
446
436
|
- [project](references/project.md) — Work with projects
|
|
447
437
|
- [project-update](references/project-update.md) — Post and list project status updates
|
|
@@ -14,14 +14,14 @@ Run a raw GraphQL query or mutation against the Linear API
|
|
|
14
14
|
linear api [options] [query]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
| Option | Description
|
|
18
|
-
| --------------------- |
|
|
19
|
-
| `--query-file <path>` | read the query from a file ('-' for stdin)
|
|
20
|
-
| `--var <k=v...>` | set a variable (repeatable;
|
|
21
|
-
| `--vars <json>` | variables as a JSON object
|
|
22
|
-
| `--vars-file <path>` | read variables from a JSON file ('-' for stdin)
|
|
23
|
-
| `--operation <name>` | run this operation of a multi-operation document
|
|
24
|
-
| `--paginate` | auto-follow the first connection's pageInfo cursor (queries only)
|
|
25
|
-
| `--raw` | print the full GraphQL response (data + extensions)
|
|
17
|
+
| Option | Description |
|
|
18
|
+
| --------------------- | ----------------------------------------------------------------- |
|
|
19
|
+
| `--query-file <path>` | read the query from a file ('-' for stdin) |
|
|
20
|
+
| `--var <k=v...>` | set a variable (repeatable; string value) |
|
|
21
|
+
| `--vars <json>` | variables as a JSON object |
|
|
22
|
+
| `--vars-file <path>` | read variables from a JSON file ('-' for stdin) |
|
|
23
|
+
| `--operation <name>` | run this operation of a multi-operation document |
|
|
24
|
+
| `--paginate` | auto-follow the first connection's pageInfo cursor (queries only) |
|
|
25
|
+
| `--raw` | print the full GraphQL response (data + extensions) |
|
|
26
26
|
|
|
27
27
|
**Output (`--json`)**: raw JSON (keys depend on the request) — the response `data` as the API returned it (`--raw`: {data, extensions}); `--paginate` prints one array of every page's nodes
|
|
@@ -14,24 +14,6 @@ Manage authentication
|
|
|
14
14
|
linear auth [options]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
### `linear auth adopt`
|
|
18
|
-
|
|
19
|
-
Adopt an existing named credential from the shared OS keyring
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
linear auth adopt [options] <slug>
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Output (`--json`)**: a receipt object
|
|
26
|
-
|
|
27
|
-
```text
|
|
28
|
-
success: boolean
|
|
29
|
-
workspace: string
|
|
30
|
-
user: {id: string, name: string, email: string}
|
|
31
|
-
storage: string
|
|
32
|
-
path: string
|
|
33
|
-
```
|
|
34
|
-
|
|
35
17
|
### `linear auth default`
|
|
36
18
|
|
|
37
19
|
Set the default workspace credential
|
|
@@ -21,7 +21,7 @@ path: string
|
|
|
21
21
|
description: string
|
|
22
22
|
aliases: string[]
|
|
23
23
|
arguments: Array<{name: string, required: boolean, variadic: boolean}>
|
|
24
|
-
options: Array<{flags: string, description: string
|
|
24
|
+
options: Array<{flags: string, description: string}>
|
|
25
25
|
output?: {kind: string, fields?: object, note?: string, variants?: object}
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -32,6 +32,6 @@ path: string
|
|
|
32
32
|
description: string
|
|
33
33
|
aliases: string[]
|
|
34
34
|
arguments: Array<{name: string, required: boolean, variadic: boolean}>
|
|
35
|
-
options: Array<{flags: string, description: string
|
|
35
|
+
options: Array<{flags: string, description: string}>
|
|
36
36
|
output?: {kind: string, fields?: object, note?: string, variants?: object}
|
|
37
37
|
```
|
|
@@ -23,7 +23,8 @@ credentialWorkspace: string | null
|
|
|
23
23
|
team: string | null
|
|
24
24
|
workspace: string | null
|
|
25
25
|
sort: string
|
|
26
|
-
|
|
26
|
+
vcs: string
|
|
27
|
+
origins: {team: {source: string, path?: string}, workspace: {source: string, path?: string}, sort: {source: string, path?: string}, vcs: {source: string, path?: string}}
|
|
27
28
|
userConfigPath: string
|
|
28
29
|
projectConfigPath: string | null
|
|
29
30
|
globalConfigPath: string | null
|
|
@@ -37,12 +38,12 @@ Write a project .linear.toml (at the git root, or here outside a repository)
|
|
|
37
38
|
linear config init [options]
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
| Option | Description
|
|
41
|
-
| ---------------- |
|
|
42
|
-
| `--team <key>` | default team key (otherwise chosen from a list)
|
|
43
|
-
| `--sort <order>` | default issue-list sort (priority \|
|
|
44
|
-
| `--path <file>` | write this file instead of <git root>/.linear.toml
|
|
45
|
-
| `--force` | overwrite an existing file
|
|
41
|
+
| Option | Description |
|
|
42
|
+
| ---------------- | -------------------------------------------------------- |
|
|
43
|
+
| `--team <key>` | default team key (otherwise chosen from a list) |
|
|
44
|
+
| `--sort <order>` | default issue-list sort (priority \| updated \| created) |
|
|
45
|
+
| `--path <file>` | write this file instead of <git root>/.linear.toml |
|
|
46
|
+
| `--force` | overwrite an existing file |
|
|
46
47
|
|
|
47
48
|
**Output (`--json`)**: a receipt object
|
|
48
49
|
|
|
@@ -55,7 +56,7 @@ sort?: string
|
|
|
55
56
|
|
|
56
57
|
### `linear config set`
|
|
57
58
|
|
|
58
|
-
Set one project setting (team, workspace, sort) in the project config
|
|
59
|
+
Set one project setting (team, workspace, sort, vcs) in the project config
|
|
59
60
|
|
|
60
61
|
```
|
|
61
62
|
linear config set [options] <key> <value>
|
|
@@ -92,7 +93,8 @@ credentialWorkspace: string | null
|
|
|
92
93
|
team: string | null
|
|
93
94
|
workspace: string | null
|
|
94
95
|
sort: string
|
|
95
|
-
|
|
96
|
+
vcs: string
|
|
97
|
+
origins: {team: {source: string, path?: string}, workspace: {source: string, path?: string}, sort: {source: string, path?: string}, vcs: {source: string, path?: string}}
|
|
96
98
|
userConfigPath: string
|
|
97
99
|
projectConfigPath: string | null
|
|
98
100
|
globalConfigPath: string | null
|
|
@@ -44,27 +44,18 @@ team: {id: string, key: string, name: string} | null
|
|
|
44
44
|
project: {id: string, name: string} | null
|
|
45
45
|
milestone: {id: string, name: string} | null
|
|
46
46
|
cycle: {id: string, number: number, name: string | null} | null
|
|
47
|
-
parent: {id: string, identifier: string
|
|
48
|
-
children: Array<{id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}>
|
|
47
|
+
parent: {id: string, identifier: string} | null
|
|
49
48
|
labels: Array<{id: string, name: string}>
|
|
50
49
|
subscribers: Array<{id: string, displayName: string}>
|
|
51
|
-
attachments: Array<{id: string, title: string, url: string, subtitle: string | null, sourceType: string | null, createdAt: string}>
|
|
52
|
-
documents: Array<{id: string, title: string, slugId: string, url: string, createdAt: string, updatedAt: string}>
|
|
53
|
-
relations: Array<{id: string, type: string, issue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}, relatedIssue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}}>
|
|
54
|
-
inverseRelations: Array<{id: string, type: string, issue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}, relatedIssue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}}>
|
|
55
|
-
comments: Array<{id: string, body: string, url: string, createdAt: string, editedAt: string | null, resolvedAt: string | null, parent: {id: string} | null, user: {id: string, displayName: string} | null, externalUser: {id: string, displayName: string} | null, resolvingCommentId: string | null, resolvingUser: {id: string, displayName: string} | null}>
|
|
56
50
|
```
|
|
57
51
|
|
|
58
|
-
With `--
|
|
52
|
+
With `--comments`: the same, plus:
|
|
59
53
|
|
|
60
54
|
```text
|
|
61
|
-
id: string
|
|
62
|
-
identifier: string
|
|
63
|
-
url: string
|
|
64
|
-
opened: boolean
|
|
55
|
+
comments: Array<{id: string, body: string, user: {id: string, displayName: string} | null, createdAt: string, editedAt: string | null, resolvedAt: string | null, parent: {id: string} | null, url: string}>
|
|
65
56
|
```
|
|
66
57
|
|
|
67
|
-
With `--
|
|
58
|
+
With `--web`: a receipt object
|
|
68
59
|
|
|
69
60
|
```text
|
|
70
61
|
id: string
|
|
@@ -152,12 +143,6 @@ Archive an issue
|
|
|
152
143
|
linear issue archive [options] [id]
|
|
153
144
|
```
|
|
154
145
|
|
|
155
|
-
| Option | Description |
|
|
156
|
-
| -------------------- | ------------------------------------------------ |
|
|
157
|
-
| `--bulk <ids>` | archive comma-separated issue ids (repeatable) |
|
|
158
|
-
| `--bulk-file <path>` | archive issue ids from a file (one per line) |
|
|
159
|
-
| `--bulk-stdin` | archive issue ids read from stdin (one per line) |
|
|
160
|
-
|
|
161
146
|
**Output (`--json`)**: a receipt object
|
|
162
147
|
|
|
163
148
|
```text
|
|
@@ -166,15 +151,6 @@ identifier: string
|
|
|
166
151
|
archived: boolean
|
|
167
152
|
```
|
|
168
153
|
|
|
169
|
-
With `--bulk`: a receipt object
|
|
170
|
-
|
|
171
|
-
```text
|
|
172
|
-
action: string
|
|
173
|
-
results: Array<{input: string, id?: string, identifier?: string, archived?: boolean, deleted?: boolean, error?: {message: string, code: string}}>
|
|
174
|
-
succeeded: number
|
|
175
|
-
failed: number
|
|
176
|
-
```
|
|
177
|
-
|
|
178
154
|
### `linear issue assign`
|
|
179
155
|
|
|
180
156
|
Assign an issue (use 'me', email, name, or id). Issue defaults to the branch.
|
|
@@ -404,12 +380,6 @@ Aliases: `rm`
|
|
|
404
380
|
linear issue delete [options] [id]
|
|
405
381
|
```
|
|
406
382
|
|
|
407
|
-
| Option | Description |
|
|
408
|
-
| -------------------- | ----------------------------------------------- |
|
|
409
|
-
| `--bulk <ids>` | delete comma-separated issue ids (repeatable) |
|
|
410
|
-
| `--bulk-file <path>` | delete issue ids from a file (one per line) |
|
|
411
|
-
| `--bulk-stdin` | delete issue ids read from stdin (one per line) |
|
|
412
|
-
|
|
413
383
|
**Output (`--json`)**: a receipt object
|
|
414
384
|
|
|
415
385
|
```text
|
|
@@ -418,15 +388,6 @@ identifier: string
|
|
|
418
388
|
deleted: boolean
|
|
419
389
|
```
|
|
420
390
|
|
|
421
|
-
With `--bulk`: a receipt object
|
|
422
|
-
|
|
423
|
-
```text
|
|
424
|
-
action: string
|
|
425
|
-
results: Array<{input: string, id?: string, identifier?: string, archived?: boolean, deleted?: boolean, error?: {message: string, code: string}}>
|
|
426
|
-
succeeded: number
|
|
427
|
-
failed: number
|
|
428
|
-
```
|
|
429
|
-
|
|
430
391
|
### `linear issue describe`
|
|
431
392
|
|
|
432
393
|
Print a commit message for the issue: 'ID Title' plus Linear-issue trailers
|
|
@@ -829,7 +790,6 @@ linear issue update [options] [id]
|
|
|
829
790
|
| `--estimate <n>` | estimate points |
|
|
830
791
|
| `--parent <id>` | parent issue id |
|
|
831
792
|
| `--due <date>` | due date (YYYY-MM-DD) |
|
|
832
|
-
| `-l, --label <name>` | replace all labels (repeatable / comma-separated) |
|
|
833
793
|
| `--add-label <name>` | add a label (repeatable) |
|
|
834
794
|
| `--remove-label <name>` | remove a label (repeatable) |
|
|
835
795
|
| `--unassign` | clear the assignee |
|
|
@@ -865,12 +825,10 @@ Show an issue (defaults to the current branch's issue)
|
|
|
865
825
|
linear issue view [options] [id]
|
|
866
826
|
```
|
|
867
827
|
|
|
868
|
-
| Option
|
|
869
|
-
|
|
|
870
|
-
| `-w, --web`
|
|
871
|
-
| `--
|
|
872
|
-
| `--no-comments` | exclude comments from the output |
|
|
873
|
-
| `--show-resolved-threads` | include resolved comment threads |
|
|
828
|
+
| Option | Description |
|
|
829
|
+
| ------------ | ------------------------------------------------- |
|
|
830
|
+
| `-w, --web` | open the issue in the browser instead of printing |
|
|
831
|
+
| `--comments` | include recent comments |
|
|
874
832
|
|
|
875
833
|
**Output (`--json`)**: a bare object
|
|
876
834
|
|
|
@@ -898,27 +856,18 @@ team: {id: string, key: string, name: string} | null
|
|
|
898
856
|
project: {id: string, name: string} | null
|
|
899
857
|
milestone: {id: string, name: string} | null
|
|
900
858
|
cycle: {id: string, number: number, name: string | null} | null
|
|
901
|
-
parent: {id: string, identifier: string
|
|
902
|
-
children: Array<{id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}>
|
|
859
|
+
parent: {id: string, identifier: string} | null
|
|
903
860
|
labels: Array<{id: string, name: string}>
|
|
904
861
|
subscribers: Array<{id: string, displayName: string}>
|
|
905
|
-
attachments: Array<{id: string, title: string, url: string, subtitle: string | null, sourceType: string | null, createdAt: string}>
|
|
906
|
-
documents: Array<{id: string, title: string, slugId: string, url: string, createdAt: string, updatedAt: string}>
|
|
907
|
-
relations: Array<{id: string, type: string, issue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}, relatedIssue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}}>
|
|
908
|
-
inverseRelations: Array<{id: string, type: string, issue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}, relatedIssue: {id: string, identifier: string, title: string, state: {id: string, name: string, type: string} | null}}>
|
|
909
|
-
comments: Array<{id: string, body: string, url: string, createdAt: string, editedAt: string | null, resolvedAt: string | null, parent: {id: string} | null, user: {id: string, displayName: string} | null, externalUser: {id: string, displayName: string} | null, resolvingCommentId: string | null, resolvingUser: {id: string, displayName: string} | null}>
|
|
910
862
|
```
|
|
911
863
|
|
|
912
|
-
With `--
|
|
864
|
+
With `--comments`: the same, plus:
|
|
913
865
|
|
|
914
866
|
```text
|
|
915
|
-
id: string
|
|
916
|
-
identifier: string
|
|
917
|
-
url: string
|
|
918
|
-
opened: boolean
|
|
867
|
+
comments: Array<{id: string, body: string, user: {id: string, displayName: string} | null, createdAt: string, editedAt: string | null, resolvedAt: string | null, parent: {id: string} | null, url: string}>
|
|
919
868
|
```
|
|
920
869
|
|
|
921
|
-
With `--
|
|
870
|
+
With `--web`: a receipt object
|
|
922
871
|
|
|
923
872
|
```text
|
|
924
873
|
id: string
|
|
@@ -86,22 +86,6 @@ movedIssues: number
|
|
|
86
86
|
movedTo: {id: string, key: string, name: string} | null
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
### `linear team id`
|
|
90
|
-
|
|
91
|
-
Print a team's UUID (defaults to the configured team)
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
linear team id [options] [key]
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Output (`--json`)**: a receipt object
|
|
98
|
-
|
|
99
|
-
```text
|
|
100
|
-
id: string
|
|
101
|
-
key: string
|
|
102
|
-
name: string
|
|
103
|
-
```
|
|
104
|
-
|
|
105
89
|
### `linear team labels`
|
|
106
90
|
|
|
107
91
|
List a team's labels
|
|
@@ -70,10 +70,7 @@ const GLOBAL_OPTIONS = new Map([
|
|
|
70
70
|
["-t, --team <key>", "default team key (e.g. TES)"],
|
|
71
71
|
["-n, --limit <n>", "max results (positive integer; 0 = all)"],
|
|
72
72
|
["--all", "fetch all results (exhaust pagination)"],
|
|
73
|
-
[
|
|
74
|
-
"-f, --fields <a,b,c>",
|
|
75
|
-
"select fields: table columns or detail lines (human), top-level keys (--json)",
|
|
76
|
-
],
|
|
73
|
+
["-f, --fields <a,b,c>", "select fields: table columns or detail lines (human), top-level keys (--json)"],
|
|
77
74
|
["-y, --yes", "skip confirmation prompts"],
|
|
78
75
|
["-q, --quiet", "suppress status output"],
|
|
79
76
|
["--no-input", "never prompt; fail instead"],
|
|
@@ -193,8 +190,7 @@ function formatOutput(out: OutputShape, heading = "**Output (`--json`)**"): stri
|
|
|
193
190
|
lines.push(`With \`${when}\`: the same${drop}, plus:`);
|
|
194
191
|
lines.push("");
|
|
195
192
|
lines.push("```text");
|
|
196
|
-
for (const [key, shape] of Object.entries(delta.added))
|
|
197
|
-
lines.push(`${key}: ${renderShape(shape)}`);
|
|
193
|
+
for (const [key, shape] of Object.entries(delta.added)) lines.push(`${key}: ${renderShape(shape)}`);
|
|
198
194
|
lines.push("```");
|
|
199
195
|
lines.push("");
|
|
200
196
|
continue;
|
package/src/bin/linear.ts
CHANGED
|
@@ -10,7 +10,6 @@ import { createProgram, parsedGlobalOptions, usageHint, suppressedHelp } from ".
|
|
|
10
10
|
import { Output } from "../output/format.js";
|
|
11
11
|
import { CliError, ExitCode, normalizeError } from "../lib/errors.js";
|
|
12
12
|
import { commandPath } from "../lib/options.js";
|
|
13
|
-
import { isDebugEnabled, shouldUseColor } from "../output/color.js";
|
|
14
13
|
|
|
15
14
|
// `linear schema | head -2` is a reader that stops early, and stdout closing
|
|
16
15
|
// under us is that reader's business, not an error: exit quietly, as every
|
|
@@ -54,14 +53,9 @@ program.parseAsync(process.argv).catch((err) => {
|
|
|
54
53
|
const json = globals.json === true;
|
|
55
54
|
const out = new Output({
|
|
56
55
|
json,
|
|
57
|
-
color:
|
|
58
|
-
disabled: globals.noAnsi,
|
|
59
|
-
json,
|
|
60
|
-
isTTY: process.stderr.isTTY === true,
|
|
61
|
-
}),
|
|
56
|
+
color: globals.noAnsi !== true && !json && process.stderr.isTTY === true,
|
|
62
57
|
quiet: globals.quiet === true,
|
|
63
|
-
debug:
|
|
64
|
-
isTTY: process.stderr.isTTY === true,
|
|
58
|
+
debug: globals.debug === true,
|
|
65
59
|
});
|
|
66
60
|
out.error(cliError);
|
|
67
61
|
process.exit(cliError.exitCode);
|
|
@@ -76,18 +70,14 @@ function fromCommander(err: CommanderError): CliError {
|
|
|
76
70
|
if (err.code === "commander.help") {
|
|
77
71
|
const help = suppressedHelp(program);
|
|
78
72
|
const path = help ? commandPath(help.command) : "linear";
|
|
79
|
-
return new CliError(
|
|
80
|
-
"Missing subcommand.",
|
|
81
|
-
"usage",
|
|
82
|
-
err.code,
|
|
83
|
-
`Run '${path} --help' to see the commands.`,
|
|
84
|
-
);
|
|
73
|
+
return new CliError(`Missing subcommand. Run '${path} --help' to see the commands.`, "usage", err.code);
|
|
85
74
|
}
|
|
86
75
|
return new CliError(
|
|
87
|
-
stripErrorPrefix(err.message).replace(/\.?$/, "."),
|
|
76
|
+
[stripErrorPrefix(err.message).replace(/\.?$/, "."), usageHint(program)]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join(" "),
|
|
88
79
|
"usage",
|
|
89
80
|
err.code,
|
|
90
|
-
usageHint(program),
|
|
91
81
|
);
|
|
92
82
|
}
|
|
93
83
|
|
package/src/cli.ts
CHANGED
|
@@ -29,7 +29,6 @@ import { registerRoadmap } from "./commands/roadmap.js";
|
|
|
29
29
|
import { registerNotification } from "./commands/notification.js";
|
|
30
30
|
import { registerOrganization } from "./commands/organization.js";
|
|
31
31
|
import { registerWebhook } from "./commands/webhook.js";
|
|
32
|
-
import { registerOpen } from "./commands/open.js";
|
|
33
32
|
import { registerCommands, registerSchema } from "./commands/discover.js";
|
|
34
33
|
import { Context, type GlobalOptions } from "./context.js";
|
|
35
34
|
import { currentIssueId } from "./git.js";
|
|
@@ -59,7 +58,6 @@ export function createProgram(): Command {
|
|
|
59
58
|
registerMeta(program);
|
|
60
59
|
registerApi(program);
|
|
61
60
|
registerCompletion(program);
|
|
62
|
-
registerOpen(program);
|
|
63
61
|
// Phase 1: issues. Agent sessions live on issues, so their group is mounted
|
|
64
62
|
// under `issue` (`linear issue agent-session …`); so is file upload
|
|
65
63
|
// (`linear issue attach <issue> <file...>`, TES-602).
|
|
@@ -110,7 +108,7 @@ export function createProgram(): Command {
|
|
|
110
108
|
return;
|
|
111
109
|
}
|
|
112
110
|
const detail = await getIssueDetail(ctx.client, id);
|
|
113
|
-
await renderIssueDetail(ctx, detail,
|
|
111
|
+
await renderIssueDetail(ctx, detail, false);
|
|
114
112
|
});
|
|
115
113
|
|
|
116
114
|
// Make global options usable in any position (e.g. `linear whoami --json`),
|
|
@@ -18,12 +18,7 @@ const ROW_COLUMNS: Column<svc.AgentSessionRow>[] = [
|
|
|
18
18
|
{ key: "agent", header: "Agent", value: (r) => r.agent?.displayName ?? "—", max: 20 },
|
|
19
19
|
{ key: "issue", header: "Issue", value: (r) => r.issue?.identifier ?? "—" },
|
|
20
20
|
{ key: "createdAt", header: "Created", value: (r) => r.createdAt.slice(0, 10) },
|
|
21
|
-
{
|
|
22
|
-
key: "summary",
|
|
23
|
-
header: "Summary",
|
|
24
|
-
value: (r) => r.summary?.replace(/\n/g, " ") ?? "—",
|
|
25
|
-
max: 60,
|
|
26
|
-
},
|
|
21
|
+
{ key: "summary", header: "Summary", value: (r) => r.summary?.replace(/\n/g, " ") ?? "—", max: 60 },
|
|
27
22
|
{ key: "id", header: "ID", value: (r) => r.id },
|
|
28
23
|
];
|
|
29
24
|
|
|
@@ -93,12 +88,7 @@ export function registerAgentSession(issue: Command): void {
|
|
|
93
88
|
["Created", d.createdAt],
|
|
94
89
|
["Started", d.startedAt],
|
|
95
90
|
["Ended", d.endedAt],
|
|
96
|
-
[
|
|
97
|
-
"Dismissed",
|
|
98
|
-
d.dismissedAt
|
|
99
|
-
? `${d.dismissedAt}${d.dismissedBy ? ` by ${d.dismissedBy.displayName}` : ""}`
|
|
100
|
-
: null,
|
|
101
|
-
],
|
|
91
|
+
["Dismissed", d.dismissedAt ? `${d.dismissedAt}${d.dismissedBy ? ` by ${d.dismissedBy.displayName}` : ""}` : null],
|
|
102
92
|
["External link", d.externalLink],
|
|
103
93
|
["URL", d.url],
|
|
104
94
|
["Summary", d.summary ? `\n${d.summary}` : null],
|
|
@@ -113,7 +103,9 @@ function renderActivities(d: svc.AgentSessionDetail): string {
|
|
|
113
103
|
const lines = d.activities.map((a) => {
|
|
114
104
|
const when = a.createdAt.slice(11, 19);
|
|
115
105
|
const text =
|
|
116
|
-
a.action !== null
|
|
106
|
+
a.action !== null
|
|
107
|
+
? `${a.action}${a.parameter ? `: ${a.parameter}` : ""}`
|
|
108
|
+
: (a.body ?? "");
|
|
117
109
|
return `- ${when} ${a.type}: ${text.replace(/\s*\n\s*/g, " ")}`;
|
|
118
110
|
});
|
|
119
111
|
if (d.activitiesTruncated) lines.push(`… more activities not shown (use --json)`);
|
package/src/commands/api.ts
CHANGED
|
@@ -32,12 +32,7 @@ export function registerApi(program: Command): void {
|
|
|
32
32
|
.command("api [query]")
|
|
33
33
|
.description("Run a raw GraphQL query or mutation against the Linear API")
|
|
34
34
|
.option("--query-file <path>", "read the query from a file ('-' for stdin)")
|
|
35
|
-
.option(
|
|
36
|
-
"--var <k=v...>",
|
|
37
|
-
"set a variable (repeatable; JSON literals are typed, other values are strings)",
|
|
38
|
-
collectKeyVal,
|
|
39
|
-
{},
|
|
40
|
-
)
|
|
35
|
+
.option("--var <k=v...>", "set a variable (repeatable; string value)", collectKeyVal, {})
|
|
41
36
|
.option("--vars <json>", "variables as a JSON object")
|
|
42
37
|
.option("--vars-file <path>", "read variables from a JSON file ('-' for stdin)")
|
|
43
38
|
.option("--operation <name>", "run this operation of a multi-operation document")
|
|
@@ -107,11 +102,6 @@ function readFileOrThrow(path: string, label: string): string {
|
|
|
107
102
|
}
|
|
108
103
|
|
|
109
104
|
function resolveQuery(arg: string | undefined, file: string | undefined): string {
|
|
110
|
-
if (arg !== undefined && file !== undefined) {
|
|
111
|
-
throw usageError(
|
|
112
|
-
"Pass the GraphQL query either as an argument or with --query-file, not both.",
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
105
|
let query: string | undefined;
|
|
116
106
|
if (arg !== undefined) query = arg;
|
|
117
107
|
else if (file !== undefined) query = readFileOrThrow(file, "--query-file");
|
|
@@ -129,7 +119,7 @@ function resolveVariables(opts: Record<string, any>): Record<string, unknown> {
|
|
|
129
119
|
vars = { ...vars, ...parseJsonObject(text, "--vars-file") };
|
|
130
120
|
}
|
|
131
121
|
if (opts.vars) vars = { ...vars, ...parseJsonObject(opts.vars, "--vars") };
|
|
132
|
-
if (opts.var) vars = { ...vars, ...(opts.var as Record<string,
|
|
122
|
+
if (opts.var) vars = { ...vars, ...(opts.var as Record<string, string>) };
|
|
133
123
|
return vars;
|
|
134
124
|
}
|
|
135
125
|
|
|
@@ -227,7 +217,10 @@ function describeNames(operations: OperationDefinitionNode[]): string {
|
|
|
227
217
|
return operations.map((o) => o.name?.value ?? "<unnamed>").join(", ");
|
|
228
218
|
}
|
|
229
219
|
|
|
230
|
-
function unknownOperationMessage(
|
|
220
|
+
function unknownOperationMessage(
|
|
221
|
+
requested: string,
|
|
222
|
+
operations: OperationDefinitionNode[],
|
|
223
|
+
): string {
|
|
231
224
|
const named = operations.filter((o) => o.name).map((o) => o.name!.value);
|
|
232
225
|
const available = named.length
|
|
233
226
|
? `Available: ${named.join(", ")}.`
|
|
@@ -276,6 +269,7 @@ async function paginate(
|
|
|
276
269
|
variables: Record<string, unknown>,
|
|
277
270
|
): Promise<{ nodes: unknown[]; pageCount: number; truncated: boolean }> {
|
|
278
271
|
let after: string | undefined = variables.after as string | undefined;
|
|
272
|
+
let firstData: any;
|
|
279
273
|
const allNodes: unknown[] = [];
|
|
280
274
|
let pages = 0;
|
|
281
275
|
let truncated = false;
|
|
@@ -284,10 +278,10 @@ async function paginate(
|
|
|
284
278
|
const result: any = await withRetry(() =>
|
|
285
279
|
(ctx.client.client as any).rawRequest(query, { ...variables, after }),
|
|
286
280
|
);
|
|
281
|
+
if (!firstData) firstData = result.data;
|
|
287
282
|
const conn = findConnection(result.data);
|
|
288
283
|
if (!conn) {
|
|
289
|
-
if (pages === 0)
|
|
290
|
-
throw new CliError("No connection (nodes/pageInfo) found to paginate.", "usage");
|
|
284
|
+
if (pages === 0) throw new CliError("No connection (nodes/pageInfo) found to paginate.", "usage");
|
|
291
285
|
break;
|
|
292
286
|
}
|
|
293
287
|
allNodes.push(...conn.nodes);
|
package/src/commands/attach.ts
CHANGED
|
@@ -27,14 +27,8 @@ export function registerAttach(issue: Command): void {
|
|
|
27
27
|
.command("attach <issue> <file...>")
|
|
28
28
|
.description("Upload files and attach them to an issue (private by default)")
|
|
29
29
|
.option("--title <title>", "attachment title (single file only; default: the file name)")
|
|
30
|
-
.option(
|
|
31
|
-
|
|
32
|
-
"also post a comment with this body embedding the files as markdown",
|
|
33
|
-
)
|
|
34
|
-
.option(
|
|
35
|
-
"--public",
|
|
36
|
-
"upload to a public, world-readable URL (raster images only; default: private)",
|
|
37
|
-
)
|
|
30
|
+
.option("--comment <body>", "also post a comment with this body embedding the files as markdown")
|
|
31
|
+
.option("--public", "upload to a public, world-readable URL (raster images only; default: private)")
|
|
38
32
|
.addHelpText(
|
|
39
33
|
"after",
|
|
40
34
|
[
|
|
@@ -56,13 +50,8 @@ export function registerAttach(issue: Command): void {
|
|
|
56
50
|
comment: opts.comment,
|
|
57
51
|
public: opts.public === true,
|
|
58
52
|
onAttached: (a, iss) => {
|
|
59
|
-
ctx.output.success(
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
if (a.public)
|
|
63
|
-
ctx.output.warn(
|
|
64
|
-
`${a.filename} is on a public URL, readable by anyone: ${a.assetUrl}`,
|
|
65
|
-
);
|
|
53
|
+
ctx.output.success(`Attached ${a.filename} (${formatBytes(a.size)}) to ${iss.identifier}`);
|
|
54
|
+
if (a.public) ctx.output.warn(`${a.filename} is on a public URL, readable by anyone: ${a.assetUrl}`);
|
|
66
55
|
},
|
|
67
56
|
});
|
|
68
57
|
const identifier = result.issue.identifier;
|
package/src/commands/comment.ts
CHANGED
|
@@ -57,10 +57,7 @@ function buildAdd(_o: MountOptions): Command {
|
|
|
57
57
|
"upload a file and embed it in the comment (images inline; repeatable; private by default)",
|
|
58
58
|
collectArray,
|
|
59
59
|
)
|
|
60
|
-
.option(
|
|
61
|
-
"--public",
|
|
62
|
-
"upload the attachments to public, world-readable URLs (raster images only)",
|
|
63
|
-
)
|
|
60
|
+
.option("--public", "upload the attachments to public, world-readable URLs (raster images only)")
|
|
64
61
|
.action(
|
|
65
62
|
action(async (ctx: Context, opts, issueArg: string, bodyArg?: string) => {
|
|
66
63
|
const attachments: string[] = opts.attach ?? [];
|
|
@@ -84,8 +81,7 @@ function buildAdd(_o: MountOptions): Command {
|
|
|
84
81
|
public: opts.public === true,
|
|
85
82
|
});
|
|
86
83
|
for (const u of uploads) {
|
|
87
|
-
if (u.public)
|
|
88
|
-
ctx.output.warn(`${u.filename} is on a public URL, readable by anyone: ${u.assetUrl}`);
|
|
84
|
+
if (u.public) ctx.output.warn(`${u.filename} is on a public URL, readable by anyone: ${u.assetUrl}`);
|
|
89
85
|
}
|
|
90
86
|
ctx.output.emit(
|
|
91
87
|
{
|