linear-cli-agents 0.2.0 → 0.4.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 (78) hide show
  1. package/README.md +253 -5
  2. package/bin/dev.js +0 -0
  3. package/dist/commands/comments/add.d.ts +13 -0
  4. package/dist/commands/comments/add.js +89 -0
  5. package/dist/commands/comments/delete.d.ts +12 -0
  6. package/dist/commands/comments/delete.js +50 -0
  7. package/dist/commands/comments/list.d.ts +14 -0
  8. package/dist/commands/comments/list.js +103 -0
  9. package/dist/commands/comments/update.d.ts +13 -0
  10. package/dist/commands/comments/update.js +81 -0
  11. package/dist/commands/issues/add-labels.d.ts +13 -0
  12. package/dist/commands/issues/add-labels.js +90 -0
  13. package/dist/commands/issues/archive.d.ts +13 -0
  14. package/dist/commands/issues/archive.js +83 -0
  15. package/dist/commands/issues/remove-labels.d.ts +13 -0
  16. package/dist/commands/issues/remove-labels.js +90 -0
  17. package/dist/commands/labels/create.d.ts +14 -0
  18. package/dist/commands/labels/create.js +102 -0
  19. package/dist/commands/labels/delete.d.ts +12 -0
  20. package/dist/commands/labels/delete.js +50 -0
  21. package/dist/commands/labels/list.d.ts +12 -0
  22. package/dist/commands/labels/list.js +117 -0
  23. package/dist/commands/labels/update.d.ts +16 -0
  24. package/dist/commands/labels/update.js +109 -0
  25. package/dist/commands/me.js +1 -5
  26. package/dist/commands/milestones/create.d.ts +15 -0
  27. package/dist/commands/milestones/create.js +90 -0
  28. package/dist/commands/milestones/get.d.ts +12 -0
  29. package/dist/commands/milestones/get.js +74 -0
  30. package/dist/commands/milestones/list.d.ts +14 -0
  31. package/dist/commands/milestones/list.js +97 -0
  32. package/dist/commands/milestones/update.d.ts +15 -0
  33. package/dist/commands/milestones/update.js +94 -0
  34. package/dist/commands/project-updates/create.d.ts +14 -0
  35. package/dist/commands/project-updates/create.js +96 -0
  36. package/dist/commands/project-updates/get.d.ts +12 -0
  37. package/dist/commands/project-updates/get.js +80 -0
  38. package/dist/commands/project-updates/list.d.ts +14 -0
  39. package/dist/commands/project-updates/list.js +120 -0
  40. package/dist/commands/project-updates/update.d.ts +14 -0
  41. package/dist/commands/project-updates/update.js +96 -0
  42. package/dist/commands/projects/archive.d.ts +13 -0
  43. package/dist/commands/projects/archive.js +79 -0
  44. package/dist/commands/projects/create.d.ts +16 -0
  45. package/dist/commands/projects/create.js +115 -0
  46. package/dist/commands/projects/delete.d.ts +12 -0
  47. package/dist/commands/projects/delete.js +50 -0
  48. package/dist/commands/projects/get.d.ts +12 -0
  49. package/dist/commands/projects/get.js +102 -0
  50. package/dist/commands/projects/list.d.ts +13 -0
  51. package/dist/commands/projects/list.js +141 -0
  52. package/dist/commands/projects/update.d.ts +18 -0
  53. package/dist/commands/projects/update.js +125 -0
  54. package/dist/commands/relations/create.d.ts +14 -0
  55. package/dist/commands/relations/create.js +98 -0
  56. package/dist/commands/relations/delete.d.ts +12 -0
  57. package/dist/commands/relations/delete.js +47 -0
  58. package/dist/commands/relations/list.d.ts +12 -0
  59. package/dist/commands/relations/list.js +128 -0
  60. package/dist/commands/search.d.ts +15 -0
  61. package/dist/commands/search.js +102 -0
  62. package/dist/commands/states/list.d.ts +12 -0
  63. package/dist/commands/states/list.js +151 -0
  64. package/dist/commands/templates/create.d.ts +14 -0
  65. package/dist/commands/templates/create.js +102 -0
  66. package/dist/commands/templates/get.d.ts +12 -0
  67. package/dist/commands/templates/get.js +84 -0
  68. package/dist/commands/templates/list.d.ts +12 -0
  69. package/dist/commands/templates/list.js +110 -0
  70. package/dist/commands/templates/update.d.ts +15 -0
  71. package/dist/commands/templates/update.js +101 -0
  72. package/dist/commands/users/get.d.ts +12 -0
  73. package/dist/commands/users/get.js +91 -0
  74. package/dist/commands/users/list.d.ts +12 -0
  75. package/dist/commands/users/list.js +99 -0
  76. package/dist/lib/config.js +1 -1
  77. package/oclif.manifest.json +2397 -184
  78. package/package.json +47 -17
@@ -0,0 +1,128 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { getClient } from '../../lib/client.js';
3
+ import { successList, print, printList } from '../../lib/output.js';
4
+ import { handleError, CliError, ErrorCodes } from '../../lib/errors.js';
5
+ import { resolveIssueId } from '../../lib/issue-utils.js';
6
+ import { colors } from '../../lib/formatter.js';
7
+ const formatRelationType = (type) => {
8
+ switch (type) {
9
+ case 'blocks':
10
+ return colors.red('blocks');
11
+ case 'blocked':
12
+ return colors.yellow('blocked by');
13
+ case 'duplicate':
14
+ return colors.magenta('duplicate of');
15
+ case 'related':
16
+ return colors.blue('related to');
17
+ default:
18
+ return type;
19
+ }
20
+ };
21
+ const COLUMNS = [
22
+ {
23
+ key: 'issueIdentifier',
24
+ header: 'ISSUE',
25
+ format: (value) => colors.cyan(String(value)),
26
+ },
27
+ {
28
+ key: 'type',
29
+ header: 'RELATION',
30
+ format: (value) => formatRelationType(String(value)),
31
+ },
32
+ {
33
+ key: 'relatedIssueIdentifier',
34
+ header: 'RELATED ISSUE',
35
+ format: (value) => colors.cyan(String(value)),
36
+ },
37
+ {
38
+ key: 'relatedIssueTitle',
39
+ header: 'TITLE',
40
+ format: (value) => colors.dim(String(value).slice(0, 30)),
41
+ },
42
+ ];
43
+ export default class RelationsList extends Command {
44
+ static description = 'List relations for an issue';
45
+ static examples = [
46
+ '<%= config.bin %> relations list ENG-123',
47
+ '<%= config.bin %> relations list ENG-123 --format table',
48
+ ];
49
+ static args = {
50
+ issue: Args.string({
51
+ description: 'Issue ID or identifier (e.g., ENG-123)',
52
+ required: true,
53
+ }),
54
+ };
55
+ static flags = {
56
+ format: Flags.string({
57
+ char: 'F',
58
+ description: 'Output format',
59
+ options: ['json', 'table', 'plain'],
60
+ default: 'json',
61
+ }),
62
+ };
63
+ async run() {
64
+ try {
65
+ const { args, flags } = await this.parse(RelationsList);
66
+ const format = flags.format;
67
+ const client = getClient();
68
+ const issueId = await resolveIssueId(client, args.issue);
69
+ const issue = await client.issue(issueId);
70
+ if (!issue) {
71
+ throw new CliError(ErrorCodes.NOT_FOUND, `Issue ${args.issue} not found`);
72
+ }
73
+ // Get both relations and inverse relations
74
+ const [relations, inverseRelations] = await Promise.all([issue.relations(), issue.inverseRelations()]);
75
+ const data = [];
76
+ // Process outgoing relations
77
+ for (const relation of relations.nodes) {
78
+ const relatedIssue = await relation.relatedIssue;
79
+ if (relatedIssue) {
80
+ data.push({
81
+ id: relation.id,
82
+ type: relation.type,
83
+ issueId: issue.id,
84
+ issueIdentifier: issue.identifier,
85
+ issueTitle: issue.title,
86
+ relatedIssueId: relatedIssue.id,
87
+ relatedIssueIdentifier: relatedIssue.identifier,
88
+ relatedIssueTitle: relatedIssue.title,
89
+ createdAt: relation.createdAt,
90
+ });
91
+ }
92
+ }
93
+ // Process incoming relations (inverse)
94
+ for (const relation of inverseRelations.nodes) {
95
+ const sourceIssue = await relation.issue;
96
+ if (sourceIssue) {
97
+ // Flip the type for inverse relations
98
+ const inverseType = relation.type === 'blocks' ? 'blocked' : relation.type === 'blocked' ? 'blocks' : relation.type;
99
+ data.push({
100
+ id: relation.id,
101
+ type: inverseType,
102
+ issueId: issue.id,
103
+ issueIdentifier: issue.identifier,
104
+ issueTitle: issue.title,
105
+ relatedIssueId: sourceIssue.id,
106
+ relatedIssueIdentifier: sourceIssue.identifier,
107
+ relatedIssueTitle: sourceIssue.title,
108
+ createdAt: relation.createdAt,
109
+ });
110
+ }
111
+ }
112
+ if (format === 'json') {
113
+ print(successList(data));
114
+ }
115
+ else {
116
+ printList(data, format, {
117
+ columns: COLUMNS,
118
+ primaryKey: 'issueIdentifier',
119
+ secondaryKey: 'relatedIssueIdentifier',
120
+ });
121
+ }
122
+ }
123
+ catch (err) {
124
+ handleError(err);
125
+ this.exit(1);
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Search extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ query: 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
+ team: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ first: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
12
+ after: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,102 @@
1
+ import { Args, 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, formatPriority } from '../lib/formatter.js';
6
+ const COLUMNS = [
7
+ {
8
+ key: 'identifier',
9
+ header: 'ID',
10
+ format: (value) => colors.cyan(String(value)),
11
+ },
12
+ {
13
+ key: 'priority',
14
+ header: 'PRI',
15
+ format: (value) => formatPriority(Number(value)),
16
+ },
17
+ {
18
+ key: 'title',
19
+ header: 'TITLE',
20
+ format: (value) => truncate(String(value), 50),
21
+ },
22
+ ];
23
+ export default class Search extends Command {
24
+ static description = 'Search issues by text query';
25
+ static examples = [
26
+ '<%= config.bin %> search "bug login"',
27
+ '<%= config.bin %> search "SSO" --team ENG',
28
+ '<%= config.bin %> search "authentication" --format table',
29
+ ];
30
+ static args = {
31
+ query: Args.string({
32
+ description: 'Search query',
33
+ required: true,
34
+ }),
35
+ };
36
+ static flags = {
37
+ format: Flags.string({
38
+ char: 'F',
39
+ description: 'Output format',
40
+ options: ['json', 'table', 'plain'],
41
+ default: 'json',
42
+ }),
43
+ team: Flags.string({
44
+ char: 't',
45
+ description: 'Filter by team key (e.g., ENG)',
46
+ }),
47
+ first: Flags.integer({
48
+ description: 'Number of results to fetch (default: 20)',
49
+ default: 20,
50
+ }),
51
+ after: Flags.string({
52
+ description: 'Cursor for pagination',
53
+ }),
54
+ };
55
+ async run() {
56
+ try {
57
+ const { args, flags } = await this.parse(Search);
58
+ const format = flags.format;
59
+ const client = getClient();
60
+ // Build filter for team if specified
61
+ const filter = flags.team ? { team: { key: { eq: flags.team } } } : undefined;
62
+ // Use the searchIssues method for text search
63
+ const searchResults = await client.searchIssues(args.query, {
64
+ filter,
65
+ first: flags.first,
66
+ after: flags.after,
67
+ });
68
+ const data = searchResults.nodes.map((issue) => ({
69
+ id: issue.id,
70
+ identifier: issue.identifier,
71
+ title: issue.title,
72
+ description: issue.description ?? undefined,
73
+ priority: issue.priority,
74
+ priorityLabel: issue.priorityLabel,
75
+ url: issue.url,
76
+ createdAt: issue.createdAt,
77
+ updatedAt: issue.updatedAt,
78
+ }));
79
+ const pageInfo = {
80
+ hasNextPage: searchResults.pageInfo.hasNextPage,
81
+ hasPreviousPage: searchResults.pageInfo.hasPreviousPage,
82
+ startCursor: searchResults.pageInfo.startCursor,
83
+ endCursor: searchResults.pageInfo.endCursor,
84
+ };
85
+ if (format === 'json') {
86
+ print(successList(data, pageInfo));
87
+ }
88
+ else {
89
+ printList(data, format, {
90
+ columns: COLUMNS,
91
+ primaryKey: 'identifier',
92
+ secondaryKey: 'title',
93
+ pageInfo,
94
+ });
95
+ }
96
+ }
97
+ catch (err) {
98
+ handleError(err);
99
+ this.exit(1);
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class StatesList 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: 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,151 @@
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 } from '../../lib/formatter.js';
6
+ const STATE_TYPE_ORDER = {
7
+ backlog: 1,
8
+ unstarted: 2,
9
+ started: 3,
10
+ completed: 4,
11
+ canceled: 5,
12
+ };
13
+ const formatStateType = (type) => {
14
+ switch (type) {
15
+ case 'backlog':
16
+ return colors.gray('backlog');
17
+ case 'unstarted':
18
+ return colors.blue('unstarted');
19
+ case 'started':
20
+ return colors.yellow('started');
21
+ case 'completed':
22
+ return colors.green('completed');
23
+ case 'canceled':
24
+ return colors.red('canceled');
25
+ default:
26
+ return type;
27
+ }
28
+ };
29
+ const COLUMNS = [
30
+ {
31
+ key: 'teamKey',
32
+ header: 'TEAM',
33
+ format: (value) => colors.cyan(String(value)),
34
+ },
35
+ {
36
+ key: 'name',
37
+ header: 'NAME',
38
+ format: (value) => colors.bold(String(value)),
39
+ },
40
+ {
41
+ key: 'type',
42
+ header: 'TYPE',
43
+ format: (value) => formatStateType(String(value)),
44
+ },
45
+ {
46
+ key: 'color',
47
+ header: 'COLOR',
48
+ format: (value) => colors.dim(String(value)),
49
+ },
50
+ ];
51
+ export default class StatesList extends Command {
52
+ static description = 'List workflow states in the workspace';
53
+ static examples = [
54
+ '<%= config.bin %> states list',
55
+ '<%= config.bin %> states list --format table',
56
+ '<%= config.bin %> states list --team ENG',
57
+ ];
58
+ static flags = {
59
+ format: Flags.string({
60
+ char: 'F',
61
+ description: 'Output format',
62
+ options: ['json', 'table', 'plain'],
63
+ default: 'json',
64
+ }),
65
+ team: Flags.string({
66
+ char: 't',
67
+ description: 'Filter by team key (e.g., ENG)',
68
+ }),
69
+ first: Flags.integer({
70
+ description: 'Number of states to fetch (default: 100)',
71
+ default: 100,
72
+ }),
73
+ after: Flags.string({
74
+ description: 'Cursor for pagination',
75
+ }),
76
+ };
77
+ async run() {
78
+ try {
79
+ const { flags } = await this.parse(StatesList);
80
+ const format = flags.format;
81
+ const client = getClient();
82
+ let states;
83
+ if (flags.team) {
84
+ // Fetch states for a specific team
85
+ const teams = await client.teams({
86
+ filter: { key: { eq: flags.team } },
87
+ });
88
+ const team = teams.nodes[0];
89
+ if (!team) {
90
+ throw new Error(`Team ${flags.team} not found`);
91
+ }
92
+ states = await team.states({
93
+ first: flags.first,
94
+ after: flags.after,
95
+ });
96
+ }
97
+ else {
98
+ // Fetch all workflow states
99
+ states = await client.workflowStates({
100
+ first: flags.first,
101
+ after: flags.after,
102
+ });
103
+ }
104
+ const data = await Promise.all(states.nodes.map(async (state) => {
105
+ const team = await state.team;
106
+ return {
107
+ id: state.id,
108
+ name: state.name,
109
+ color: state.color,
110
+ type: state.type,
111
+ position: state.position,
112
+ teamId: team?.id ?? '',
113
+ teamKey: team?.key ?? '',
114
+ };
115
+ }));
116
+ // Sort by team, then by type order, then by position
117
+ data.sort((a, b) => {
118
+ if (a.teamKey !== b.teamKey) {
119
+ return a.teamKey.localeCompare(b.teamKey);
120
+ }
121
+ const typeOrderA = STATE_TYPE_ORDER[a.type] ?? 99;
122
+ const typeOrderB = STATE_TYPE_ORDER[b.type] ?? 99;
123
+ if (typeOrderA !== typeOrderB) {
124
+ return typeOrderA - typeOrderB;
125
+ }
126
+ return a.position - b.position;
127
+ });
128
+ const pageInfo = {
129
+ hasNextPage: states.pageInfo.hasNextPage,
130
+ hasPreviousPage: states.pageInfo.hasPreviousPage,
131
+ startCursor: states.pageInfo.startCursor,
132
+ endCursor: states.pageInfo.endCursor,
133
+ };
134
+ if (format === 'json') {
135
+ print(successList(data, pageInfo));
136
+ }
137
+ else {
138
+ printList(data, format, {
139
+ columns: COLUMNS,
140
+ primaryKey: 'name',
141
+ secondaryKey: 'type',
142
+ pageInfo,
143
+ });
144
+ }
145
+ }
146
+ catch (err) {
147
+ handleError(err);
148
+ this.exit(1);
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class TemplatesCreate 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
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'team-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'template-data': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,102 @@
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 TemplatesCreate extends Command {
6
+ static description = 'Create a template';
7
+ static examples = [
8
+ '<%= config.bin %> templates create --name "Bug Report" --type issue --team-id TEAM_ID --template-data \'{"title":"Bug: ","priority":2}\'',
9
+ '<%= config.bin %> templates create --name "Feature Request" --type issue --template-data \'{"title":"Feature: "}\'',
10
+ ];
11
+ static flags = {
12
+ format: Flags.string({
13
+ char: 'F',
14
+ description: 'Output format',
15
+ options: ['json', 'table', 'plain'],
16
+ default: 'json',
17
+ }),
18
+ name: Flags.string({
19
+ char: 'n',
20
+ description: 'Template name',
21
+ required: true,
22
+ }),
23
+ type: Flags.string({
24
+ char: 't',
25
+ description: 'Template type',
26
+ required: true,
27
+ options: ['issue', 'project'],
28
+ }),
29
+ description: Flags.string({
30
+ char: 'd',
31
+ description: 'Template description',
32
+ }),
33
+ 'team-id': Flags.string({
34
+ description: 'Team ID (optional, for team-specific templates)',
35
+ }),
36
+ 'template-data': Flags.string({
37
+ description: 'Template data as JSON (e.g., {"title":"Bug: ","priority":2})',
38
+ required: true,
39
+ }),
40
+ };
41
+ async run() {
42
+ try {
43
+ const { flags } = await this.parse(TemplatesCreate);
44
+ const format = flags.format;
45
+ const client = getClient();
46
+ let templateData;
47
+ try {
48
+ templateData = JSON.parse(flags['template-data']);
49
+ }
50
+ catch {
51
+ throw new CliError(ErrorCodes.INVALID_INPUT, 'Invalid JSON in --template-data');
52
+ }
53
+ const payload = await client.createTemplate({
54
+ name: flags.name,
55
+ type: flags.type,
56
+ description: flags.description,
57
+ teamId: flags['team-id'],
58
+ templateData,
59
+ });
60
+ if (!payload.success || !payload.template) {
61
+ throw new CliError(ErrorCodes.API_ERROR, 'Failed to create template');
62
+ }
63
+ const template = await payload.template;
64
+ const team = await template.team;
65
+ const data = {
66
+ id: template.id,
67
+ name: template.name,
68
+ type: template.type,
69
+ description: template.description,
70
+ templateData: template.templateData,
71
+ team: team
72
+ ? {
73
+ id: team.id,
74
+ key: team.key,
75
+ name: team.name,
76
+ }
77
+ : null,
78
+ createdAt: template.createdAt,
79
+ };
80
+ if (format === 'json') {
81
+ print(success(data));
82
+ }
83
+ else if (format === 'table') {
84
+ printItem({
85
+ id: data.id,
86
+ name: data.name,
87
+ type: data.type,
88
+ description: data.description ?? 'N/A',
89
+ team: data.team?.key ?? 'Organization',
90
+ createdAt: data.createdAt,
91
+ }, format);
92
+ }
93
+ else {
94
+ console.log(data.id);
95
+ }
96
+ }
97
+ catch (err) {
98
+ handleError(err);
99
+ this.exit(1);
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class TemplatesGet 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,84 @@
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 TemplatesGet extends Command {
6
+ static description = 'Get a template by ID';
7
+ static examples = [
8
+ '<%= config.bin %> templates get TEMPLATE_ID',
9
+ '<%= config.bin %> templates get TEMPLATE_ID --format table',
10
+ ];
11
+ static args = {
12
+ id: Args.string({
13
+ description: 'Template 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(TemplatesGet);
28
+ const format = flags.format;
29
+ const client = getClient();
30
+ // Templates need to be fetched via organization
31
+ const org = await client.organization;
32
+ const templates = await org.templates();
33
+ const template = templates.nodes.find((t) => t.id === args.id);
34
+ if (!template) {
35
+ throw new CliError(ErrorCodes.NOT_FOUND, `Template ${args.id} not found`);
36
+ }
37
+ const [team, creator] = await Promise.all([template.team, template.creator]);
38
+ const data = {
39
+ id: template.id,
40
+ name: template.name,
41
+ type: template.type,
42
+ description: template.description,
43
+ templateData: template.templateData,
44
+ team: team
45
+ ? {
46
+ id: team.id,
47
+ key: team.key,
48
+ name: team.name,
49
+ }
50
+ : null,
51
+ creator: creator
52
+ ? {
53
+ id: creator.id,
54
+ name: creator.name,
55
+ }
56
+ : null,
57
+ createdAt: template.createdAt,
58
+ updatedAt: template.updatedAt,
59
+ };
60
+ if (format === 'json') {
61
+ print(success(data));
62
+ }
63
+ else if (format === 'table') {
64
+ printItem({
65
+ id: data.id,
66
+ name: data.name,
67
+ type: data.type,
68
+ description: data.description ?? 'N/A',
69
+ team: data.team?.key ?? 'Organization',
70
+ creator: data.creator?.name ?? 'Unknown',
71
+ createdAt: data.createdAt,
72
+ updatedAt: data.updatedAt,
73
+ }, format);
74
+ }
75
+ else {
76
+ console.log(data.id);
77
+ }
78
+ }
79
+ catch (err) {
80
+ handleError(err);
81
+ this.exit(1);
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class TemplatesList 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: 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
+ }