linear-cli-agents 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +68 -1
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/documents/create.d.ts +13 -0
  4. package/dist/commands/documents/create.js +68 -0
  5. package/dist/commands/documents/delete.d.ts +9 -0
  6. package/dist/commands/documents/delete.js +32 -0
  7. package/dist/commands/documents/get.d.ts +12 -0
  8. package/dist/commands/documents/get.js +79 -0
  9. package/dist/commands/documents/list.d.ts +12 -0
  10. package/dist/commands/documents/list.js +105 -0
  11. package/dist/commands/documents/update.d.ts +16 -0
  12. package/dist/commands/documents/update.js +75 -0
  13. package/dist/commands/info.js +174 -7
  14. package/dist/commands/initiatives/archive.d.ts +12 -0
  15. package/dist/commands/initiatives/archive.js +44 -0
  16. package/dist/commands/initiatives/create.d.ts +15 -0
  17. package/dist/commands/initiatives/create.js +84 -0
  18. package/dist/commands/initiatives/delete.d.ts +9 -0
  19. package/dist/commands/initiatives/delete.js +32 -0
  20. package/dist/commands/initiatives/get.d.ts +12 -0
  21. package/dist/commands/initiatives/get.js +90 -0
  22. package/dist/commands/initiatives/list.d.ts +11 -0
  23. package/dist/commands/initiatives/list.js +135 -0
  24. package/dist/commands/initiatives/update.d.ts +18 -0
  25. package/dist/commands/initiatives/update.js +90 -0
  26. package/dist/commands/issues/bulk-update.d.ts +2 -0
  27. package/dist/commands/issues/bulk-update.js +10 -0
  28. package/dist/commands/issues/create.d.ts +2 -0
  29. package/dist/commands/issues/create.js +10 -0
  30. package/dist/commands/issues/get.d.ts +1 -0
  31. package/dist/commands/issues/get.js +19 -1
  32. package/dist/commands/issues/update.d.ts +2 -0
  33. package/dist/commands/issues/update.js +12 -0
  34. package/dist/commands/projects/create.d.ts +1 -0
  35. package/dist/commands/projects/create.js +10 -0
  36. package/dist/commands/projects/update.d.ts +1 -0
  37. package/dist/commands/projects/update.js +7 -0
  38. package/dist/commands/upload.d.ts +13 -0
  39. package/dist/commands/upload.js +117 -0
  40. package/oclif.manifest.json +1362 -625
  41. package/package.json +23 -14
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
@@ -288,6 +291,70 @@ linear cycles current --team ENG
288
291
  linear cycles get CYCLE_ID
289
292
  ```
290
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
+
291
358
  ### Users
292
359
 
293
360
  ```bash
package/bin/dev.js CHANGED
File without changes
@@ -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
+ }
@@ -0,0 +1,79 @@
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 DocumentsGet extends Command {
6
+ static description = 'Get document details';
7
+ static examples = [
8
+ '<%= config.bin %> documents get DOCUMENT_ID',
9
+ '<%= config.bin %> documents get DOCUMENT_ID --format table',
10
+ ];
11
+ static args = {
12
+ id: Args.string({
13
+ description: 'Document 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(DocumentsGet);
28
+ const format = flags.format;
29
+ const client = getClient();
30
+ const document = await client.document(args.id);
31
+ if (!document) {
32
+ throw new CliError(ErrorCodes.NOT_FOUND, `Document ${args.id} not found`);
33
+ }
34
+ const [project, creator] = await Promise.all([document.project, document.creator]);
35
+ const data = {
36
+ id: document.id,
37
+ title: document.title,
38
+ content: document.content,
39
+ icon: document.icon ?? null,
40
+ color: document.color ?? null,
41
+ project: project
42
+ ? {
43
+ id: project.id,
44
+ name: project.name,
45
+ }
46
+ : null,
47
+ creator: creator
48
+ ? {
49
+ id: creator.id,
50
+ name: creator.name,
51
+ }
52
+ : null,
53
+ createdAt: document.createdAt,
54
+ updatedAt: document.updatedAt,
55
+ };
56
+ if (format === 'json') {
57
+ print(success(data));
58
+ }
59
+ else if (format === 'table') {
60
+ printItem({
61
+ id: data.id,
62
+ title: data.title,
63
+ project: data.project?.name ?? 'None',
64
+ creator: data.creator?.name ?? 'Unknown',
65
+ content: data.content ? `${data.content.slice(0, 100)}...` : 'Empty',
66
+ createdAt: data.createdAt,
67
+ updatedAt: data.updatedAt,
68
+ }, format);
69
+ }
70
+ else {
71
+ console.log(data.id);
72
+ }
73
+ }
74
+ catch (err) {
75
+ handleError(err);
76
+ this.exit(1);
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DocumentsList 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
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ first: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ after: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,105 @@
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 } from '../../lib/formatter.js';
6
+ const COLUMNS = [
7
+ {
8
+ key: 'title',
9
+ header: 'TITLE',
10
+ format: (value) => colors.bold(truncate(String(value), 40)),
11
+ },
12
+ {
13
+ key: 'projectName',
14
+ header: 'PROJECT',
15
+ format: (value) => (value ? colors.cyan(truncate(String(value), 20)) : colors.gray('None')),
16
+ },
17
+ {
18
+ key: 'creatorName',
19
+ header: 'CREATOR',
20
+ format: (value) => (value ? colors.dim(String(value)) : colors.gray('Unknown')),
21
+ },
22
+ {
23
+ key: 'updatedAt',
24
+ header: 'UPDATED',
25
+ format: (value) => colors.dim(new Date(value).toISOString().split('T')[0]),
26
+ },
27
+ ];
28
+ export default class DocumentsList extends Command {
29
+ static description = 'List documents';
30
+ static examples = [
31
+ '<%= config.bin %> documents list',
32
+ '<%= config.bin %> documents list --project-id PROJECT_ID',
33
+ '<%= config.bin %> documents list --format table',
34
+ ];
35
+ static flags = {
36
+ format: Flags.string({
37
+ char: 'F',
38
+ description: 'Output format',
39
+ options: ['json', 'table', 'plain'],
40
+ default: 'json',
41
+ }),
42
+ 'project-id': Flags.string({
43
+ description: 'Filter by project ID',
44
+ }),
45
+ first: Flags.integer({
46
+ description: 'Number of documents to fetch (default: 50)',
47
+ default: 50,
48
+ }),
49
+ after: Flags.string({
50
+ description: 'Cursor for pagination',
51
+ }),
52
+ };
53
+ async run() {
54
+ try {
55
+ const { flags } = await this.parse(DocumentsList);
56
+ const format = flags.format;
57
+ const client = getClient();
58
+ const filter = {};
59
+ if (flags['project-id']) {
60
+ filter.project = { id: { eq: flags['project-id'] } };
61
+ }
62
+ const documents = await client.documents({
63
+ first: flags.first,
64
+ after: flags.after,
65
+ filter: Object.keys(filter).length > 0 ? filter : undefined,
66
+ });
67
+ const data = await Promise.all(documents.nodes.map(async (doc) => {
68
+ const [project, creator] = await Promise.all([doc.project, doc.creator]);
69
+ return {
70
+ id: doc.id,
71
+ title: doc.title,
72
+ icon: doc.icon ?? null,
73
+ color: doc.color ?? null,
74
+ projectId: project?.id ?? null,
75
+ projectName: project?.name ?? null,
76
+ creatorId: creator?.id ?? null,
77
+ creatorName: creator?.name ?? null,
78
+ createdAt: doc.createdAt,
79
+ updatedAt: doc.updatedAt,
80
+ };
81
+ }));
82
+ const pageInfo = {
83
+ hasNextPage: documents.pageInfo.hasNextPage,
84
+ hasPreviousPage: documents.pageInfo.hasPreviousPage,
85
+ startCursor: documents.pageInfo.startCursor,
86
+ endCursor: documents.pageInfo.endCursor,
87
+ };
88
+ if (format === 'json') {
89
+ print(successList(data, pageInfo));
90
+ }
91
+ else {
92
+ printList(data, format, {
93
+ columns: COLUMNS,
94
+ primaryKey: 'title',
95
+ secondaryKey: 'id',
96
+ pageInfo,
97
+ });
98
+ }
99
+ }
100
+ catch (err) {
101
+ handleError(err);
102
+ this.exit(1);
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DocumentsUpdate 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
+ title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ content: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ icon: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,75 @@
1
+ import { Args, 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 DocumentsUpdate extends Command {
6
+ static description = 'Update a document';
7
+ static examples = [
8
+ '<%= config.bin %> documents update DOCUMENT_ID --title "New Title"',
9
+ '<%= config.bin %> documents update DOCUMENT_ID --content "Updated content"',
10
+ '<%= config.bin %> documents update DOCUMENT_ID --project-id PROJECT_ID',
11
+ ];
12
+ static args = {
13
+ id: Args.string({
14
+ description: 'Document ID',
15
+ required: true,
16
+ }),
17
+ };
18
+ static flags = {
19
+ title: Flags.string({
20
+ char: 't',
21
+ description: 'New document title',
22
+ }),
23
+ content: Flags.string({
24
+ char: 'c',
25
+ description: 'New document content (markdown)',
26
+ }),
27
+ 'project-id': Flags.string({
28
+ description: 'New project ID (empty string to remove)',
29
+ }),
30
+ icon: Flags.string({
31
+ description: 'New document icon (emoji)',
32
+ }),
33
+ color: Flags.string({
34
+ description: 'New document color (hex)',
35
+ }),
36
+ };
37
+ async run() {
38
+ try {
39
+ const { args, flags } = await this.parse(DocumentsUpdate);
40
+ const client = getClient();
41
+ const input = {};
42
+ if (flags.title)
43
+ input.title = flags.title;
44
+ if (flags.content)
45
+ input.content = flags.content;
46
+ if (flags['project-id'] !== undefined) {
47
+ input.projectId = flags['project-id'] || null;
48
+ }
49
+ if (flags.icon)
50
+ input.icon = flags.icon;
51
+ if (flags.color)
52
+ input.color = flags.color;
53
+ if (Object.keys(input).length === 0) {
54
+ throw new CliError(ErrorCodes.INVALID_INPUT, 'No update fields provided');
55
+ }
56
+ const payload = await client.updateDocument(args.id, input);
57
+ if (!payload.success) {
58
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to update document');
59
+ }
60
+ const document = await payload.document;
61
+ if (!document) {
62
+ throw new CliError(ErrorCodes.API_ERROR, 'Document not returned');
63
+ }
64
+ print(success({
65
+ id: document.id,
66
+ title: document.title,
67
+ updatedAt: document.updatedAt,
68
+ }));
69
+ }
70
+ catch (err) {
71
+ handleError(err);
72
+ this.exit(1);
73
+ }
74
+ }
75
+ }