linear-cli-agents 0.2.1 → 0.4.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.
- package/README.md +166 -3
- package/bin/dev.js +0 -0
- package/dist/commands/comments/add.d.ts +13 -0
- package/dist/commands/comments/add.js +89 -0
- package/dist/commands/comments/delete.d.ts +12 -0
- package/dist/commands/comments/delete.js +50 -0
- package/dist/commands/comments/list.d.ts +14 -0
- package/dist/commands/comments/list.js +103 -0
- package/dist/commands/comments/update.d.ts +13 -0
- package/dist/commands/comments/update.js +81 -0
- package/dist/commands/issues/add-labels.d.ts +13 -0
- package/dist/commands/issues/add-labels.js +90 -0
- package/dist/commands/issues/archive.d.ts +13 -0
- package/dist/commands/issues/archive.js +83 -0
- package/dist/commands/issues/remove-labels.d.ts +13 -0
- package/dist/commands/issues/remove-labels.js +90 -0
- package/dist/commands/labels/create.d.ts +14 -0
- package/dist/commands/labels/create.js +102 -0
- package/dist/commands/labels/delete.d.ts +12 -0
- package/dist/commands/labels/delete.js +50 -0
- package/dist/commands/labels/list.d.ts +12 -0
- package/dist/commands/labels/list.js +117 -0
- package/dist/commands/labels/update.d.ts +16 -0
- package/dist/commands/labels/update.js +109 -0
- package/dist/commands/me.js +1 -5
- package/dist/commands/milestones/create.d.ts +15 -0
- package/dist/commands/milestones/create.js +90 -0
- package/dist/commands/milestones/get.d.ts +12 -0
- package/dist/commands/milestones/get.js +74 -0
- package/dist/commands/milestones/list.d.ts +14 -0
- package/dist/commands/milestones/list.js +97 -0
- package/dist/commands/milestones/update.d.ts +15 -0
- package/dist/commands/milestones/update.js +94 -0
- package/dist/commands/project-updates/create.d.ts +14 -0
- package/dist/commands/project-updates/create.js +96 -0
- package/dist/commands/project-updates/get.d.ts +12 -0
- package/dist/commands/project-updates/get.js +80 -0
- package/dist/commands/project-updates/list.d.ts +14 -0
- package/dist/commands/project-updates/list.js +120 -0
- package/dist/commands/project-updates/update.d.ts +14 -0
- package/dist/commands/project-updates/update.js +96 -0
- package/dist/commands/projects/archive.d.ts +13 -0
- package/dist/commands/projects/archive.js +79 -0
- package/dist/commands/projects/create.d.ts +16 -0
- package/dist/commands/projects/create.js +115 -0
- package/dist/commands/projects/delete.d.ts +12 -0
- package/dist/commands/projects/delete.js +50 -0
- package/dist/commands/projects/get.d.ts +12 -0
- package/dist/commands/projects/get.js +102 -0
- package/dist/commands/projects/list.d.ts +13 -0
- package/dist/commands/projects/list.js +141 -0
- package/dist/commands/projects/update.d.ts +18 -0
- package/dist/commands/projects/update.js +125 -0
- package/dist/commands/relations/create.d.ts +14 -0
- package/dist/commands/relations/create.js +98 -0
- package/dist/commands/relations/delete.d.ts +12 -0
- package/dist/commands/relations/delete.js +47 -0
- package/dist/commands/relations/list.d.ts +12 -0
- package/dist/commands/relations/list.js +128 -0
- package/dist/commands/search.d.ts +15 -0
- package/dist/commands/search.js +102 -0
- package/dist/commands/states/list.d.ts +12 -0
- package/dist/commands/states/list.js +151 -0
- package/dist/commands/teams/list.js +1 -1
- package/dist/commands/templates/create.d.ts +14 -0
- package/dist/commands/templates/create.js +102 -0
- package/dist/commands/templates/get.d.ts +12 -0
- package/dist/commands/templates/get.js +84 -0
- package/dist/commands/templates/list.d.ts +12 -0
- package/dist/commands/templates/list.js +110 -0
- package/dist/commands/templates/update.d.ts +15 -0
- package/dist/commands/templates/update.js +101 -0
- package/dist/commands/users/get.d.ts +12 -0
- package/dist/commands/users/get.js +91 -0
- package/dist/commands/users/list.d.ts +12 -0
- package/dist/commands/users/list.js +99 -0
- package/dist/lib/config.js +1 -1
- package/oclif.manifest.json +2402 -189
- package/package.json +47 -17
|
@@ -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
|
+
}
|
|
@@ -62,7 +62,7 @@ export default class TeamsList extends Command {
|
|
|
62
62
|
after: flags.after,
|
|
63
63
|
});
|
|
64
64
|
const data = await Promise.all(teams.nodes.map(async (team) => {
|
|
65
|
-
const issues = await team.issues({ first:
|
|
65
|
+
const issues = await team.issues({ first: 1 });
|
|
66
66
|
return {
|
|
67
67
|
id: team.id,
|
|
68
68
|
key: team.key,
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
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: 'name',
|
|
9
|
+
header: 'NAME',
|
|
10
|
+
format: (value) => colors.bold(truncate(String(value), 30)),
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
key: 'type',
|
|
14
|
+
header: 'TYPE',
|
|
15
|
+
format: (value) => colors.cyan(String(value)),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
key: 'teamKey',
|
|
19
|
+
header: 'TEAM',
|
|
20
|
+
format: (value) => (value ? colors.dim(String(value)) : colors.gray('Org')),
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
export default class TemplatesList extends Command {
|
|
24
|
+
static description = 'List templates in the workspace';
|
|
25
|
+
static examples = [
|
|
26
|
+
'<%= config.bin %> templates list',
|
|
27
|
+
'<%= config.bin %> templates list --format table',
|
|
28
|
+
'<%= config.bin %> templates list --team ENG',
|
|
29
|
+
];
|
|
30
|
+
static flags = {
|
|
31
|
+
format: Flags.string({
|
|
32
|
+
char: 'F',
|
|
33
|
+
description: 'Output format',
|
|
34
|
+
options: ['json', 'table', 'plain'],
|
|
35
|
+
default: 'json',
|
|
36
|
+
}),
|
|
37
|
+
team: Flags.string({
|
|
38
|
+
char: 't',
|
|
39
|
+
description: 'Filter by team key (e.g., ENG)',
|
|
40
|
+
}),
|
|
41
|
+
first: Flags.integer({
|
|
42
|
+
description: 'Number of templates to fetch (default: 50)',
|
|
43
|
+
default: 50,
|
|
44
|
+
}),
|
|
45
|
+
after: Flags.string({
|
|
46
|
+
description: 'Cursor for pagination',
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
async run() {
|
|
50
|
+
try {
|
|
51
|
+
const { flags } = await this.parse(TemplatesList);
|
|
52
|
+
const format = flags.format;
|
|
53
|
+
const client = getClient();
|
|
54
|
+
// Get organization to fetch templates
|
|
55
|
+
const org = await client.organization;
|
|
56
|
+
let templates;
|
|
57
|
+
if (flags.team) {
|
|
58
|
+
const teams = await client.teams({ filter: { key: { eq: flags.team } } });
|
|
59
|
+
const team = teams.nodes[0];
|
|
60
|
+
if (!team) {
|
|
61
|
+
throw new Error(`Team ${flags.team} not found`);
|
|
62
|
+
}
|
|
63
|
+
templates = await team.templates({
|
|
64
|
+
first: flags.first,
|
|
65
|
+
after: flags.after,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
templates = await org.templates({
|
|
70
|
+
first: flags.first,
|
|
71
|
+
after: flags.after,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const data = await Promise.all(templates.nodes.map(async (template) => {
|
|
75
|
+
const team = await template.team;
|
|
76
|
+
return {
|
|
77
|
+
id: template.id,
|
|
78
|
+
name: template.name,
|
|
79
|
+
type: template.type,
|
|
80
|
+
description: template.description ?? undefined,
|
|
81
|
+
teamId: team?.id,
|
|
82
|
+
teamKey: team?.key,
|
|
83
|
+
createdAt: template.createdAt,
|
|
84
|
+
updatedAt: template.updatedAt,
|
|
85
|
+
};
|
|
86
|
+
}));
|
|
87
|
+
const pageInfo = {
|
|
88
|
+
hasNextPage: templates.pageInfo.hasNextPage,
|
|
89
|
+
hasPreviousPage: templates.pageInfo.hasPreviousPage,
|
|
90
|
+
startCursor: templates.pageInfo.startCursor,
|
|
91
|
+
endCursor: templates.pageInfo.endCursor,
|
|
92
|
+
};
|
|
93
|
+
if (format === 'json') {
|
|
94
|
+
print(successList(data, pageInfo));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
printList(data, format, {
|
|
98
|
+
columns: COLUMNS,
|
|
99
|
+
primaryKey: 'name',
|
|
100
|
+
secondaryKey: 'type',
|
|
101
|
+
pageInfo,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
handleError(err);
|
|
107
|
+
this.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class TemplatesUpdate 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
|
+
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
'template-data': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
}
|