linear-sdk-cli 0.1.0
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/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +9 -0
- package/CHANGELOG.md +807 -0
- package/COVERAGE.md +495 -0
- package/LICENSE +21 -0
- package/MIGRATING.md +220 -0
- package/README.md +524 -0
- package/package.json +87 -0
- package/skills/linear-sdk-cli/SKILL.md +534 -0
- package/skills/linear-sdk-cli/references/api.md +27 -0
- package/skills/linear-sdk-cli/references/attachment.md +82 -0
- package/skills/linear-sdk-cli/references/auth.md +174 -0
- package/skills/linear-sdk-cli/references/commands.md +37 -0
- package/skills/linear-sdk-cli/references/comment.md +154 -0
- package/skills/linear-sdk-cli/references/completion.md +17 -0
- package/skills/linear-sdk-cli/references/config.md +99 -0
- package/skills/linear-sdk-cli/references/cycle.md +131 -0
- package/skills/linear-sdk-cli/references/document.md +180 -0
- package/skills/linear-sdk-cli/references/favorite.md +78 -0
- package/skills/linear-sdk-cli/references/initiative-update.md +67 -0
- package/skills/linear-sdk-cli/references/initiative.md +256 -0
- package/skills/linear-sdk-cli/references/issue.md +928 -0
- package/skills/linear-sdk-cli/references/label.md +112 -0
- package/skills/linear-sdk-cli/references/milestone.md +139 -0
- package/skills/linear-sdk-cli/references/notification.md +123 -0
- package/skills/linear-sdk-cli/references/open.md +29 -0
- package/skills/linear-sdk-cli/references/organization.md +99 -0
- package/skills/linear-sdk-cli/references/project-update.md +67 -0
- package/skills/linear-sdk-cli/references/project.md +234 -0
- package/skills/linear-sdk-cli/references/roadmap.md +150 -0
- package/skills/linear-sdk-cli/references/schema.md +21 -0
- package/skills/linear-sdk-cli/references/state.md +61 -0
- package/skills/linear-sdk-cli/references/team.md +227 -0
- package/skills/linear-sdk-cli/references/user.md +103 -0
- package/skills/linear-sdk-cli/references/webhook.md +151 -0
- package/skills/linear-sdk-cli/references/whoami.md +26 -0
- package/skills/linear-sdk-cli/scripts/generate-docs.ts +339 -0
- package/src/bin/linear.ts +97 -0
- package/src/cli.ts +232 -0
- package/src/client.ts +172 -0
- package/src/commands/agent-session.ts +121 -0
- package/src/commands/api.ts +317 -0
- package/src/commands/attach.ts +90 -0
- package/src/commands/attachment.ts +74 -0
- package/src/commands/comment.ts +225 -0
- package/src/commands/completion.ts +69 -0
- package/src/commands/cycle.ts +125 -0
- package/src/commands/discover.ts +170 -0
- package/src/commands/document.ts +211 -0
- package/src/commands/favorite.ts +72 -0
- package/src/commands/initiative-update.ts +47 -0
- package/src/commands/initiative.ts +261 -0
- package/src/commands/issue.ts +1263 -0
- package/src/commands/label.ts +119 -0
- package/src/commands/meta.ts +534 -0
- package/src/commands/milestone.ts +163 -0
- package/src/commands/notification.ts +110 -0
- package/src/commands/open.ts +84 -0
- package/src/commands/organization.ts +79 -0
- package/src/commands/project-update.ts +47 -0
- package/src/commands/project.ts +339 -0
- package/src/commands/roadmap.ts +131 -0
- package/src/commands/state.ts +58 -0
- package/src/commands/team.ts +243 -0
- package/src/commands/user.ts +107 -0
- package/src/commands/webhook.ts +156 -0
- package/src/config.ts +1236 -0
- package/src/context.ts +117 -0
- package/src/git.ts +174 -0
- package/src/index.ts +10 -0
- package/src/lib/action.ts +26 -0
- package/src/lib/body.ts +172 -0
- package/src/lib/errors.ts +195 -0
- package/src/lib/introspect.ts +150 -0
- package/src/lib/keyring.ts +270 -0
- package/src/lib/mutation.ts +53 -0
- package/src/lib/open.ts +32 -0
- package/src/lib/options.ts +640 -0
- package/src/lib/output-shapes.ts +562 -0
- package/src/lib/pagination.ts +145 -0
- package/src/lib/prompt.ts +91 -0
- package/src/lib/resolve.ts +596 -0
- package/src/lib/shape.ts +273 -0
- package/src/lib/status-update.ts +91 -0
- package/src/lib/upload.ts +329 -0
- package/src/output/color.ts +52 -0
- package/src/output/format.ts +238 -0
- package/src/output/markdown.ts +133 -0
- package/src/output/pager.ts +131 -0
- package/src/output/sanitize.ts +76 -0
- package/src/output/table.ts +198 -0
- package/src/services/agent-session.ts +289 -0
- package/src/services/attachment.ts +198 -0
- package/src/services/comment.ts +262 -0
- package/src/services/cycle.ts +224 -0
- package/src/services/document.ts +357 -0
- package/src/services/favorite.ts +147 -0
- package/src/services/initiative-update.ts +59 -0
- package/src/services/initiative.ts +449 -0
- package/src/services/issue.ts +1153 -0
- package/src/services/label.ts +193 -0
- package/src/services/milestone.ts +262 -0
- package/src/services/notification.ts +190 -0
- package/src/services/organization.ts +144 -0
- package/src/services/project-update.ts +36 -0
- package/src/services/project.ts +494 -0
- package/src/services/roadmap.ts +177 -0
- package/src/services/state.ts +127 -0
- package/src/services/team.ts +386 -0
- package/src/services/user.ts +137 -0
- package/src/services/webhook.ts +203 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# linear user
|
|
2
|
+
|
|
3
|
+
> Inspect workspace users
|
|
4
|
+
|
|
5
|
+
Group alias: `u`
|
|
6
|
+
|
|
7
|
+
_Generated from `linear commands --json`. `linear user --help` (or `<subcommand> --help`) is authoritative._
|
|
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.
|
|
10
|
+
|
|
11
|
+
### `linear user`
|
|
12
|
+
|
|
13
|
+
Inspect workspace users
|
|
14
|
+
|
|
15
|
+
Aliases: `u`
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
linear user [options]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### `linear user list`
|
|
22
|
+
|
|
23
|
+
List workspace users
|
|
24
|
+
|
|
25
|
+
Aliases: `ls`
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
linear user list [options]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Option | Description |
|
|
32
|
+
| -------------------- | ----------------------------------------------- |
|
|
33
|
+
| `--include-disabled` | include deactivated users (excluded by default) |
|
|
34
|
+
|
|
35
|
+
**Output (`--json`)**: a bare array of objects
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
id: string
|
|
39
|
+
displayName: string
|
|
40
|
+
name: string
|
|
41
|
+
email: string
|
|
42
|
+
active: boolean
|
|
43
|
+
admin: boolean
|
|
44
|
+
guest: boolean
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `linear user me`
|
|
48
|
+
|
|
49
|
+
Show the authenticated viewer
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
linear user me [options]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Output (`--json`)**: a bare object
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
id: string
|
|
59
|
+
displayName: string
|
|
60
|
+
name: string
|
|
61
|
+
email: string
|
|
62
|
+
active: boolean
|
|
63
|
+
admin: boolean
|
|
64
|
+
guest: boolean
|
|
65
|
+
isMe: boolean
|
|
66
|
+
description: string | null
|
|
67
|
+
statusLabel: string | null
|
|
68
|
+
timezone: string | null
|
|
69
|
+
url: string
|
|
70
|
+
avatarUrl: string | null
|
|
71
|
+
lastSeen: string | null
|
|
72
|
+
createdAt: string
|
|
73
|
+
updatedAt: string
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `linear user view`
|
|
77
|
+
|
|
78
|
+
Show a user (me, email, name, or id)
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
linear user view [options] <who>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Output (`--json`)**: a bare object
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
id: string
|
|
88
|
+
displayName: string
|
|
89
|
+
name: string
|
|
90
|
+
email: string
|
|
91
|
+
active: boolean
|
|
92
|
+
admin: boolean
|
|
93
|
+
guest: boolean
|
|
94
|
+
isMe: boolean
|
|
95
|
+
description: string | null
|
|
96
|
+
statusLabel: string | null
|
|
97
|
+
timezone: string | null
|
|
98
|
+
url: string
|
|
99
|
+
avatarUrl: string | null
|
|
100
|
+
lastSeen: string | null
|
|
101
|
+
createdAt: string
|
|
102
|
+
updatedAt: string
|
|
103
|
+
```
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# linear webhook
|
|
2
|
+
|
|
3
|
+
> Manage workspace webhooks
|
|
4
|
+
|
|
5
|
+
Group alias: `wh`
|
|
6
|
+
|
|
7
|
+
_Generated from `linear commands --json`. `linear webhook --help` (or `<subcommand> --help`) is authoritative._
|
|
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.
|
|
10
|
+
|
|
11
|
+
### `linear webhook`
|
|
12
|
+
|
|
13
|
+
Manage workspace webhooks
|
|
14
|
+
|
|
15
|
+
Aliases: `wh`
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
linear webhook [options]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Output (`--json`)**: a bare object — runs `webhook view` by default
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
id: string
|
|
25
|
+
url: string | null
|
|
26
|
+
enabled: boolean
|
|
27
|
+
resourceTypes: string[]
|
|
28
|
+
label: string | null
|
|
29
|
+
allPublicTeams: boolean
|
|
30
|
+
team: string | null
|
|
31
|
+
creator: string | null
|
|
32
|
+
createdAt: string
|
|
33
|
+
updatedAt: string
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `linear webhook create`
|
|
37
|
+
|
|
38
|
+
Create a webhook (scope to the global --team, or --all-public)
|
|
39
|
+
|
|
40
|
+
Aliases: `new`
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
linear webhook create [options]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Option | Description |
|
|
47
|
+
| ---------------------- | ---------------------------------------------- |
|
|
48
|
+
| `--url <url>` | destination URL that receives event payloads |
|
|
49
|
+
| `--resource <type...>` | resource type to subscribe to (repeatable) |
|
|
50
|
+
| `--label <label>` | human-readable label for the webhook |
|
|
51
|
+
| `--all-public` | subscribe to all public teams in the workspace |
|
|
52
|
+
| `--secret <secret>` | secret used to sign webhook payloads |
|
|
53
|
+
|
|
54
|
+
**Output (`--json`)**: a receipt object
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
id: string
|
|
58
|
+
url: string | null
|
|
59
|
+
enabled: boolean
|
|
60
|
+
resourceTypes: string[]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `linear webhook delete`
|
|
64
|
+
|
|
65
|
+
Delete a webhook
|
|
66
|
+
|
|
67
|
+
Aliases: `rm`
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
linear webhook delete [options] <id>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Output (`--json`)**: a receipt object
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
id: string
|
|
77
|
+
deleted: boolean
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `linear webhook list`
|
|
81
|
+
|
|
82
|
+
List webhooks
|
|
83
|
+
|
|
84
|
+
Aliases: `ls`
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
linear webhook list [options]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Output (`--json`)**: a bare array of objects
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
id: string
|
|
94
|
+
url: string | null
|
|
95
|
+
enabled: boolean
|
|
96
|
+
resourceTypes: string[]
|
|
97
|
+
label: string | null
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `linear webhook update`
|
|
101
|
+
|
|
102
|
+
Update a webhook
|
|
103
|
+
|
|
104
|
+
Aliases: `edit`
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
linear webhook update [options] <id>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Option | Description |
|
|
111
|
+
| ---------------------- | ------------------------------- |
|
|
112
|
+
| `--url <url>` | new destination URL |
|
|
113
|
+
| `--enabled` | enable the webhook |
|
|
114
|
+
| `--disabled` | disable the webhook |
|
|
115
|
+
| `--resource <type...>` | set resource types (repeatable) |
|
|
116
|
+
| `--label <label>` | new label |
|
|
117
|
+
| `--secret <secret>` | new signing secret |
|
|
118
|
+
|
|
119
|
+
**Output (`--json`)**: a receipt object
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
id: string
|
|
123
|
+
url: string | null
|
|
124
|
+
enabled: boolean
|
|
125
|
+
resourceTypes: string[]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `linear webhook view`
|
|
129
|
+
|
|
130
|
+
Show a webhook
|
|
131
|
+
|
|
132
|
+
Aliases: `show`
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
linear webhook view [options] <id>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Output (`--json`)**: a bare object
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
id: string
|
|
142
|
+
url: string | null
|
|
143
|
+
enabled: boolean
|
|
144
|
+
resourceTypes: string[]
|
|
145
|
+
label: string | null
|
|
146
|
+
allPublicTeams: boolean
|
|
147
|
+
team: string | null
|
|
148
|
+
creator: string | null
|
|
149
|
+
createdAt: string
|
|
150
|
+
updatedAt: string
|
|
151
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# linear whoami
|
|
2
|
+
|
|
3
|
+
> Show the authenticated user
|
|
4
|
+
|
|
5
|
+
_Generated from `linear commands --json`. `linear whoami --help` (or `<subcommand> --help`) is authoritative._
|
|
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.
|
|
8
|
+
|
|
9
|
+
### `linear whoami`
|
|
10
|
+
|
|
11
|
+
Show the authenticated user
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
linear whoami [options]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Output (`--json`)**: a bare object
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
id: string
|
|
21
|
+
name: string
|
|
22
|
+
displayName: string
|
|
23
|
+
email: string
|
|
24
|
+
admin: boolean
|
|
25
|
+
organization: {id: string, name: string, urlKey: string}
|
|
26
|
+
```
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Regenerate the linear-sdk-cli skill docs from the CLI's own machine-readable
|
|
5
|
+
* command tree (`linear commands --json`).
|
|
6
|
+
*
|
|
7
|
+
* Produces, idempotently:
|
|
8
|
+
* - skills/linear-sdk-cli/references/<group>.md — one per top-level group
|
|
9
|
+
* - the generated command list inside SKILL.md, between the markers
|
|
10
|
+
* <!-- BEGIN GENERATED COMMANDS --> / <!-- END GENERATED COMMANDS -->
|
|
11
|
+
*
|
|
12
|
+
* The CLI is resolved as `src/bin/linear.ts` under the repo root (run via Bun),
|
|
13
|
+
* falling back to a `linear` binary on PATH. Override with LINEAR_CLI, e.g.
|
|
14
|
+
* LINEAR_CLI="linear" bun run skills/linear-sdk-cli/scripts/generate-docs.ts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { existsSync } from "node:fs";
|
|
19
|
+
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
// The one renderer for a shape, shared with `linear commands <path>` so the
|
|
23
|
+
// reference and the CLI spell a row the same way.
|
|
24
|
+
import { renderShape, variantDelta, type OutputShape } from "../../../src/lib/shape.js";
|
|
25
|
+
|
|
26
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const SKILL_DIR = join(SCRIPT_DIR, "..");
|
|
28
|
+
const REFERENCES_DIR = join(SKILL_DIR, "references");
|
|
29
|
+
const SKILL_MD = join(SKILL_DIR, "SKILL.md");
|
|
30
|
+
const REPO_ROOT = join(SKILL_DIR, "..", "..");
|
|
31
|
+
|
|
32
|
+
const BEGIN_MARKER = "<!-- BEGIN GENERATED COMMANDS -->";
|
|
33
|
+
const END_MARKER = "<!-- END GENERATED COMMANDS -->";
|
|
34
|
+
|
|
35
|
+
interface CliArgument {
|
|
36
|
+
name: string;
|
|
37
|
+
required: boolean;
|
|
38
|
+
variadic: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface CliOption {
|
|
42
|
+
flags: string;
|
|
43
|
+
description: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface CliCommand {
|
|
47
|
+
path: string;
|
|
48
|
+
description: string;
|
|
49
|
+
aliases: string[];
|
|
50
|
+
arguments: CliArgument[];
|
|
51
|
+
options: CliOption[];
|
|
52
|
+
/** What the command prints under `--json` (TES-610); absent on a bare group. */
|
|
53
|
+
output?: OutputShape;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options shared by (almost) every command. We collapse them into a single note
|
|
58
|
+
* per reference file instead of repeating ~15 rows under each subcommand.
|
|
59
|
+
*
|
|
60
|
+
* Keyed by flags AND description, because a command may redefine a global with a
|
|
61
|
+
* meaning of its own — `--team` filters (repeatably) on the issue queries and
|
|
62
|
+
* *moves the issue* on `issue update`. Those rows must survive the collapse;
|
|
63
|
+
* matching on the flag string alone would hide exactly the ones worth reading.
|
|
64
|
+
*/
|
|
65
|
+
const GLOBAL_OPTIONS = new Map([
|
|
66
|
+
["-j, --json", "output machine-readable JSON"],
|
|
67
|
+
["--no-ansi", "disable colored output"],
|
|
68
|
+
["--api-key <key>", "Linear API key (overrides env/config)"],
|
|
69
|
+
["--workspace <slug>", "select workspace credential profile"],
|
|
70
|
+
["-t, --team <key>", "default team key (e.g. TES)"],
|
|
71
|
+
["-n, --limit <n>", "max results (positive integer; 0 = all)"],
|
|
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
|
+
],
|
|
77
|
+
["-y, --yes", "skip confirmation prompts"],
|
|
78
|
+
["-q, --quiet", "suppress status output"],
|
|
79
|
+
["--no-input", "never prompt; fail instead"],
|
|
80
|
+
["--debug", "verbose errors (stack traces, raw GraphQL)"],
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const isGlobal = (opt: CliOption): boolean => GLOBAL_OPTIONS.get(opt.flags) === opt.description;
|
|
84
|
+
|
|
85
|
+
function resolveCli(): string[] {
|
|
86
|
+
const fromEnv = process.env.LINEAR_CLI?.trim();
|
|
87
|
+
if (fromEnv) return fromEnv.split(/\s+/);
|
|
88
|
+
const local = join(REPO_ROOT, "src", "bin", "linear.ts");
|
|
89
|
+
if (existsSync(local)) return ["bun", "run", local];
|
|
90
|
+
return ["linear"];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function loadCommands(): CliCommand[] {
|
|
94
|
+
const [bin, ...rest] = resolveCli();
|
|
95
|
+
const result = spawnSync(bin, [...rest, "commands", "--json"], {
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
98
|
+
env: { ...process.env, NO_COLOR: "1" },
|
|
99
|
+
});
|
|
100
|
+
if (result.status !== 0) {
|
|
101
|
+
const detail = result.stderr || result.error?.message || "unknown error";
|
|
102
|
+
throw new Error(`\`linear commands --json\` failed: ${detail}`);
|
|
103
|
+
}
|
|
104
|
+
const parsed = JSON.parse(result.stdout) as CliCommand[];
|
|
105
|
+
return parsed.sort((a, b) => a.path.localeCompare(b.path));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The top-level group name for a command path ("issue create" -> "issue"). */
|
|
109
|
+
function groupOf(path: string): string {
|
|
110
|
+
return path.split(" ")[0]!;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function usageLine(cmd: CliCommand): string {
|
|
114
|
+
const parts = ["linear", cmd.path];
|
|
115
|
+
if (cmd.options.length > 0) parts.push("[options]");
|
|
116
|
+
for (const arg of cmd.arguments) {
|
|
117
|
+
const inner = arg.variadic ? `${arg.name}...` : arg.name;
|
|
118
|
+
parts.push(arg.required ? `<${inner}>` : `[${inner}]`);
|
|
119
|
+
}
|
|
120
|
+
return parts.join(" ");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function formatCommand(cmd: CliCommand): string {
|
|
124
|
+
const lines: string[] = [];
|
|
125
|
+
lines.push(`### \`linear ${cmd.path}\``);
|
|
126
|
+
lines.push("");
|
|
127
|
+
if (cmd.description) {
|
|
128
|
+
lines.push(cmd.description);
|
|
129
|
+
lines.push("");
|
|
130
|
+
}
|
|
131
|
+
if (cmd.aliases.length > 0) {
|
|
132
|
+
lines.push(`Aliases: ${cmd.aliases.map((a) => `\`${a}\``).join(", ")}`);
|
|
133
|
+
lines.push("");
|
|
134
|
+
}
|
|
135
|
+
lines.push("```");
|
|
136
|
+
lines.push(usageLine(cmd));
|
|
137
|
+
lines.push("```");
|
|
138
|
+
lines.push("");
|
|
139
|
+
|
|
140
|
+
const localOptions = cmd.options.filter((o) => !isGlobal(o));
|
|
141
|
+
if (localOptions.length > 0) {
|
|
142
|
+
lines.push("| Option | Description |");
|
|
143
|
+
lines.push("| --- | --- |");
|
|
144
|
+
for (const opt of localOptions) {
|
|
145
|
+
lines.push(`| \`${escapePipes(opt.flags)}\` | ${escapePipes(opt.description)} |`);
|
|
146
|
+
}
|
|
147
|
+
lines.push("");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (cmd.output) lines.push(...formatOutput(cmd.output));
|
|
151
|
+
|
|
152
|
+
return lines.join("\n");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** `array of objects` / `object` / `receipt` / `raw` / `none`, as prose. */
|
|
156
|
+
function kindLabel(out: OutputShape): string {
|
|
157
|
+
switch (out.kind) {
|
|
158
|
+
case "list":
|
|
159
|
+
return "a bare array of objects";
|
|
160
|
+
case "object":
|
|
161
|
+
return "a bare object";
|
|
162
|
+
case "receipt":
|
|
163
|
+
return "a receipt object";
|
|
164
|
+
case "raw":
|
|
165
|
+
return "raw JSON (keys depend on the request)";
|
|
166
|
+
default:
|
|
167
|
+
return "none — never prints JSON";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The `--json` output block of a command reference: what kind of value, then
|
|
173
|
+
* one `key: type` line per field in a text fence, then any variant the same
|
|
174
|
+
* way. Rendered with the CLI's own `renderShape`, so `linear commands <path>`
|
|
175
|
+
* and the reference agree to the character.
|
|
176
|
+
*/
|
|
177
|
+
function formatOutput(out: OutputShape, heading = "**Output (`--json`)**"): string[] {
|
|
178
|
+
const lines: string[] = [];
|
|
179
|
+
lines.push(`${heading}: ${kindLabel(out)}${out.note ? ` — ${out.note}` : ""}`);
|
|
180
|
+
lines.push("");
|
|
181
|
+
const fields = Object.entries(out.fields ?? {});
|
|
182
|
+
if (fields.length > 0) {
|
|
183
|
+
lines.push("```text");
|
|
184
|
+
for (const [key, shape] of fields) lines.push(`${key}: ${renderShape(shape)}`);
|
|
185
|
+
lines.push("```");
|
|
186
|
+
lines.push("");
|
|
187
|
+
}
|
|
188
|
+
for (const [when, variant] of Object.entries(out.variants ?? {})) {
|
|
189
|
+
const delta = variantDelta(out, variant);
|
|
190
|
+
if (delta) {
|
|
191
|
+
// The base plus a few keys: say only what changes.
|
|
192
|
+
const drop = delta.dropped.length ? `, without \`${delta.dropped.join("`, `")}\`` : "";
|
|
193
|
+
lines.push(`With \`${when}\`: the same${drop}, plus:`);
|
|
194
|
+
lines.push("");
|
|
195
|
+
lines.push("```text");
|
|
196
|
+
for (const [key, shape] of Object.entries(delta.added))
|
|
197
|
+
lines.push(`${key}: ${renderShape(shape)}`);
|
|
198
|
+
lines.push("```");
|
|
199
|
+
lines.push("");
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
lines.push(...formatOutput(variant, `With \`${when}\``));
|
|
203
|
+
}
|
|
204
|
+
return lines;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function escapePipes(text: string): string {
|
|
208
|
+
return text.replace(/\|/g, "\\|");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function buildReference(group: string, commands: CliCommand[]): string {
|
|
212
|
+
const root = commands.find((c) => c.path === group);
|
|
213
|
+
const lines: string[] = [];
|
|
214
|
+
lines.push(`# linear ${group}`);
|
|
215
|
+
lines.push("");
|
|
216
|
+
if (root?.description) {
|
|
217
|
+
lines.push(`> ${root.description}`);
|
|
218
|
+
lines.push("");
|
|
219
|
+
}
|
|
220
|
+
if (root && root.aliases.length > 0) {
|
|
221
|
+
lines.push(`Group alias: ${root.aliases.map((a) => `\`${a}\``).join(", ")}`);
|
|
222
|
+
lines.push("");
|
|
223
|
+
}
|
|
224
|
+
lines.push(
|
|
225
|
+
"_Generated from `linear commands --json`. `linear " +
|
|
226
|
+
group +
|
|
227
|
+
" --help` (or `<subcommand> --help`) is authoritative._",
|
|
228
|
+
);
|
|
229
|
+
lines.push("");
|
|
230
|
+
lines.push(
|
|
231
|
+
"Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, " +
|
|
232
|
+
"`-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `-t/--team <key>`, " +
|
|
233
|
+
"`-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), " +
|
|
234
|
+
"and `--debug`. " +
|
|
235
|
+
"Only command-specific options are listed below.",
|
|
236
|
+
);
|
|
237
|
+
lines.push("");
|
|
238
|
+
|
|
239
|
+
for (const cmd of commands) {
|
|
240
|
+
lines.push(formatCommand(cmd));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
lines
|
|
245
|
+
.join("\n")
|
|
246
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
247
|
+
.trimEnd() + "\n"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function buildCommandList(commands: CliCommand[]): string {
|
|
252
|
+
const groups = new Map<string, CliCommand[]>();
|
|
253
|
+
for (const cmd of commands) {
|
|
254
|
+
const g = groupOf(cmd.path);
|
|
255
|
+
if (!groups.has(g)) groups.set(g, []);
|
|
256
|
+
groups.get(g)!.push(cmd);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const blocks: string[] = [];
|
|
260
|
+
for (const [, cmds] of [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
261
|
+
blocks.push(cmds.map((c) => `linear ${c.path}`).join("\n"));
|
|
262
|
+
}
|
|
263
|
+
return blocks.join("\n\n");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function writeReferences(commands: CliCommand[]): Promise<string[]> {
|
|
267
|
+
await mkdir(REFERENCES_DIR, { recursive: true });
|
|
268
|
+
|
|
269
|
+
// Remove previously generated reference files so renamed/removed groups don't linger.
|
|
270
|
+
try {
|
|
271
|
+
for (const entry of await readdir(REFERENCES_DIR)) {
|
|
272
|
+
if (entry.endsWith(".md")) await rm(join(REFERENCES_DIR, entry));
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
// directory was just created; nothing to clean
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const groups = [...new Set(commands.map((c) => groupOf(c.path)))].sort();
|
|
279
|
+
for (const group of groups) {
|
|
280
|
+
const groupCommands = commands.filter((c) => groupOf(c.path) === group);
|
|
281
|
+
const filepath = join(REFERENCES_DIR, `${group}.md`);
|
|
282
|
+
await writeFile(filepath, buildReference(group, groupCommands), "utf8");
|
|
283
|
+
}
|
|
284
|
+
return groups;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function updateSkillMd(commands: CliCommand[], groups: string[]): Promise<void> {
|
|
288
|
+
const current = await readFile(SKILL_MD, "utf8");
|
|
289
|
+
|
|
290
|
+
const begin = current.indexOf(BEGIN_MARKER);
|
|
291
|
+
const end = current.indexOf(END_MARKER);
|
|
292
|
+
if (begin === -1 || end === -1 || end < begin) {
|
|
293
|
+
throw new Error(`Could not find generated-commands markers in ${SKILL_MD}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const before = current.slice(0, begin + BEGIN_MARKER.length);
|
|
297
|
+
const after = current.slice(end);
|
|
298
|
+
// Format chosen to match Prettier: no blank line after the opening marker,
|
|
299
|
+
// one blank line before the closing marker, so re-running stays a no-op.
|
|
300
|
+
const block = ["", "```text", buildCommandList(commands), "```", "", ""].join("\n");
|
|
301
|
+
const next = `${before}${block}${after}`;
|
|
302
|
+
|
|
303
|
+
await writeFile(SKILL_MD, next, "utf8");
|
|
304
|
+
|
|
305
|
+
// Sanity check that every group has a reference link (warn only; non-fatal).
|
|
306
|
+
for (const group of groups) {
|
|
307
|
+
if (!next.includes(`references/${group}.md`)) {
|
|
308
|
+
console.warn(`Warning: SKILL.md has no link to references/${group}.md`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Run Prettier over the generated markdown so the output matches the repo's
|
|
315
|
+
* formatting (table alignment, list spacing) and re-running stays a clean no-op.
|
|
316
|
+
* Best-effort: if Prettier isn't available the docs are still valid markdown.
|
|
317
|
+
*/
|
|
318
|
+
function formatGenerated(): void {
|
|
319
|
+
const result = spawnSync(
|
|
320
|
+
"bunx",
|
|
321
|
+
["prettier", "--write", "--log-level", "warn", `${SKILL_DIR}/**/*.md`],
|
|
322
|
+
{ stdio: "inherit", cwd: REPO_ROOT },
|
|
323
|
+
);
|
|
324
|
+
if (result.status !== 0) {
|
|
325
|
+
console.warn("Warning: prettier formatting step skipped (non-zero exit).");
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function main(): Promise<void> {
|
|
330
|
+
const commands = loadCommands();
|
|
331
|
+
const groups = await writeReferences(commands);
|
|
332
|
+
await updateSkillMd(commands, groups);
|
|
333
|
+
formatGenerated();
|
|
334
|
+
console.log(
|
|
335
|
+
`Generated ${groups.length} reference files (${commands.length} commands) and refreshed SKILL.md.`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
await main();
|