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
@@ -0,0 +1,84 @@
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
+ import { LinearDocument } from '@linear/sdk';
6
+ const InitiativeStatus = LinearDocument.InitiativeStatus;
7
+ export default class InitiativesCreate extends Command {
8
+ static description = 'Create a new initiative';
9
+ static examples = [
10
+ '<%= config.bin %> initiatives create --name "Q1 Goals"',
11
+ '<%= config.bin %> initiatives create --name "Product Launch" --status Active',
12
+ '<%= config.bin %> initiatives create --name "H2 Objectives" --target-date 2024-12-31',
13
+ ];
14
+ static flags = {
15
+ name: Flags.string({
16
+ char: 'n',
17
+ description: 'Initiative name',
18
+ required: true,
19
+ }),
20
+ description: Flags.string({
21
+ char: 'd',
22
+ description: 'Initiative description',
23
+ }),
24
+ status: Flags.string({
25
+ char: 's',
26
+ description: 'Initiative status',
27
+ options: ['Planned', 'Active', 'Completed'],
28
+ }),
29
+ 'target-date': Flags.string({
30
+ description: 'Target completion date (YYYY-MM-DD)',
31
+ }),
32
+ 'owner-id': Flags.string({
33
+ description: 'Owner user ID',
34
+ }),
35
+ icon: Flags.string({
36
+ description: 'Initiative icon (emoji)',
37
+ }),
38
+ color: Flags.string({
39
+ description: 'Initiative color (hex)',
40
+ }),
41
+ };
42
+ async run() {
43
+ try {
44
+ const { flags } = await this.parse(InitiativesCreate);
45
+ const client = getClient();
46
+ const input = {
47
+ name: flags.name,
48
+ };
49
+ if (flags.description)
50
+ input.description = flags.description;
51
+ if (flags.status) {
52
+ input.status = InitiativeStatus[flags.status];
53
+ }
54
+ if (flags['target-date'])
55
+ input.targetDate = flags['target-date'];
56
+ if (flags['owner-id'])
57
+ input.ownerId = flags['owner-id'];
58
+ if (flags.icon)
59
+ input.icon = flags.icon;
60
+ if (flags.color)
61
+ input.color = flags.color;
62
+ const payload = await client.createInitiative(input);
63
+ if (!payload.success) {
64
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to create initiative');
65
+ }
66
+ const initiative = await payload.initiative;
67
+ if (!initiative) {
68
+ throw new CliError(ErrorCodes.API_ERROR, 'Initiative not returned');
69
+ }
70
+ const owner = await initiative.owner;
71
+ print(success({
72
+ id: initiative.id,
73
+ name: initiative.name,
74
+ status: initiative.status,
75
+ owner: owner ? { id: owner.id, name: owner.name } : null,
76
+ createdAt: initiative.createdAt,
77
+ }));
78
+ }
79
+ catch (err) {
80
+ handleError(err);
81
+ this.exit(1);
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class InitiativesDelete 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 InitiativesDelete extends Command {
6
+ static description = 'Delete an initiative (moves to trash)';
7
+ static examples = ['<%= config.bin %> initiatives delete INITIATIVE_ID'];
8
+ static args = {
9
+ id: Args.string({
10
+ description: 'Initiative ID',
11
+ required: true,
12
+ }),
13
+ };
14
+ async run() {
15
+ try {
16
+ const { args } = await this.parse(InitiativesDelete);
17
+ const client = getClient();
18
+ const payload = await client.deleteInitiative(args.id);
19
+ if (!payload.success) {
20
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to delete initiative');
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 InitiativesGet 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,90 @@
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 InitiativesGet extends Command {
6
+ static description = 'Get initiative details';
7
+ static examples = [
8
+ '<%= config.bin %> initiatives get INITIATIVE_ID',
9
+ '<%= config.bin %> initiatives get INITIATIVE_ID --format table',
10
+ ];
11
+ static args = {
12
+ id: Args.string({
13
+ description: 'Initiative 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(InitiativesGet);
28
+ const format = flags.format;
29
+ const client = getClient();
30
+ const initiative = await client.initiative(args.id);
31
+ if (!initiative) {
32
+ throw new CliError(ErrorCodes.NOT_FOUND, `Initiative ${args.id} not found`);
33
+ }
34
+ const [owner, creator, projects] = await Promise.all([
35
+ initiative.owner,
36
+ initiative.creator,
37
+ initiative.projects(),
38
+ ]);
39
+ const data = {
40
+ id: initiative.id,
41
+ name: initiative.name,
42
+ description: initiative.description ?? null,
43
+ status: initiative.status,
44
+ icon: initiative.icon ?? null,
45
+ color: initiative.color ?? null,
46
+ owner: owner
47
+ ? {
48
+ id: owner.id,
49
+ name: owner.name,
50
+ }
51
+ : null,
52
+ creator: creator
53
+ ? {
54
+ id: creator.id,
55
+ name: creator.name,
56
+ }
57
+ : null,
58
+ targetDate: initiative.targetDate ?? null,
59
+ projects: projects.nodes.map((p) => ({
60
+ id: p.id,
61
+ name: p.name,
62
+ })),
63
+ createdAt: initiative.createdAt,
64
+ updatedAt: initiative.updatedAt,
65
+ };
66
+ if (format === 'json') {
67
+ print(success(data));
68
+ }
69
+ else if (format === 'table') {
70
+ printItem({
71
+ id: data.id,
72
+ name: data.name,
73
+ status: data.status,
74
+ owner: data.owner?.name ?? 'Unassigned',
75
+ targetDate: data.targetDate ?? 'None',
76
+ projects: data.projects.length,
77
+ description: data.description ? `${data.description.slice(0, 100)}...` : 'None',
78
+ createdAt: data.createdAt,
79
+ }, format);
80
+ }
81
+ else {
82
+ console.log(data.id);
83
+ }
84
+ }
85
+ catch (err) {
86
+ handleError(err);
87
+ this.exit(1);
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class InitiativesList 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
+ status: 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
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,135 @@
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: 'id',
9
+ header: 'ID',
10
+ format: (value) => colors.dim(truncate(String(value), 12)),
11
+ },
12
+ {
13
+ key: 'name',
14
+ header: 'NAME',
15
+ format: (value) => colors.bold(truncate(String(value), 30)),
16
+ },
17
+ {
18
+ key: 'status',
19
+ header: 'STATUS',
20
+ format: (value) => {
21
+ const status = String(value);
22
+ if (status === 'Active')
23
+ return colors.green(status);
24
+ if (status === 'Completed')
25
+ return colors.cyan(status);
26
+ return colors.gray(status);
27
+ },
28
+ },
29
+ {
30
+ key: 'owner',
31
+ header: 'OWNER',
32
+ format: (_value, row) => {
33
+ const owner = row.owner;
34
+ return owner ? truncate(owner.name, 15) : colors.gray('Unassigned');
35
+ },
36
+ },
37
+ {
38
+ key: 'targetDate',
39
+ header: 'TARGET',
40
+ format: (value) => (value ? colors.dim(String(value)) : colors.gray('None')),
41
+ },
42
+ {
43
+ key: 'projectCount',
44
+ header: 'PROJECTS',
45
+ format: (value) => String(value),
46
+ },
47
+ {
48
+ key: 'progress',
49
+ header: 'PROGRESS',
50
+ format: (value) => String(value),
51
+ },
52
+ ];
53
+ export default class InitiativesList extends Command {
54
+ static description = 'List initiatives';
55
+ static examples = [
56
+ '<%= config.bin %> initiatives list',
57
+ '<%= config.bin %> initiatives list --status Active',
58
+ '<%= config.bin %> initiatives list --format table',
59
+ ];
60
+ static flags = {
61
+ format: Flags.string({
62
+ char: 'F',
63
+ description: 'Output format',
64
+ options: ['json', 'table', 'plain'],
65
+ default: 'json',
66
+ }),
67
+ status: Flags.string({
68
+ char: 's',
69
+ description: 'Filter by status (Planned, Active, Completed)',
70
+ options: ['Planned', 'Active', 'Completed'],
71
+ }),
72
+ first: Flags.integer({
73
+ description: 'Number of initiatives to fetch',
74
+ default: 50,
75
+ }),
76
+ };
77
+ async run() {
78
+ try {
79
+ const { flags } = await this.parse(InitiativesList);
80
+ const format = flags.format;
81
+ const client = getClient();
82
+ const initiatives = await client.initiatives({
83
+ first: flags.first,
84
+ });
85
+ // Client-side filtering since the API doesn't support filter
86
+ let filtered = initiatives.nodes;
87
+ if (flags.status) {
88
+ filtered = filtered.filter((i) => i.status === flags.status);
89
+ }
90
+ const data = await Promise.all(filtered.map(async (initiative) => {
91
+ const owner = await initiative.owner;
92
+ const projects = await initiative.projects();
93
+ // Calculate progress from projects
94
+ let totalProgress = 0;
95
+ if (projects.nodes.length > 0) {
96
+ for (const project of projects.nodes) {
97
+ totalProgress += project.progress;
98
+ }
99
+ totalProgress = totalProgress / projects.nodes.length;
100
+ }
101
+ return {
102
+ id: initiative.id,
103
+ name: initiative.name,
104
+ status: initiative.status,
105
+ owner: owner ? { id: owner.id, name: owner.name } : null,
106
+ targetDate: initiative.targetDate ?? null,
107
+ projectCount: projects.nodes.length,
108
+ progress: formatProgress(totalProgress),
109
+ createdAt: initiative.createdAt,
110
+ };
111
+ }));
112
+ const pageInfo = {
113
+ hasNextPage: initiatives.pageInfo.hasNextPage,
114
+ hasPreviousPage: initiatives.pageInfo.hasPreviousPage,
115
+ startCursor: initiatives.pageInfo.startCursor,
116
+ endCursor: initiatives.pageInfo.endCursor,
117
+ };
118
+ if (format === 'json') {
119
+ print(successList(data, pageInfo));
120
+ }
121
+ else {
122
+ printList(data, format, {
123
+ columns: COLUMNS,
124
+ primaryKey: 'name',
125
+ secondaryKey: 'id',
126
+ pageInfo,
127
+ });
128
+ }
129
+ }
130
+ catch (err) {
131
+ handleError(err);
132
+ this.exit(1);
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class InitiativesUpdate 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
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'target-date': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'owner-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ icon: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ color: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ };
17
+ run(): Promise<void>;
18
+ }
@@ -0,0 +1,90 @@
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
+ import { LinearDocument } from '@linear/sdk';
6
+ const InitiativeStatus = LinearDocument.InitiativeStatus;
7
+ export default class InitiativesUpdate extends Command {
8
+ static description = 'Update an initiative';
9
+ static examples = [
10
+ '<%= config.bin %> initiatives update INITIATIVE_ID --name "New Name"',
11
+ '<%= config.bin %> initiatives update INITIATIVE_ID --status Completed',
12
+ '<%= config.bin %> initiatives update INITIATIVE_ID --target-date 2024-12-31',
13
+ ];
14
+ static args = {
15
+ id: Args.string({
16
+ description: 'Initiative ID',
17
+ required: true,
18
+ }),
19
+ };
20
+ static flags = {
21
+ name: Flags.string({
22
+ char: 'n',
23
+ description: 'New initiative name',
24
+ }),
25
+ description: Flags.string({
26
+ char: 'd',
27
+ description: 'New description',
28
+ }),
29
+ status: Flags.string({
30
+ char: 's',
31
+ description: 'New status',
32
+ options: ['Planned', 'Active', 'Completed'],
33
+ }),
34
+ 'target-date': Flags.string({
35
+ description: 'New target completion date (YYYY-MM-DD)',
36
+ }),
37
+ 'owner-id': Flags.string({
38
+ description: 'New owner user ID',
39
+ }),
40
+ icon: Flags.string({
41
+ description: 'New initiative icon (emoji)',
42
+ }),
43
+ color: Flags.string({
44
+ description: 'New initiative color (hex)',
45
+ }),
46
+ };
47
+ async run() {
48
+ try {
49
+ const { args, flags } = await this.parse(InitiativesUpdate);
50
+ const client = getClient();
51
+ const input = {};
52
+ if (flags.name)
53
+ input.name = flags.name;
54
+ if (flags.description)
55
+ input.description = flags.description;
56
+ if (flags.status) {
57
+ input.status = InitiativeStatus[flags.status];
58
+ }
59
+ if (flags['target-date'])
60
+ input.targetDate = flags['target-date'];
61
+ if (flags['owner-id'])
62
+ input.ownerId = flags['owner-id'];
63
+ if (flags.icon)
64
+ input.icon = flags.icon;
65
+ if (flags.color)
66
+ input.color = flags.color;
67
+ if (Object.keys(input).length === 0) {
68
+ throw new CliError(ErrorCodes.INVALID_INPUT, 'No update fields provided');
69
+ }
70
+ const payload = await client.updateInitiative(args.id, input);
71
+ if (!payload.success) {
72
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to update initiative');
73
+ }
74
+ const initiative = await payload.initiative;
75
+ if (!initiative) {
76
+ throw new CliError(ErrorCodes.API_ERROR, 'Initiative not returned');
77
+ }
78
+ print(success({
79
+ id: initiative.id,
80
+ name: initiative.name,
81
+ status: initiative.status,
82
+ updatedAt: initiative.updatedAt,
83
+ }));
84
+ }
85
+ catch (err) {
86
+ handleError(err);
87
+ this.exit(1);
88
+ }
89
+ }
90
+ }
@@ -10,6 +10,8 @@ export default class IssuesBulkUpdate extends Command {
10
10
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  estimate: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
12
  'label-ids': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'due-date': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'cycle-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
15
  };
14
16
  run(): Promise<void>;
15
17
  }
@@ -35,6 +35,12 @@ export default class IssuesBulkUpdate extends Command {
35
35
  'label-ids': Flags.string({
36
36
  description: 'Comma-separated label IDs (replaces existing labels)',
37
37
  }),
38
+ 'due-date': Flags.string({
39
+ description: 'Due date (YYYY-MM-DD)',
40
+ }),
41
+ 'cycle-id': Flags.string({
42
+ description: 'Cycle (sprint) ID',
43
+ }),
38
44
  };
39
45
  async run() {
40
46
  try {
@@ -55,6 +61,10 @@ export default class IssuesBulkUpdate extends Command {
55
61
  input.estimate = flags.estimate;
56
62
  if (flags['label-ids'])
57
63
  input.labelIds = flags['label-ids'].split(',').map((id) => id.trim());
64
+ if (flags['due-date'])
65
+ input.dueDate = flags['due-date'];
66
+ if (flags['cycle-id'])
67
+ input.cycleId = flags['cycle-id'];
58
68
  if (Object.keys(input).length === 0) {
59
69
  throw new CliError(ErrorCodes.INVALID_INPUT, 'No update fields provided. Use at least one of: --state-id, --priority, --assignee-id, --project-id, --estimate, --label-ids');
60
70
  }
@@ -13,6 +13,8 @@ export default class IssuesCreate extends Command {
13
13
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
14
  estimate: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
15
  'label-ids': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'due-date': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'cycle-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
18
  };
17
19
  run(): Promise<void>;
18
20
  }
@@ -46,6 +46,12 @@ export default class IssuesCreate extends Command {
46
46
  'label-ids': Flags.string({
47
47
  description: 'Comma-separated label IDs',
48
48
  }),
49
+ 'due-date': Flags.string({
50
+ description: 'Due date (YYYY-MM-DD)',
51
+ }),
52
+ 'cycle-id': Flags.string({
53
+ description: 'Cycle (sprint) ID',
54
+ }),
49
55
  };
50
56
  async run() {
51
57
  try {
@@ -89,6 +95,10 @@ export default class IssuesCreate extends Command {
89
95
  input.estimate = flags.estimate;
90
96
  if (flags['label-ids'])
91
97
  input.labelIds = flags['label-ids'].split(',');
98
+ if (flags['due-date'])
99
+ input.dueDate = flags['due-date'];
100
+ if (flags['cycle-id'])
101
+ input.cycleId = flags['cycle-id'];
92
102
  }
93
103
  // Create the issue
94
104
  const payload = await client.createIssue(input);
@@ -7,6 +7,7 @@ export default class IssuesGet extends Command {
7
7
  };
8
8
  static flags: {
9
9
  format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'with-attachments': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  }
@@ -8,6 +8,7 @@ export default class IssuesGet extends Command {
8
8
  static examples = [
9
9
  '<%= config.bin %> issues get ENG-123',
10
10
  '<%= config.bin %> issues get ENG-123 --format table',
11
+ '<%= config.bin %> issues get ENG-123 --with-attachments',
11
12
  '<%= config.bin %> issues get abc123',
12
13
  ];
13
14
  static args = {
@@ -23,6 +24,10 @@ export default class IssuesGet extends Command {
23
24
  options: ['json', 'table', 'plain'],
24
25
  default: 'json',
25
26
  }),
27
+ 'with-attachments': Flags.boolean({
28
+ description: 'Include attachments (linked PRs, commits, etc.)',
29
+ default: false,
30
+ }),
26
31
  };
27
32
  async run() {
28
33
  try {
@@ -35,12 +40,13 @@ export default class IssuesGet extends Command {
35
40
  throw new CliError(ErrorCodes.NOT_FOUND, `Issue ${args.id} not found`);
36
41
  }
37
42
  // Fetch related data
38
- const [state, assignee, team, labels, comments] = await Promise.all([
43
+ const [state, assignee, team, labels, comments, attachments] = await Promise.all([
39
44
  issue.state,
40
45
  issue.assignee,
41
46
  issue.team,
42
47
  issue.labels(),
43
48
  issue.comments(),
49
+ flags['with-attachments'] ? issue.attachments() : Promise.resolve(null),
44
50
  ]);
45
51
  const data = {
46
52
  id: issue.id,
@@ -81,6 +87,17 @@ export default class IssuesGet extends Command {
81
87
  color: label.color,
82
88
  })),
83
89
  commentsCount: comments.nodes.length,
90
+ ...(attachments && {
91
+ attachments: attachments.nodes.map((attachment) => ({
92
+ id: attachment.id,
93
+ title: attachment.title,
94
+ subtitle: attachment.subtitle ?? null,
95
+ url: attachment.url,
96
+ sourceType: attachment.sourceType ?? null,
97
+ metadata: attachment.metadata,
98
+ createdAt: attachment.createdAt,
99
+ })),
100
+ }),
84
101
  };
85
102
  if (format === 'json') {
86
103
  print(success(data));
@@ -96,6 +113,7 @@ export default class IssuesGet extends Command {
96
113
  labels: data.labels.map((l) => l.name).join(', ') || 'None',
97
114
  estimate: data.estimate ?? 'None',
98
115
  comments: data.commentsCount,
116
+ ...('attachments' in data && { attachments: data.attachments?.length ?? 0 }),
99
117
  url: data.url,
100
118
  createdAt: data.createdAt,
101
119
  updatedAt: data.updatedAt,
@@ -15,6 +15,8 @@ export default class IssuesUpdate extends Command {
15
15
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
16
  estimate: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
17
  'label-ids': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'due-date': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ 'cycle-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
20
  };
19
21
  run(): Promise<void>;
20
22
  }
@@ -48,6 +48,12 @@ export default class IssuesUpdate extends Command {
48
48
  'label-ids': Flags.string({
49
49
  description: 'Comma-separated label IDs (replaces existing labels)',
50
50
  }),
51
+ 'due-date': Flags.string({
52
+ description: 'Due date (YYYY-MM-DD, use empty string to clear)',
53
+ }),
54
+ 'cycle-id': Flags.string({
55
+ description: 'Cycle (sprint) ID (use empty string to remove from cycle)',
56
+ }),
51
57
  };
52
58
  async run() {
53
59
  try {
@@ -84,6 +90,12 @@ export default class IssuesUpdate extends Command {
84
90
  input.estimate = flags.estimate;
85
91
  if (flags['label-ids'])
86
92
  input.labelIds = flags['label-ids'].split(',');
93
+ if (flags['due-date'] !== undefined) {
94
+ input.dueDate = flags['due-date'] || null;
95
+ }
96
+ if (flags['cycle-id'] !== undefined) {
97
+ input.cycleId = flags['cycle-id'] || null;
98
+ }
87
99
  if (Object.keys(input).length === 0) {
88
100
  throw new CliError(ErrorCodes.INVALID_INPUT, 'No update fields provided. Use --input or individual flags.');
89
101
  }