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.
Files changed (111) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.claude-plugin/plugin.json +9 -0
  3. package/CHANGELOG.md +807 -0
  4. package/COVERAGE.md +495 -0
  5. package/LICENSE +21 -0
  6. package/MIGRATING.md +220 -0
  7. package/README.md +524 -0
  8. package/package.json +87 -0
  9. package/skills/linear-sdk-cli/SKILL.md +534 -0
  10. package/skills/linear-sdk-cli/references/api.md +27 -0
  11. package/skills/linear-sdk-cli/references/attachment.md +82 -0
  12. package/skills/linear-sdk-cli/references/auth.md +174 -0
  13. package/skills/linear-sdk-cli/references/commands.md +37 -0
  14. package/skills/linear-sdk-cli/references/comment.md +154 -0
  15. package/skills/linear-sdk-cli/references/completion.md +17 -0
  16. package/skills/linear-sdk-cli/references/config.md +99 -0
  17. package/skills/linear-sdk-cli/references/cycle.md +131 -0
  18. package/skills/linear-sdk-cli/references/document.md +180 -0
  19. package/skills/linear-sdk-cli/references/favorite.md +78 -0
  20. package/skills/linear-sdk-cli/references/initiative-update.md +67 -0
  21. package/skills/linear-sdk-cli/references/initiative.md +256 -0
  22. package/skills/linear-sdk-cli/references/issue.md +928 -0
  23. package/skills/linear-sdk-cli/references/label.md +112 -0
  24. package/skills/linear-sdk-cli/references/milestone.md +139 -0
  25. package/skills/linear-sdk-cli/references/notification.md +123 -0
  26. package/skills/linear-sdk-cli/references/open.md +29 -0
  27. package/skills/linear-sdk-cli/references/organization.md +99 -0
  28. package/skills/linear-sdk-cli/references/project-update.md +67 -0
  29. package/skills/linear-sdk-cli/references/project.md +234 -0
  30. package/skills/linear-sdk-cli/references/roadmap.md +150 -0
  31. package/skills/linear-sdk-cli/references/schema.md +21 -0
  32. package/skills/linear-sdk-cli/references/state.md +61 -0
  33. package/skills/linear-sdk-cli/references/team.md +227 -0
  34. package/skills/linear-sdk-cli/references/user.md +103 -0
  35. package/skills/linear-sdk-cli/references/webhook.md +151 -0
  36. package/skills/linear-sdk-cli/references/whoami.md +26 -0
  37. package/skills/linear-sdk-cli/scripts/generate-docs.ts +339 -0
  38. package/src/bin/linear.ts +97 -0
  39. package/src/cli.ts +232 -0
  40. package/src/client.ts +172 -0
  41. package/src/commands/agent-session.ts +121 -0
  42. package/src/commands/api.ts +317 -0
  43. package/src/commands/attach.ts +90 -0
  44. package/src/commands/attachment.ts +74 -0
  45. package/src/commands/comment.ts +225 -0
  46. package/src/commands/completion.ts +69 -0
  47. package/src/commands/cycle.ts +125 -0
  48. package/src/commands/discover.ts +170 -0
  49. package/src/commands/document.ts +211 -0
  50. package/src/commands/favorite.ts +72 -0
  51. package/src/commands/initiative-update.ts +47 -0
  52. package/src/commands/initiative.ts +261 -0
  53. package/src/commands/issue.ts +1263 -0
  54. package/src/commands/label.ts +119 -0
  55. package/src/commands/meta.ts +534 -0
  56. package/src/commands/milestone.ts +163 -0
  57. package/src/commands/notification.ts +110 -0
  58. package/src/commands/open.ts +84 -0
  59. package/src/commands/organization.ts +79 -0
  60. package/src/commands/project-update.ts +47 -0
  61. package/src/commands/project.ts +339 -0
  62. package/src/commands/roadmap.ts +131 -0
  63. package/src/commands/state.ts +58 -0
  64. package/src/commands/team.ts +243 -0
  65. package/src/commands/user.ts +107 -0
  66. package/src/commands/webhook.ts +156 -0
  67. package/src/config.ts +1236 -0
  68. package/src/context.ts +117 -0
  69. package/src/git.ts +174 -0
  70. package/src/index.ts +10 -0
  71. package/src/lib/action.ts +26 -0
  72. package/src/lib/body.ts +172 -0
  73. package/src/lib/errors.ts +195 -0
  74. package/src/lib/introspect.ts +150 -0
  75. package/src/lib/keyring.ts +270 -0
  76. package/src/lib/mutation.ts +53 -0
  77. package/src/lib/open.ts +32 -0
  78. package/src/lib/options.ts +640 -0
  79. package/src/lib/output-shapes.ts +562 -0
  80. package/src/lib/pagination.ts +145 -0
  81. package/src/lib/prompt.ts +91 -0
  82. package/src/lib/resolve.ts +596 -0
  83. package/src/lib/shape.ts +273 -0
  84. package/src/lib/status-update.ts +91 -0
  85. package/src/lib/upload.ts +329 -0
  86. package/src/output/color.ts +52 -0
  87. package/src/output/format.ts +238 -0
  88. package/src/output/markdown.ts +133 -0
  89. package/src/output/pager.ts +131 -0
  90. package/src/output/sanitize.ts +76 -0
  91. package/src/output/table.ts +198 -0
  92. package/src/services/agent-session.ts +289 -0
  93. package/src/services/attachment.ts +198 -0
  94. package/src/services/comment.ts +262 -0
  95. package/src/services/cycle.ts +224 -0
  96. package/src/services/document.ts +357 -0
  97. package/src/services/favorite.ts +147 -0
  98. package/src/services/initiative-update.ts +59 -0
  99. package/src/services/initiative.ts +449 -0
  100. package/src/services/issue.ts +1153 -0
  101. package/src/services/label.ts +193 -0
  102. package/src/services/milestone.ts +262 -0
  103. package/src/services/notification.ts +190 -0
  104. package/src/services/organization.ts +144 -0
  105. package/src/services/project-update.ts +36 -0
  106. package/src/services/project.ts +494 -0
  107. package/src/services/roadmap.ts +177 -0
  108. package/src/services/state.ts +127 -0
  109. package/src/services/team.ts +386 -0
  110. package/src/services/user.ts +137 -0
  111. package/src/services/webhook.ts +203 -0
@@ -0,0 +1,562 @@
1
+ /**
2
+ * What every command prints under `--json`, by command path — the `output`
3
+ * field of `linear commands --json` (TES-610).
4
+ *
5
+ * Why a registry keyed by path, and why here: the row and detail shapes are
6
+ * declared beside the interfaces they describe (`ISSUE_ROW_SHAPE` next to
7
+ * `IssueRow`, …), where `shape<T>()` makes any disagreement with the interface a
8
+ * compile error. Mutation receipts, though, are object literals at the emit
9
+ * site in `src/commands/*` with no type of their own; a shape for one is a
10
+ * second copy of the same keys, and the only thing that can keep the copy
11
+ * honest is running the command — which is exactly what
12
+ * `test/unit/output-shapes.test.ts` does for every path in this table, against
13
+ * the shape declared here. The same test refuses a command that has no entry
14
+ * at all, so a new command cannot ship undocumented.
15
+ *
16
+ * `null` marks a command that never prints anything as JSON on its own (a
17
+ * group whose only job is to hold subcommands); it is omitted from the
18
+ * emitted node. A group that runs a default subcommand carries that
19
+ * subcommand's shape, because `linear issue --json` really does print it.
20
+ */
21
+
22
+ import type { FieldShape, ObjectFields, OutputShape } from "./shape.js";
23
+ import { shape } from "./shape.js";
24
+ import { AGENT_SESSION_DETAIL_SHAPE, AGENT_SESSION_ROW_SHAPE } from "../services/agent-session.js";
25
+ import { ATTACHMENT_ROW_SHAPE } from "../services/attachment.js";
26
+ import { COMMENT_ROW_SHAPE } from "../services/comment.js";
27
+ import { CYCLE_DETAIL_SHAPE, CYCLE_ROW_SHAPE } from "../services/cycle.js";
28
+ import type { DocumentDetail, DocumentRow, DocumentTargets } from "../services/document.js";
29
+ import { FAVORITE_ROW_SHAPE } from "../services/favorite.js";
30
+ import {
31
+ INITIATIVE_DETAIL_SHAPE,
32
+ INITIATIVE_ROW_SHAPE,
33
+ PROJECT_LINK_SHAPE,
34
+ } from "../services/initiative.js";
35
+ import { ISSUE_DETAIL_SHAPE, ISSUE_ROW_SHAPE } from "../services/issue.js";
36
+ import { LABEL_ROW_SHAPE } from "../services/label.js";
37
+ import { MILESTONE_DETAIL_SHAPE, MILESTONE_ROW_SHAPE } from "../services/milestone.js";
38
+ import { MARK_ALL_ITEM_SHAPE, NOTIFICATION_ROW_SHAPE } from "../services/notification.js";
39
+ import {
40
+ INVITE_ROW_SHAPE,
41
+ ORGANIZATION_DETAIL_SHAPE,
42
+ ORGANIZATION_MEMBER_ROW_SHAPE,
43
+ } from "../services/organization.js";
44
+ import {
45
+ PROJECT_DETAIL_SHAPE,
46
+ PROJECT_MILESTONE_ROW_SHAPE,
47
+ PROJECT_ROW_SHAPE,
48
+ } from "../services/project.js";
49
+ import { ROADMAP_DETAIL_SHAPE, ROADMAP_ROW_SHAPE } from "../services/roadmap.js";
50
+ import { STATE_DETAIL_SHAPE, STATE_ROW_SHAPE } from "../services/state.js";
51
+ import {
52
+ TEAM_CYCLE_ROW_SHAPE,
53
+ TEAM_DETAIL_SHAPE,
54
+ TEAM_LABEL_ROW_SHAPE,
55
+ TEAM_MEMBER_ROW_SHAPE,
56
+ TEAM_ROW_SHAPE,
57
+ TEAM_STATE_ROW_SHAPE,
58
+ } from "../services/team.js";
59
+ import { USER_DETAIL_SHAPE, USER_ROW_SHAPE } from "../services/user.js";
60
+ import { WEBHOOK_DETAIL_SHAPE, WEBHOOK_ROW_SHAPE } from "../services/webhook.js";
61
+ import type { UpdateRow } from "../lib/status-update.js";
62
+ import type { SettingOrigin, SettingOrigins } from "../config.js";
63
+ import type { CommandNode } from "./introspect.js";
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Builders
67
+ // ---------------------------------------------------------------------------
68
+
69
+ const list = (fields: ObjectFields, extra: Partial<OutputShape> = {}): OutputShape => ({
70
+ kind: "list",
71
+ fields,
72
+ ...extra,
73
+ });
74
+ const object = (fields: ObjectFields, extra: Partial<OutputShape> = {}): OutputShape => ({
75
+ kind: "object",
76
+ fields,
77
+ ...extra,
78
+ });
79
+ const receipt = (fields: ObjectFields, extra: Partial<OutputShape> = {}): OutputShape => ({
80
+ kind: "receipt",
81
+ fields,
82
+ ...extra,
83
+ });
84
+ /** A group whose default subcommand's output this is. */
85
+ const runs = (sub: string, out: OutputShape): OutputShape => ({
86
+ ...out,
87
+ note: `runs \`${sub}\` by default${out.note ? `; ${out.note}` : ""}`,
88
+ });
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Shapes that have no service interface of their own, or whose service file
92
+ // is not the place for them.
93
+ // ---------------------------------------------------------------------------
94
+
95
+ /** Documents attach to one of six targets; the row and the detail share them. */
96
+ const DOCUMENT_TARGETS_SHAPE = shape<DocumentTargets>({
97
+ project: { nullable: { id: "string", name: "string" } },
98
+ issue: { nullable: { id: "string", identifier: "string" } },
99
+ initiative: { nullable: { id: "string", name: "string" } },
100
+ team: { nullable: { id: "string", key: "string", name: "string" } },
101
+ cycle: { nullable: { id: "string", number: "number", name: "string|null" } },
102
+ release: { nullable: { id: "string", name: "string", version: "string|null" } },
103
+ });
104
+ export const DOCUMENT_ROW_SHAPE = shape<DocumentRow>({
105
+ ...DOCUMENT_TARGETS_SHAPE,
106
+ id: "string",
107
+ title: "string",
108
+ url: "string",
109
+ updatedAt: "string",
110
+ });
111
+ export const DOCUMENT_DETAIL_SHAPE = shape<DocumentDetail>({
112
+ ...DOCUMENT_TARGETS_SHAPE,
113
+ id: "string",
114
+ title: "string",
115
+ content: "string|null",
116
+ url: "string",
117
+ slugId: "string",
118
+ icon: "string|null",
119
+ color: "string|null",
120
+ createdAt: "string",
121
+ updatedAt: "string",
122
+ creator: { nullable: { id: "string", displayName: "string" } },
123
+ });
124
+
125
+ /** A project/initiative status update, as `list` rows it and `create` returns it (+ url). */
126
+ export const UPDATE_ROW_SHAPE = shape<UpdateRow>({
127
+ id: "string",
128
+ createdAt: "string",
129
+ user: "string",
130
+ body: "string",
131
+ health: "string|null",
132
+ });
133
+ const UPDATE_RECEIPT_SHAPE = shape<UpdateRow & { url: string }>({
134
+ ...UPDATE_ROW_SHAPE,
135
+ url: "string",
136
+ });
137
+
138
+ /** `whoami` / `auth whoami`. */
139
+ const WHOAMI_SHAPE: ObjectFields = {
140
+ id: "string",
141
+ name: "string",
142
+ displayName: "string",
143
+ email: "string",
144
+ admin: "boolean",
145
+ organization: { id: "string", name: "string", urlKey: "string" },
146
+ };
147
+
148
+ const SETTING_ORIGIN_SHAPE = shape<SettingOrigin>({
149
+ source: "string",
150
+ "path?": "string",
151
+ "key?": "string",
152
+ });
153
+ const SETTING_ORIGINS_SHAPE = shape<SettingOrigins>({
154
+ team: SETTING_ORIGIN_SHAPE,
155
+ workspace: SETTING_ORIGIN_SHAPE,
156
+ sort: SETTING_ORIGIN_SHAPE,
157
+ });
158
+
159
+ /** `config show`: every resolved setting, redacted, with its provenance. */
160
+ const CONFIG_SHOW_SHAPE: ObjectFields = {
161
+ apiKey: "string",
162
+ apiKeySource: "string",
163
+ credentialWorkspace: "string|null",
164
+ team: "string|null",
165
+ workspace: "string|null",
166
+ sort: "string",
167
+ origins: SETTING_ORIGINS_SHAPE,
168
+ userConfigPath: "string",
169
+ projectConfigPath: "string|null",
170
+ globalConfigPath: "string|null",
171
+ };
172
+
173
+ /** A node of `linear commands --json` — this very table, described in its own terms. */
174
+ export const COMMAND_NODE_SHAPE = shape<CommandNode>({
175
+ path: "string",
176
+ description: "string",
177
+ aliases: ["string"],
178
+ arguments: [{ name: "string", required: "boolean", variadic: "boolean" }],
179
+ options: [
180
+ {
181
+ flags: "string",
182
+ description: "string",
183
+ attribute: "string",
184
+ valueRequired: "boolean",
185
+ valueOptional: "boolean",
186
+ variadic: "boolean",
187
+ "choices?": ["string"],
188
+ "defaultValue?": "unknown",
189
+ global: "boolean",
190
+ applicable: "boolean",
191
+ },
192
+ ],
193
+ "output?": {
194
+ kind: "string",
195
+ "fields?": "object",
196
+ "note?": "string",
197
+ "variants?": "object",
198
+ },
199
+ });
200
+
201
+ // Receipts shared by several commands.
202
+ const ISSUE_REF: ObjectFields = { id: "string", identifier: "string" };
203
+ const ISSUE_URL_RECEIPT: ObjectFields = { ...ISSUE_REF, url: "string" };
204
+ const NAMED_URL_RECEIPT: ObjectFields = { id: "string", name: "string", url: "string" };
205
+ const NAMED_DELETED: ObjectFields = { id: "string", name: "string", deleted: "boolean" };
206
+ const NAMED_ARCHIVED: ObjectFields = { id: "string", name: "string", archived: "boolean" };
207
+ const UPLOADED_FILE: ObjectFields = {
208
+ filename: "string",
209
+ assetUrl: "string",
210
+ contentType: "string",
211
+ size: "number",
212
+ };
213
+ const COMMENT_ADD_RECEIPT: ObjectFields = {
214
+ id: "string",
215
+ issue: "string",
216
+ url: "string",
217
+ "attachments?": [UPLOADED_FILE],
218
+ };
219
+ const COMMENT_UPDATE_RECEIPT: ObjectFields = { id: "string", url: "string" };
220
+ const COMMENT_DELETE_RECEIPT: ObjectFields = { id: "string", deleted: "boolean" };
221
+ const WEBHOOK_RECEIPT: ObjectFields = {
222
+ id: "string",
223
+ url: "string|null",
224
+ enabled: "boolean",
225
+ resourceTypes: ["string"],
226
+ };
227
+ const RELATION_ROW: ObjectFields = { type: "string", issue: "string", title: "string" };
228
+
229
+ const ISSUE_VIEW = object(ISSUE_DETAIL_SHAPE, {
230
+ variants: {
231
+ "--web": receipt({ ...ISSUE_URL_RECEIPT, opened: "boolean" }),
232
+ "--app": receipt({ ...ISSUE_URL_RECEIPT, opened: "boolean" }),
233
+ },
234
+ });
235
+ const ISSUE_LIST = list(ISSUE_ROW_SHAPE);
236
+ const CYCLE_VIEW = object(CYCLE_DETAIL_SHAPE);
237
+ const USER_VIEW = object(USER_DETAIL_SHAPE);
238
+ const WHOAMI = object(WHOAMI_SHAPE);
239
+ const RAW_API: OutputShape = {
240
+ kind: "raw",
241
+ note: "the response `data` as the API returned it (`--raw`: {data, extensions}); `--paginate` prints one array of every page's nodes",
242
+ };
243
+
244
+ // ---------------------------------------------------------------------------
245
+ // The table. One entry per command path (`linear commands --json | jq '.[].path'`).
246
+ // ---------------------------------------------------------------------------
247
+
248
+ export const OUTPUT_SHAPES: Record<string, OutputShape | null> = {
249
+ api: RAW_API,
250
+
251
+ attachment: null,
252
+ "attachment create": receipt({ id: "string", title: "string", url: "string" }),
253
+ "attachment delete": receipt({ id: "string", title: "string", deleted: "boolean" }),
254
+ "attachment list": list(ATTACHMENT_ROW_SHAPE),
255
+
256
+ auth: null,
257
+ "auth adopt": receipt({
258
+ success: "boolean",
259
+ workspace: "string",
260
+ user: { id: "string", name: "string", email: "string" },
261
+ storage: "string",
262
+ path: "string",
263
+ }),
264
+ "auth default": receipt({ success: "boolean", default_workspace: "string", path: "string" }),
265
+ "auth list": list({ slug: "string", isDefault: "boolean", storage: "string" }),
266
+ "auth login": receipt({
267
+ success: "boolean",
268
+ workspace: "string",
269
+ user: { id: "string", name: "string", email: "string" },
270
+ storage: "string",
271
+ path: "string",
272
+ }),
273
+ "auth logout": receipt({ success: "boolean", workspace: "string", removed: "boolean" }),
274
+ "auth migrate": receipt({ success: "boolean", migrated: ["string"], path: "string" }),
275
+ "auth status": object({
276
+ authenticated: "boolean",
277
+ source: "string",
278
+ workspace: "string|null",
279
+ key: "string",
280
+ keyring: "string|null",
281
+ }),
282
+ "auth token": object({ apiKey: "string", workspace: "string|null" }),
283
+ "auth whoami": WHOAMI,
284
+
285
+ commands: list(COMMAND_NODE_SHAPE, {
286
+ note: "`output` is absent on a group that only holds subcommands",
287
+ variants: { "[path]": object(COMMAND_NODE_SHAPE) },
288
+ }),
289
+
290
+ comment: null,
291
+ "comment add": receipt(COMMENT_ADD_RECEIPT, { note: "`attachments` only with --attach" }),
292
+ "comment delete": receipt(COMMENT_DELETE_RECEIPT),
293
+ "comment list": list(COMMENT_ROW_SHAPE),
294
+ "comment reply": receipt({ id: "string", parent: "string", issue: "string|null", url: "string" }),
295
+ "comment resolve": receipt({ id: "string", resolved: "boolean" }),
296
+ "comment unresolve": receipt({ id: "string", resolved: "boolean" }),
297
+ "comment update": receipt(COMMENT_UPDATE_RECEIPT),
298
+
299
+ completion: { kind: "none", note: "always prints a shell script; --json has no effect" },
300
+
301
+ config: runs("config show", object(CONFIG_SHOW_SHAPE)),
302
+ "config init": receipt({ success: "boolean", path: "string", team: "string", "sort?": "string" }),
303
+ "config set": receipt({ success: "boolean", path: "string", key: "string", value: "string" }),
304
+ "config show": object(CONFIG_SHOW_SHAPE),
305
+
306
+ cycle: null,
307
+ "cycle create": receipt({ id: "string", number: "number" }),
308
+ "cycle current": CYCLE_VIEW,
309
+ "cycle list": list(CYCLE_ROW_SHAPE),
310
+ "cycle update": receipt({ id: "string", number: "number" }),
311
+ "cycle view": CYCLE_VIEW,
312
+
313
+ document: runs("document view", object(DOCUMENT_DETAIL_SHAPE)),
314
+ "document create": receipt({ id: "string", title: "string", url: "string" }),
315
+ "document delete": receipt({ id: "string", title: "string", deleted: "boolean" }),
316
+ "document list": list(DOCUMENT_ROW_SHAPE),
317
+ "document update": receipt({ id: "string", title: "string", url: "string" }),
318
+ "document view": object(DOCUMENT_DETAIL_SHAPE),
319
+
320
+ favorite: null,
321
+ "favorite add": receipt({ id: "string", type: "string" }),
322
+ "favorite list": list(FAVORITE_ROW_SHAPE),
323
+ "favorite remove": receipt({ id: "string", removed: "boolean" }),
324
+
325
+ initiative: runs("initiative view", object(INITIATIVE_DETAIL_SHAPE)),
326
+ "initiative add-project": receipt(PROJECT_LINK_SHAPE),
327
+ "initiative archive": receipt(NAMED_ARCHIVED),
328
+ "initiative create": receipt(NAMED_URL_RECEIPT),
329
+ "initiative delete": receipt(NAMED_DELETED),
330
+ "initiative list": list(INITIATIVE_ROW_SHAPE),
331
+ "initiative remove-project": receipt({ ...PROJECT_LINK_SHAPE, removed: "boolean" }),
332
+ "initiative unarchive": receipt(NAMED_ARCHIVED),
333
+ "initiative update": receipt(NAMED_URL_RECEIPT),
334
+ "initiative view": object(INITIATIVE_DETAIL_SHAPE),
335
+
336
+ "initiative-update": null,
337
+ "initiative-update create": receipt(UPDATE_RECEIPT_SHAPE),
338
+ "initiative-update list": list(UPDATE_ROW_SHAPE),
339
+
340
+ issue: runs("issue view", ISSUE_VIEW),
341
+ "issue agent-session": null,
342
+ "issue agent-session list": list(AGENT_SESSION_ROW_SHAPE),
343
+ "issue agent-session view": object(AGENT_SESSION_DETAIL_SHAPE),
344
+ "issue archive": receipt(
345
+ { ...ISSUE_REF, archived: "boolean" },
346
+ {
347
+ variants: {
348
+ "--bulk": receipt({
349
+ action: "string",
350
+ results: [
351
+ {
352
+ input: "string",
353
+ "id?": "string",
354
+ "identifier?": "string",
355
+ "archived?": "boolean",
356
+ "deleted?": "boolean",
357
+ "error?": { message: "string", code: "string" },
358
+ },
359
+ ],
360
+ succeeded: "number",
361
+ failed: "number",
362
+ }),
363
+ },
364
+ },
365
+ ),
366
+ "issue assign": receipt(ISSUE_REF),
367
+ "issue attach": list(
368
+ {
369
+ id: "string",
370
+ title: "string",
371
+ url: "string",
372
+ assetUrl: "string",
373
+ contentType: "string",
374
+ size: "number",
375
+ "comment?": { id: "string", url: "string" },
376
+ },
377
+ { note: "one row per uploaded file; `comment` is on every row when --comment posted one" },
378
+ ),
379
+ "issue branch": receipt({ branch: "string" }),
380
+ "issue comment": receipt(
381
+ { id: "string", issue: "string" },
382
+ { note: "the bare form adds a comment; add/list/update/delete are subcommands" },
383
+ ),
384
+ "issue comment add": receipt(COMMENT_ADD_RECEIPT, { note: "`attachments` only with --attach" }),
385
+ "issue comment delete": receipt(COMMENT_DELETE_RECEIPT),
386
+ "issue comment list": list(COMMENT_ROW_SHAPE),
387
+ "issue comment update": receipt(COMMENT_UPDATE_RECEIPT),
388
+ "issue comments": list(COMMENT_ROW_SHAPE),
389
+ "issue create": receipt(ISSUE_URL_RECEIPT, {
390
+ variants: {
391
+ "--start": receipt({
392
+ ...ISSUE_URL_RECEIPT,
393
+ branch: "string",
394
+ checkedOut: "boolean",
395
+ stateChanged: "boolean",
396
+ }),
397
+ },
398
+ }),
399
+ "issue delete": receipt(
400
+ { ...ISSUE_REF, deleted: "boolean" },
401
+ {
402
+ variants: {
403
+ "--bulk": receipt({
404
+ action: "string",
405
+ results: [
406
+ {
407
+ input: "string",
408
+ "id?": "string",
409
+ "identifier?": "string",
410
+ "archived?": "boolean",
411
+ "deleted?": "boolean",
412
+ "error?": { message: "string", code: "string" },
413
+ },
414
+ ],
415
+ succeeded: "number",
416
+ failed: "number",
417
+ }),
418
+ },
419
+ },
420
+ ),
421
+ "issue describe": receipt(
422
+ { identifier: "string", title: "string", url: "string", trailer: "string", message: "string" },
423
+ {
424
+ note: "`trailer` is the magic-word phrase (Fixes TES-1); `message` the full commit text as printed",
425
+ },
426
+ ),
427
+ "issue id": receipt({ id: "string" }, { note: "`id` is the identifier (TES-123), not the UUID" }),
428
+ "issue label": receipt(ISSUE_REF),
429
+ "issue list": ISSUE_LIST,
430
+ "issue mine": ISSUE_LIST,
431
+ "issue pull-request": receipt(
432
+ { url: "string", identifier: "string", title: "string" },
433
+ { variants: { "--web": receipt({ web: "boolean", identifier: "string" }) } },
434
+ ),
435
+ "issue relation": receipt(
436
+ {
437
+ issueId: "string",
438
+ issueIdentifier: "string",
439
+ otherId: "string",
440
+ otherIdentifier: "string",
441
+ type: "string",
442
+ op: "string",
443
+ },
444
+ { note: "for op=add|remove", variants: { "op=list": list(RELATION_ROW) } },
445
+ ),
446
+ "issue search": ISSUE_LIST,
447
+ "issue start": receipt({
448
+ ...ISSUE_REF,
449
+ branch: "string",
450
+ checkedOut: "boolean",
451
+ stateChanged: "boolean",
452
+ }),
453
+ "issue state": receipt(ISSUE_REF),
454
+ "issue subscribe": receipt({ ...ISSUE_REF, subscribed: "boolean" }),
455
+ "issue title": receipt({ title: "string" }),
456
+ "issue unarchive": receipt({ ...ISSUE_REF, archived: "boolean" }),
457
+ "issue unsubscribe": receipt({ ...ISSUE_REF, subscribed: "boolean" }),
458
+ "issue update": receipt(ISSUE_URL_RECEIPT),
459
+ "issue url": receipt({ url: "string" }),
460
+ "issue view": ISSUE_VIEW,
461
+
462
+ label: null,
463
+ "label create": receipt({ id: "string", name: "string", color: "string" }),
464
+ "label delete": receipt(NAMED_DELETED),
465
+ "label list": list(LABEL_ROW_SHAPE),
466
+ "label update": receipt({ id: "string", name: "string", color: "string" }),
467
+
468
+ milestone: null,
469
+ "milestone create": receipt({ id: "string", name: "string" }),
470
+ "milestone delete": receipt(NAMED_DELETED),
471
+ "milestone list": list(MILESTONE_ROW_SHAPE),
472
+ "milestone update": receipt({ id: "string", name: "string" }),
473
+ "milestone view": object(MILESTONE_DETAIL_SHAPE),
474
+
475
+ notification: null,
476
+ "notification archive": receipt({ id: "string", archived: "boolean" }),
477
+ "notification list": list(NOTIFICATION_ROW_SHAPE),
478
+ "notification read": receipt({ id: "string", read: "boolean" }),
479
+ "notification read-all": receipt({
480
+ success: "boolean",
481
+ count: "number",
482
+ attempted: "number",
483
+ failed: [MARK_ALL_ITEM_SHAPE],
484
+ }),
485
+ "notification snooze": receipt({ id: "string", snoozedUntilAt: "string" }),
486
+ "notification unread": receipt({ id: "string", read: "boolean" }),
487
+
488
+ open: receipt({ target: "string", url: "string", label: "string", opened: "boolean" }),
489
+
490
+ organization: runs("organization view", object(ORGANIZATION_DETAIL_SHAPE)),
491
+ "organization invites": list(INVITE_ROW_SHAPE),
492
+ "organization members": list(ORGANIZATION_MEMBER_ROW_SHAPE),
493
+ "organization view": object(ORGANIZATION_DETAIL_SHAPE),
494
+
495
+ project: runs("project view", object(PROJECT_DETAIL_SHAPE)),
496
+ "project archive": receipt(NAMED_ARCHIVED),
497
+ "project create": receipt(NAMED_URL_RECEIPT),
498
+ "project delete": receipt(NAMED_DELETED),
499
+ "project list": list(PROJECT_ROW_SHAPE),
500
+ "project milestones": list(PROJECT_MILESTONE_ROW_SHAPE),
501
+ "project update": receipt(NAMED_URL_RECEIPT),
502
+ "project view": object(PROJECT_DETAIL_SHAPE),
503
+
504
+ "project-update": null,
505
+ "project-update create": receipt(UPDATE_RECEIPT_SHAPE),
506
+ "project-update list": list(UPDATE_ROW_SHAPE),
507
+
508
+ roadmap: runs("roadmap view", object(ROADMAP_DETAIL_SHAPE)),
509
+ "roadmap create": receipt(NAMED_URL_RECEIPT),
510
+ "roadmap delete": receipt(NAMED_DELETED),
511
+ "roadmap list": list(ROADMAP_ROW_SHAPE),
512
+ "roadmap update": receipt(NAMED_URL_RECEIPT),
513
+ "roadmap view": object(ROADMAP_DETAIL_SHAPE),
514
+
515
+ schema: {
516
+ kind: "raw",
517
+ note: "the GraphQL introspection result ({__schema: …}); with -o <file> it is written there and stdout stays empty",
518
+ },
519
+
520
+ state: null,
521
+ "state list": list(STATE_ROW_SHAPE),
522
+ "state view": object(STATE_DETAIL_SHAPE),
523
+
524
+ team: null,
525
+ "team create": receipt(TEAM_ROW_SHAPE),
526
+ "team cycles": list(TEAM_CYCLE_ROW_SHAPE),
527
+ "team delete": receipt({
528
+ ...TEAM_ROW_SHAPE,
529
+ deleted: "boolean",
530
+ movedIssues: "number",
531
+ movedTo: { nullable: TEAM_ROW_SHAPE },
532
+ }),
533
+ "team id": receipt(TEAM_ROW_SHAPE),
534
+ "team labels": list(TEAM_LABEL_ROW_SHAPE),
535
+ "team list": list(TEAM_ROW_SHAPE),
536
+ "team members": list(TEAM_MEMBER_ROW_SHAPE),
537
+ "team states": list(TEAM_STATE_ROW_SHAPE),
538
+ "team update": receipt(TEAM_ROW_SHAPE),
539
+ "team view": object(TEAM_DETAIL_SHAPE),
540
+
541
+ user: null,
542
+ "user list": list(USER_ROW_SHAPE),
543
+ "user me": USER_VIEW,
544
+ "user view": USER_VIEW,
545
+
546
+ webhook: runs("webhook view", object(WEBHOOK_DETAIL_SHAPE)),
547
+ "webhook create": receipt(WEBHOOK_RECEIPT),
548
+ "webhook delete": receipt({ id: "string", deleted: "boolean" }),
549
+ "webhook list": list(WEBHOOK_ROW_SHAPE),
550
+ "webhook update": receipt(WEBHOOK_RECEIPT),
551
+ "webhook view": object(WEBHOOK_DETAIL_SHAPE),
552
+
553
+ whoami: WHOAMI,
554
+ };
555
+
556
+ /** The declared output of one command path, or undefined when it prints no JSON of its own. */
557
+ export function outputShapeOf(path: string): OutputShape | undefined {
558
+ return OUTPUT_SHAPES[path] ?? undefined;
559
+ }
560
+
561
+ // Re-exported so the tests (and `commands`) can speak of a field shape without importing shape.ts.
562
+ export type { FieldShape };