linear-cli-agents 0.5.1 → 0.7.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 (44) hide show
  1. package/README.md +87 -1
  2. package/dist/commands/cycles/current.d.ts +11 -0
  3. package/dist/commands/cycles/current.js +104 -0
  4. package/dist/commands/cycles/get.d.ts +12 -0
  5. package/dist/commands/cycles/get.js +86 -0
  6. package/dist/commands/cycles/list.d.ts +16 -0
  7. package/dist/commands/cycles/list.js +147 -0
  8. package/dist/commands/documents/create.d.ts +13 -0
  9. package/dist/commands/documents/create.js +68 -0
  10. package/dist/commands/documents/delete.d.ts +9 -0
  11. package/dist/commands/documents/delete.js +32 -0
  12. package/dist/commands/documents/get.d.ts +12 -0
  13. package/dist/commands/documents/get.js +79 -0
  14. package/dist/commands/documents/list.d.ts +12 -0
  15. package/dist/commands/documents/list.js +105 -0
  16. package/dist/commands/documents/update.d.ts +16 -0
  17. package/dist/commands/documents/update.js +75 -0
  18. package/dist/commands/info.js +209 -3
  19. package/dist/commands/initiatives/archive.d.ts +12 -0
  20. package/dist/commands/initiatives/archive.js +44 -0
  21. package/dist/commands/initiatives/create.d.ts +15 -0
  22. package/dist/commands/initiatives/create.js +84 -0
  23. package/dist/commands/initiatives/delete.d.ts +9 -0
  24. package/dist/commands/initiatives/delete.js +32 -0
  25. package/dist/commands/initiatives/get.d.ts +12 -0
  26. package/dist/commands/initiatives/get.js +90 -0
  27. package/dist/commands/initiatives/list.d.ts +11 -0
  28. package/dist/commands/initiatives/list.js +135 -0
  29. package/dist/commands/initiatives/update.d.ts +18 -0
  30. package/dist/commands/initiatives/update.js +90 -0
  31. package/dist/commands/issues/bulk-update.d.ts +2 -0
  32. package/dist/commands/issues/bulk-update.js +10 -0
  33. package/dist/commands/issues/create.d.ts +2 -0
  34. package/dist/commands/issues/create.js +10 -0
  35. package/dist/commands/issues/get.d.ts +1 -0
  36. package/dist/commands/issues/get.js +19 -1
  37. package/dist/commands/issues/update.d.ts +2 -0
  38. package/dist/commands/issues/update.js +12 -0
  39. package/dist/commands/upload.d.ts +13 -0
  40. package/dist/commands/upload.js +117 -0
  41. package/dist/lib/formatter.d.ts +4 -0
  42. package/dist/lib/formatter.js +15 -0
  43. package/oclif.manifest.json +1323 -405
  44. package/package.json +10 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@ A CLI for interacting with [Linear](https://linear.app), designed for LLMs and a
14
14
  - **Configurable defaults**: Set default team to skip `--team-id` on every command
15
15
  - **Bulk operations**: Update multiple issues at once with `bulk-update` and `bulk-label`
16
16
  - **Schema introspection**: Discover available operations programmatically
17
- - **Full CRUD**: Issues, projects, labels, comments, templates, milestones
17
+ - **Full CRUD**: Issues, projects, labels, comments, templates, milestones, documents, initiatives
18
18
  - **Issue relations**: Manage blocks, duplicates, and related issues
19
19
  - **Project management**: Projects, milestones, and status updates
20
20
  - **Team management**: List and browse teams, states, users
@@ -106,9 +106,12 @@ linear issues list --format plain
106
106
  # Get a specific issue
107
107
  linear issues get ENG-123
108
108
  linear issues get ENG-123 --format table
109
+ linear issues get ENG-123 --with-attachments # Include linked PRs, commits
109
110
 
110
111
  # Create an issue
111
112
  linear issues create --title "Bug fix" --team-id <team-id>
113
+ linear issues create --title "Task" --team-id <team-id> --due-date 2024-12-31
114
+ linear issues create --title "Sprint work" --team-id <team-id> --cycle-id <cycle-id>
112
115
  linear issues create --input '{"title":"Feature","teamId":"xxx","priority":2}'
113
116
 
114
117
  # Update an issue
@@ -269,6 +272,89 @@ linear states list
269
272
  linear states list --team ENG
270
273
  ```
271
274
 
275
+ ### Cycles (Sprints)
276
+
277
+ ```bash
278
+ # List all cycles
279
+ linear cycles list
280
+ linear cycles list --team ENG
281
+
282
+ # Filter by status
283
+ linear cycles list --active # Currently running
284
+ linear cycles list --upcoming # Future cycles
285
+ linear cycles list --completed # Past cycles
286
+
287
+ # Get current cycle for a team
288
+ linear cycles current --team ENG
289
+
290
+ # Get cycle details
291
+ linear cycles get CYCLE_ID
292
+ ```
293
+
294
+ ### Documents
295
+
296
+ ```bash
297
+ # List documents
298
+ linear documents list
299
+
300
+ # Get document details
301
+ linear documents get DOCUMENT_ID
302
+
303
+ # Create a document
304
+ linear documents create --title "Meeting Notes"
305
+ linear documents create --title "Specs" --content "# Overview\n\nDetails here..."
306
+ linear documents create --title "Project Doc" --project-id PROJECT_ID
307
+
308
+ # Update a document
309
+ linear documents update DOCUMENT_ID --title "New Title"
310
+ linear documents update DOCUMENT_ID --content "Updated content"
311
+
312
+ # Delete a document
313
+ linear documents delete DOCUMENT_ID
314
+ ```
315
+
316
+ ### Initiatives
317
+
318
+ ```bash
319
+ # List initiatives
320
+ linear initiatives list
321
+ linear initiatives list --status Active
322
+ linear initiatives list --status Completed
323
+
324
+ # Get initiative details
325
+ linear initiatives get INITIATIVE_ID
326
+
327
+ # Create an initiative
328
+ linear initiatives create --name "Q1 Goals"
329
+ linear initiatives create --name "Product Launch" --status Active --target-date 2024-12-31
330
+
331
+ # Update an initiative
332
+ linear initiatives update INITIATIVE_ID --name "New Name"
333
+ linear initiatives update INITIATIVE_ID --status Completed
334
+
335
+ # Archive/unarchive an initiative
336
+ linear initiatives archive INITIATIVE_ID
337
+ linear initiatives archive INITIATIVE_ID --unarchive
338
+
339
+ # Delete an initiative
340
+ linear initiatives delete INITIATIVE_ID
341
+ ```
342
+
343
+ ### Upload Files
344
+
345
+ ```bash
346
+ # Upload a file and get the asset URL
347
+ linear upload ./screenshot.png
348
+ linear upload ./document.pdf
349
+
350
+ # Output as markdown (for embedding in descriptions)
351
+ linear upload ./image.png --markdown
352
+ # Returns: ![image.png](https://linear-uploads.s3.amazonaws.com/...)
353
+
354
+ # Specify content type
355
+ linear upload ./file.dat --content-type application/octet-stream
356
+ ```
357
+
272
358
  ### Users
273
359
 
274
360
  ```bash
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CyclesCurrent extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'team-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ team: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,104 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { success, print, printItem } from '../../lib/output.js';
4
+ import { handleError, CliError, ErrorCodes } from '../../lib/errors.js';
5
+ export default class CyclesCurrent extends Command {
6
+ static description = 'Get the current active cycle for a team';
7
+ static examples = [
8
+ '<%= config.bin %> cycles current --team ENG',
9
+ '<%= config.bin %> cycles current --team-id TEAM_ID',
10
+ '<%= config.bin %> cycles current --team ENG --format table',
11
+ ];
12
+ static flags = {
13
+ format: Flags.string({
14
+ char: 'F',
15
+ description: 'Output format',
16
+ options: ['json', 'table', 'plain'],
17
+ default: 'json',
18
+ }),
19
+ 'team-id': Flags.string({
20
+ description: 'Team ID',
21
+ exclusive: ['team'],
22
+ }),
23
+ team: Flags.string({
24
+ description: 'Team key (e.g., ENG)',
25
+ exclusive: ['team-id'],
26
+ }),
27
+ };
28
+ async run() {
29
+ try {
30
+ const { flags } = await this.parse(CyclesCurrent);
31
+ const format = flags.format;
32
+ const client = getClient();
33
+ if (!flags['team-id'] && !flags.team) {
34
+ throw new CliError(ErrorCodes.MISSING_REQUIRED_FIELD, 'Team is required. Use --team or --team-id');
35
+ }
36
+ // Build filter for active cycle
37
+ const now = new Date();
38
+ const filter = {
39
+ startsAt: { lte: now },
40
+ endsAt: { gte: now },
41
+ };
42
+ if (flags['team-id']) {
43
+ filter.team = { id: { eq: flags['team-id'] } };
44
+ }
45
+ else if (flags.team) {
46
+ filter.team = { key: { eq: flags.team } };
47
+ }
48
+ const cycles = await client.cycles({
49
+ first: 1,
50
+ filter,
51
+ });
52
+ if (cycles.nodes.length === 0) {
53
+ throw new CliError(ErrorCodes.NOT_FOUND, 'No active cycle found for this team');
54
+ }
55
+ const cycle = cycles.nodes[0];
56
+ const [team, issues] = await Promise.all([cycle.team, cycle.issues()]);
57
+ const issuesSummary = {
58
+ total: issues.nodes.length,
59
+ completed: issues.nodes.filter((i) => i.completedAt).length,
60
+ };
61
+ const data = {
62
+ id: cycle.id,
63
+ number: cycle.number,
64
+ name: cycle.name ?? null,
65
+ description: cycle.description ?? null,
66
+ startsAt: cycle.startsAt,
67
+ endsAt: cycle.endsAt,
68
+ progress: cycle.progress,
69
+ team: team
70
+ ? {
71
+ id: team.id,
72
+ key: team.key,
73
+ name: team.name,
74
+ }
75
+ : null,
76
+ issues: issuesSummary,
77
+ daysRemaining: Math.ceil((new Date(cycle.endsAt).getTime() - now.getTime()) / (1000 * 60 * 60 * 24)),
78
+ };
79
+ if (format === 'json') {
80
+ print(success(data));
81
+ }
82
+ else if (format === 'table') {
83
+ printItem({
84
+ id: data.id,
85
+ number: data.number,
86
+ name: data.name ?? 'Unnamed',
87
+ team: data.team?.key ?? 'N/A',
88
+ startsAt: data.startsAt,
89
+ endsAt: data.endsAt,
90
+ progress: `${Math.round(data.progress * 100)}%`,
91
+ issues: `${issuesSummary.completed}/${issuesSummary.total} completed`,
92
+ daysRemaining: `${data.daysRemaining} days`,
93
+ }, format);
94
+ }
95
+ else {
96
+ console.log(data.id);
97
+ }
98
+ }
99
+ catch (err) {
100
+ handleError(err);
101
+ this.exit(1);
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CyclesGet extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,86 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { success, print, printItem } from '../../lib/output.js';
4
+ import { handleError, CliError, ErrorCodes } from '../../lib/errors.js';
5
+ export default class CyclesGet extends Command {
6
+ static description = 'Get cycle (sprint) details';
7
+ static examples = [
8
+ '<%= config.bin %> cycles get CYCLE_ID',
9
+ '<%= config.bin %> cycles get CYCLE_ID --format table',
10
+ ];
11
+ static args = {
12
+ id: Args.string({
13
+ description: 'Cycle ID',
14
+ required: true,
15
+ }),
16
+ };
17
+ static flags = {
18
+ format: Flags.string({
19
+ char: 'F',
20
+ description: 'Output format',
21
+ options: ['json', 'table', 'plain'],
22
+ default: 'json',
23
+ }),
24
+ };
25
+ async run() {
26
+ try {
27
+ const { args, flags } = await this.parse(CyclesGet);
28
+ const format = flags.format;
29
+ const client = getClient();
30
+ const cycle = await client.cycle(args.id);
31
+ if (!cycle) {
32
+ throw new CliError(ErrorCodes.NOT_FOUND, `Cycle ${args.id} not found`);
33
+ }
34
+ const [team, issues] = await Promise.all([cycle.team, cycle.issues()]);
35
+ const issuesSummary = {
36
+ total: issues.nodes.length,
37
+ completed: issues.nodes.filter((i) => i.completedAt).length,
38
+ };
39
+ const data = {
40
+ id: cycle.id,
41
+ number: cycle.number,
42
+ name: cycle.name ?? null,
43
+ description: cycle.description ?? null,
44
+ startsAt: cycle.startsAt,
45
+ endsAt: cycle.endsAt,
46
+ completedAt: cycle.completedAt ?? null,
47
+ progress: cycle.progress,
48
+ scopeHistory: cycle.scopeHistory,
49
+ completedScopeHistory: cycle.completedScopeHistory,
50
+ team: team
51
+ ? {
52
+ id: team.id,
53
+ key: team.key,
54
+ name: team.name,
55
+ }
56
+ : null,
57
+ issues: issuesSummary,
58
+ createdAt: cycle.createdAt,
59
+ updatedAt: cycle.updatedAt,
60
+ };
61
+ if (format === 'json') {
62
+ print(success(data));
63
+ }
64
+ else if (format === 'table') {
65
+ printItem({
66
+ id: data.id,
67
+ number: data.number,
68
+ name: data.name ?? 'Unnamed',
69
+ team: data.team?.key ?? 'N/A',
70
+ startsAt: data.startsAt,
71
+ endsAt: data.endsAt,
72
+ completedAt: data.completedAt ?? 'In progress',
73
+ progress: `${Math.round(data.progress * 100)}%`,
74
+ issues: `${issuesSummary.completed}/${issuesSummary.total} completed`,
75
+ }, format);
76
+ }
77
+ else {
78
+ console.log(data.id);
79
+ }
80
+ }
81
+ catch (err) {
82
+ handleError(err);
83
+ this.exit(1);
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CyclesList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'team-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ team: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ active: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ upcoming: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ completed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ first: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ after: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,147 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { successList, print, printList } from '../../lib/output.js';
4
+ import { handleError } from '../../lib/errors.js';
5
+ import { colors, truncate, formatProgress } from '../../lib/formatter.js';
6
+ const COLUMNS = [
7
+ {
8
+ key: 'number',
9
+ header: '#',
10
+ format: (value) => colors.dim(String(value)),
11
+ },
12
+ {
13
+ key: 'name',
14
+ header: 'NAME',
15
+ format: (value) => (value ? colors.bold(truncate(String(value), 25)) : colors.gray('Unnamed')),
16
+ },
17
+ {
18
+ key: 'teamKey',
19
+ header: 'TEAM',
20
+ format: (value) => colors.cyan(String(value)),
21
+ },
22
+ {
23
+ key: 'startsAt',
24
+ header: 'START',
25
+ format: (value) => colors.dim(new Date(value).toISOString().split('T')[0]),
26
+ },
27
+ {
28
+ key: 'endsAt',
29
+ header: 'END',
30
+ format: (value) => colors.dim(new Date(value).toISOString().split('T')[0]),
31
+ },
32
+ {
33
+ key: 'progress',
34
+ header: 'PROGRESS',
35
+ format: (value) => formatProgress(Number(value)),
36
+ },
37
+ ];
38
+ export default class CyclesList extends Command {
39
+ static description = 'List cycles (sprints)';
40
+ static examples = [
41
+ '<%= config.bin %> cycles list',
42
+ '<%= config.bin %> cycles list --team-id TEAM_ID',
43
+ '<%= config.bin %> cycles list --team ENG',
44
+ '<%= config.bin %> cycles list --format table',
45
+ '<%= config.bin %> cycles list --active',
46
+ ];
47
+ static flags = {
48
+ format: Flags.string({
49
+ char: 'F',
50
+ description: 'Output format',
51
+ options: ['json', 'table', 'plain'],
52
+ default: 'json',
53
+ }),
54
+ 'team-id': Flags.string({
55
+ description: 'Filter by team ID',
56
+ }),
57
+ team: Flags.string({
58
+ description: 'Filter by team key (e.g., ENG)',
59
+ }),
60
+ active: Flags.boolean({
61
+ description: 'Show only active cycles',
62
+ default: false,
63
+ }),
64
+ upcoming: Flags.boolean({
65
+ description: 'Show only upcoming cycles',
66
+ default: false,
67
+ }),
68
+ completed: Flags.boolean({
69
+ description: 'Show only completed cycles',
70
+ default: false,
71
+ }),
72
+ first: Flags.integer({
73
+ description: 'Number of cycles to fetch (default: 50)',
74
+ default: 50,
75
+ }),
76
+ after: Flags.string({
77
+ description: 'Cursor for pagination',
78
+ }),
79
+ };
80
+ async run() {
81
+ try {
82
+ const { flags } = await this.parse(CyclesList);
83
+ const format = flags.format;
84
+ const client = getClient();
85
+ // Build filter
86
+ const filter = {};
87
+ if (flags['team-id']) {
88
+ filter.team = { id: { eq: flags['team-id'] } };
89
+ }
90
+ else if (flags.team) {
91
+ filter.team = { key: { eq: flags.team } };
92
+ }
93
+ const now = new Date();
94
+ if (flags.active) {
95
+ filter.startsAt = { lte: now };
96
+ filter.endsAt = { gte: now };
97
+ }
98
+ else if (flags.upcoming) {
99
+ filter.startsAt = { gt: now };
100
+ }
101
+ else if (flags.completed) {
102
+ filter.completedAt = { neq: null };
103
+ }
104
+ const cycles = await client.cycles({
105
+ first: flags.first,
106
+ after: flags.after,
107
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
108
+ });
109
+ const data = await Promise.all(cycles.nodes.map(async (cycle) => {
110
+ const team = await cycle.team;
111
+ return {
112
+ id: cycle.id,
113
+ number: cycle.number,
114
+ name: cycle.name ?? null,
115
+ startsAt: cycle.startsAt,
116
+ endsAt: cycle.endsAt,
117
+ completedAt: cycle.completedAt ?? null,
118
+ progress: cycle.progress,
119
+ teamId: team?.id ?? '',
120
+ teamKey: team?.key ?? '',
121
+ teamName: team?.name ?? '',
122
+ };
123
+ }));
124
+ const pageInfo = {
125
+ hasNextPage: cycles.pageInfo.hasNextPage,
126
+ hasPreviousPage: cycles.pageInfo.hasPreviousPage,
127
+ startCursor: cycles.pageInfo.startCursor,
128
+ endCursor: cycles.pageInfo.endCursor,
129
+ };
130
+ if (format === 'json') {
131
+ print(successList(data, pageInfo));
132
+ }
133
+ else {
134
+ printList(data, format, {
135
+ columns: COLUMNS,
136
+ primaryKey: 'name',
137
+ secondaryKey: 'number',
138
+ pageInfo,
139
+ });
140
+ }
141
+ }
142
+ catch (err) {
143
+ handleError(err);
144
+ this.exit(1);
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DocumentsCreate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ title: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ icon: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,68 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { success, print } from '../../lib/output.js';
4
+ import { handleError, CliError, ErrorCodes } from '../../lib/errors.js';
5
+ export default class DocumentsCreate extends Command {
6
+ static description = 'Create a new document';
7
+ static examples = [
8
+ '<%= config.bin %> documents create --title "My Document"',
9
+ '<%= config.bin %> documents create --title "Project Doc" --project-id PROJECT_ID',
10
+ '<%= config.bin %> documents create --title "Notes" --content "# Heading\\n\\nContent here"',
11
+ ];
12
+ static flags = {
13
+ title: Flags.string({
14
+ char: 't',
15
+ description: 'Document title',
16
+ required: true,
17
+ }),
18
+ content: Flags.string({
19
+ char: 'c',
20
+ description: 'Document content (markdown)',
21
+ }),
22
+ 'project-id': Flags.string({
23
+ description: 'Project ID to associate with',
24
+ }),
25
+ icon: Flags.string({
26
+ description: 'Document icon (emoji)',
27
+ }),
28
+ color: Flags.string({
29
+ description: 'Document color (hex)',
30
+ }),
31
+ };
32
+ async run() {
33
+ try {
34
+ const { flags } = await this.parse(DocumentsCreate);
35
+ const client = getClient();
36
+ const input = {
37
+ title: flags.title,
38
+ };
39
+ if (flags.content)
40
+ input.content = flags.content;
41
+ if (flags['project-id'])
42
+ input.projectId = flags['project-id'];
43
+ if (flags.icon)
44
+ input.icon = flags.icon;
45
+ if (flags.color)
46
+ input.color = flags.color;
47
+ const payload = await client.createDocument(input);
48
+ if (!payload.success) {
49
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to create document');
50
+ }
51
+ const document = await payload.document;
52
+ if (!document) {
53
+ throw new CliError(ErrorCodes.API_ERROR, 'Document not returned');
54
+ }
55
+ const project = await document.project;
56
+ print(success({
57
+ id: document.id,
58
+ title: document.title,
59
+ project: project ? { id: project.id, name: project.name } : null,
60
+ createdAt: document.createdAt,
61
+ }));
62
+ }
63
+ catch (err) {
64
+ handleError(err);
65
+ this.exit(1);
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DocumentsDelete extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,32 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { success, print } from '../../lib/output.js';
4
+ import { handleError, CliError, ErrorCodes } from '../../lib/errors.js';
5
+ export default class DocumentsDelete extends Command {
6
+ static description = 'Delete a document (moves to trash)';
7
+ static examples = ['<%= config.bin %> documents delete DOCUMENT_ID'];
8
+ static args = {
9
+ id: Args.string({
10
+ description: 'Document ID',
11
+ required: true,
12
+ }),
13
+ };
14
+ async run() {
15
+ try {
16
+ const { args } = await this.parse(DocumentsDelete);
17
+ const client = getClient();
18
+ const payload = await client.deleteDocument(args.id);
19
+ if (!payload.success) {
20
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to delete document');
21
+ }
22
+ print(success({
23
+ id: args.id,
24
+ deleted: true,
25
+ }));
26
+ }
27
+ catch (err) {
28
+ handleError(err);
29
+ this.exit(1);
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DocumentsGet extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }