@hasna/connectors 0.3.16 → 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.
- package/bin/index.js +71 -1
- package/bin/mcp.js +71 -1
- package/bin/serve.js +70 -0
- package/connectors/connect-asana/.env.example +11 -0
- package/connectors/connect-asana/CLAUDE.md +128 -0
- package/connectors/connect-asana/README.md +193 -0
- package/connectors/connect-asana/package.json +52 -0
- package/connectors/connect-asana/src/api/client.ts +119 -0
- package/connectors/connect-asana/src/api/index.ts +319 -0
- package/connectors/connect-asana/src/cli/index.ts +731 -0
- package/connectors/connect-asana/src/index.ts +19 -0
- package/connectors/connect-asana/src/types/index.ts +270 -0
- package/connectors/connect-asana/src/utils/config.ts +171 -0
- package/connectors/connect-asana/src/utils/output.ts +119 -0
- package/connectors/connect-asana/tsconfig.json +16 -0
- package/connectors/connect-clickup/.env.example +11 -0
- package/connectors/connect-clickup/CLAUDE.md +128 -0
- package/connectors/connect-clickup/README.md +193 -0
- package/connectors/connect-clickup/package.json +52 -0
- package/connectors/connect-clickup/src/api/client.ts +116 -0
- package/connectors/connect-clickup/src/api/index.ts +400 -0
- package/connectors/connect-clickup/src/cli/index.ts +625 -0
- package/connectors/connect-clickup/src/index.ts +19 -0
- package/connectors/connect-clickup/src/types/index.ts +591 -0
- package/connectors/connect-clickup/src/utils/config.ts +157 -0
- package/connectors/connect-clickup/src/utils/output.ts +119 -0
- package/connectors/connect-clickup/tsconfig.json +16 -0
- package/connectors/connect-confluence/.env.example +11 -0
- package/connectors/connect-confluence/CLAUDE.md +272 -0
- package/connectors/connect-confluence/README.md +193 -0
- package/connectors/connect-confluence/package.json +53 -0
- package/connectors/connect-confluence/scripts/release.ts +179 -0
- package/connectors/connect-confluence/src/api/client.ts +213 -0
- package/connectors/connect-confluence/src/api/example.ts +48 -0
- package/connectors/connect-confluence/src/api/index.ts +51 -0
- package/connectors/connect-confluence/src/cli/index.ts +254 -0
- package/connectors/connect-confluence/src/index.ts +103 -0
- package/connectors/connect-confluence/src/types/index.ts +237 -0
- package/connectors/connect-confluence/src/utils/auth.ts +274 -0
- package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
- package/connectors/connect-confluence/src/utils/config.ts +326 -0
- package/connectors/connect-confluence/src/utils/output.ts +175 -0
- package/connectors/connect-confluence/src/utils/settings.ts +114 -0
- package/connectors/connect-confluence/src/utils/storage.ts +198 -0
- package/connectors/connect-confluence/tsconfig.json +16 -0
- package/connectors/connect-jira/.env.example +11 -0
- package/connectors/connect-jira/CLAUDE.md +128 -0
- package/connectors/connect-jira/README.md +193 -0
- package/connectors/connect-jira/package.json +53 -0
- package/connectors/connect-jira/src/api/client.ts +131 -0
- package/connectors/connect-jira/src/api/index.ts +266 -0
- package/connectors/connect-jira/src/cli/index.ts +653 -0
- package/connectors/connect-jira/src/index.ts +23 -0
- package/connectors/connect-jira/src/types/index.ts +448 -0
- package/connectors/connect-jira/src/utils/config.ts +179 -0
- package/connectors/connect-jira/src/utils/output.ts +119 -0
- package/connectors/connect-jira/tsconfig.json +16 -0
- package/connectors/connect-linear/CLAUDE.md +88 -0
- package/connectors/connect-linear/README.md +201 -0
- package/connectors/connect-linear/package.json +45 -0
- package/connectors/connect-linear/src/api/client.ts +62 -0
- package/connectors/connect-linear/src/api/index.ts +46 -0
- package/connectors/connect-linear/src/api/issues.ts +247 -0
- package/connectors/connect-linear/src/api/projects.ts +179 -0
- package/connectors/connect-linear/src/api/teams.ts +125 -0
- package/connectors/connect-linear/src/api/users.ts +112 -0
- package/connectors/connect-linear/src/cli/index.ts +560 -0
- package/connectors/connect-linear/src/index.ts +27 -0
- package/connectors/connect-linear/src/types/index.ts +275 -0
- package/connectors/connect-linear/src/utils/config.ts +249 -0
- package/connectors/connect-linear/src/utils/output.ts +119 -0
- package/connectors/connect-linear/tsconfig.json +16 -0
- package/connectors/connect-slack/.env.example +7 -0
- package/connectors/connect-slack/CLAUDE.md +69 -0
- package/connectors/connect-slack/README.md +150 -0
- package/connectors/connect-slack/package.json +44 -0
- package/connectors/connect-slack/src/api/channels.ts +112 -0
- package/connectors/connect-slack/src/api/client.ts +97 -0
- package/connectors/connect-slack/src/api/index.ts +42 -0
- package/connectors/connect-slack/src/api/messages.ts +127 -0
- package/connectors/connect-slack/src/api/users.ts +110 -0
- package/connectors/connect-slack/src/cli/index.ts +494 -0
- package/connectors/connect-slack/src/index.ts +21 -0
- package/connectors/connect-slack/src/types/index.ts +263 -0
- package/connectors/connect-slack/src/utils/config.ts +297 -0
- package/connectors/connect-slack/src/utils/output.ts +119 -0
- package/connectors/connect-slack/tsconfig.json +16 -0
- package/connectors/connect-telegram/.env.example +2 -0
- package/connectors/connect-telegram/package.json +49 -0
- package/connectors/connect-todoist/.env.example +11 -0
- package/connectors/connect-todoist/CLAUDE.md +104 -0
- package/connectors/connect-todoist/README.md +193 -0
- package/connectors/connect-todoist/package.json +52 -0
- package/connectors/connect-todoist/src/api/client.ts +117 -0
- package/connectors/connect-todoist/src/api/index.ts +188 -0
- package/connectors/connect-todoist/src/cli/index.ts +990 -0
- package/connectors/connect-todoist/src/index.ts +21 -0
- package/connectors/connect-todoist/src/types/index.ts +240 -0
- package/connectors/connect-todoist/src/utils/config.ts +157 -0
- package/connectors/connect-todoist/src/utils/output.ts +119 -0
- package/connectors/connect-todoist/tsconfig.json +16 -0
- package/connectors/connect-trello/.env.example +11 -0
- package/connectors/connect-trello/CLAUDE.md +128 -0
- package/connectors/connect-trello/README.md +193 -0
- package/connectors/connect-trello/package.json +53 -0
- package/connectors/connect-trello/src/api/client.ts +128 -0
- package/connectors/connect-trello/src/api/index.ts +278 -0
- package/connectors/connect-trello/src/cli/index.ts +737 -0
- package/connectors/connect-trello/src/index.ts +21 -0
- package/connectors/connect-trello/src/types/index.ts +314 -0
- package/connectors/connect-trello/src/utils/config.ts +182 -0
- package/connectors/connect-trello/src/utils/output.ts +119 -0
- package/connectors/connect-trello/tsconfig.json +16 -0
- package/connectors/connect-whatsapp/.env.example +11 -0
- package/connectors/connect-whatsapp/CLAUDE.md +113 -0
- package/connectors/connect-whatsapp/README.md +193 -0
- package/connectors/connect-whatsapp/package.json +53 -0
- package/connectors/connect-whatsapp/src/api/client.ts +133 -0
- package/connectors/connect-whatsapp/src/api/index.ts +365 -0
- package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
- package/connectors/connect-whatsapp/src/index.ts +25 -0
- package/connectors/connect-whatsapp/src/types/index.ts +502 -0
- package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
- package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
- package/connectors/connect-whatsapp/tsconfig.json +16 -0
- package/dist/index.js +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { Linear } from '../api';
|
|
5
|
+
import type { OutputFormat, LinearPriority } from '../types';
|
|
6
|
+
import {
|
|
7
|
+
getApiKey,
|
|
8
|
+
setApiKey,
|
|
9
|
+
getDefaultTeamId,
|
|
10
|
+
setDefaultTeamId,
|
|
11
|
+
getCurrentProfile,
|
|
12
|
+
setCurrentProfile,
|
|
13
|
+
listProfiles,
|
|
14
|
+
createProfile,
|
|
15
|
+
deleteProfile,
|
|
16
|
+
profileExists,
|
|
17
|
+
clearConfig,
|
|
18
|
+
isAuthenticated,
|
|
19
|
+
setProfileOverride,
|
|
20
|
+
} from '../utils/config';
|
|
21
|
+
import { print, success, error, info, heading } from '../utils/output';
|
|
22
|
+
|
|
23
|
+
const program = new Command();
|
|
24
|
+
|
|
25
|
+
// Helper to get authenticated client
|
|
26
|
+
function getClient(): Linear {
|
|
27
|
+
const apiKey = getApiKey();
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
console.error(chalk.red('Error: No Linear API key configured.'));
|
|
30
|
+
console.error(chalk.yellow('Set API key with: connect-linear config set-api-key <key>'));
|
|
31
|
+
console.error(chalk.yellow('Or set LINEAR_API_KEY environment variable'));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
return new Linear({ apiKey });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Global options
|
|
38
|
+
program
|
|
39
|
+
.name('connect-linear')
|
|
40
|
+
.description('Linear API CLI')
|
|
41
|
+
.version('0.0.1')
|
|
42
|
+
.option('-p, --profile <name>', 'Use specific profile')
|
|
43
|
+
.option('-f, --format <format>', 'Output format: json, table, pretty', 'pretty')
|
|
44
|
+
.hook('preAction', (thisCommand) => {
|
|
45
|
+
const opts = thisCommand.opts();
|
|
46
|
+
if (opts.profile) {
|
|
47
|
+
setProfileOverride(opts.profile);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// ============================================
|
|
52
|
+
// Auth/Config Commands
|
|
53
|
+
// ============================================
|
|
54
|
+
|
|
55
|
+
const configCmd = program
|
|
56
|
+
.command('config')
|
|
57
|
+
.description('Configuration commands');
|
|
58
|
+
|
|
59
|
+
configCmd
|
|
60
|
+
.command('set-api-key <key>')
|
|
61
|
+
.description('Set API key for current profile')
|
|
62
|
+
.action((key: string) => {
|
|
63
|
+
setApiKey(key);
|
|
64
|
+
success(`API key saved to profile "${getCurrentProfile()}"`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
configCmd
|
|
68
|
+
.command('set-team <teamId>')
|
|
69
|
+
.description('Set default team ID')
|
|
70
|
+
.action((teamId: string) => {
|
|
71
|
+
setDefaultTeamId(teamId);
|
|
72
|
+
success(`Default team set to "${teamId}"`);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
configCmd
|
|
76
|
+
.command('show')
|
|
77
|
+
.description('Show current configuration')
|
|
78
|
+
.action(() => {
|
|
79
|
+
const profile = getCurrentProfile();
|
|
80
|
+
const apiKey = getApiKey();
|
|
81
|
+
const teamId = getDefaultTeamId();
|
|
82
|
+
|
|
83
|
+
heading('Current Configuration');
|
|
84
|
+
print({
|
|
85
|
+
profile,
|
|
86
|
+
authenticated: isAuthenticated(),
|
|
87
|
+
apiKey: apiKey ? `${apiKey.substring(0, 10)}...` : 'Not set',
|
|
88
|
+
defaultTeamId: teamId || 'Not set',
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
configCmd
|
|
93
|
+
.command('clear')
|
|
94
|
+
.description('Clear configuration for current profile')
|
|
95
|
+
.action(() => {
|
|
96
|
+
clearConfig();
|
|
97
|
+
success('Configuration cleared');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// ============================================
|
|
101
|
+
// Profile Commands
|
|
102
|
+
// ============================================
|
|
103
|
+
|
|
104
|
+
const profileCmd = program
|
|
105
|
+
.command('profile')
|
|
106
|
+
.description('Profile management');
|
|
107
|
+
|
|
108
|
+
profileCmd
|
|
109
|
+
.command('list')
|
|
110
|
+
.description('List all profiles')
|
|
111
|
+
.action(() => {
|
|
112
|
+
const profiles = listProfiles();
|
|
113
|
+
const current = getCurrentProfile();
|
|
114
|
+
|
|
115
|
+
if (profiles.length === 0) {
|
|
116
|
+
info('No profiles found. Using default.');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
heading('Profiles');
|
|
121
|
+
profiles.forEach(p => {
|
|
122
|
+
const marker = p === current ? chalk.green(' (active)') : '';
|
|
123
|
+
console.log(` ${p}${marker}`);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
profileCmd
|
|
128
|
+
.command('use <name>')
|
|
129
|
+
.description('Switch to a profile')
|
|
130
|
+
.action((name: string) => {
|
|
131
|
+
if (!profileExists(name)) {
|
|
132
|
+
error(`Profile "${name}" does not exist`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
setCurrentProfile(name);
|
|
136
|
+
success(`Switched to profile "${name}"`);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
profileCmd
|
|
140
|
+
.command('create <name>')
|
|
141
|
+
.description('Create a new profile')
|
|
142
|
+
.action((name: string) => {
|
|
143
|
+
try {
|
|
144
|
+
createProfile(name);
|
|
145
|
+
success(`Profile "${name}" created`);
|
|
146
|
+
} catch (e) {
|
|
147
|
+
error((e as Error).message);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
profileCmd
|
|
153
|
+
.command('delete <name>')
|
|
154
|
+
.description('Delete a profile')
|
|
155
|
+
.action((name: string) => {
|
|
156
|
+
try {
|
|
157
|
+
deleteProfile(name);
|
|
158
|
+
success(`Profile "${name}" deleted`);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
error((e as Error).message);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
profileCmd
|
|
166
|
+
.command('show')
|
|
167
|
+
.description('Show current profile name')
|
|
168
|
+
.action(() => {
|
|
169
|
+
console.log(getCurrentProfile());
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ============================================
|
|
173
|
+
// Test/Auth Commands
|
|
174
|
+
// ============================================
|
|
175
|
+
|
|
176
|
+
program
|
|
177
|
+
.command('test')
|
|
178
|
+
.alias('whoami')
|
|
179
|
+
.description('Test authentication and show current user')
|
|
180
|
+
.action(async () => {
|
|
181
|
+
try {
|
|
182
|
+
const client = getClient();
|
|
183
|
+
const result = await client.test();
|
|
184
|
+
print({
|
|
185
|
+
name: result.name,
|
|
186
|
+
displayName: result.displayName,
|
|
187
|
+
email: result.email,
|
|
188
|
+
admin: result.admin,
|
|
189
|
+
active: result.active,
|
|
190
|
+
});
|
|
191
|
+
} catch (e) {
|
|
192
|
+
error((e as Error).message);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ============================================
|
|
198
|
+
// Issue Commands
|
|
199
|
+
// ============================================
|
|
200
|
+
|
|
201
|
+
const issuesCmd = program
|
|
202
|
+
.command('issues')
|
|
203
|
+
.description('Issue commands');
|
|
204
|
+
|
|
205
|
+
issuesCmd
|
|
206
|
+
.command('list')
|
|
207
|
+
.description('List issues')
|
|
208
|
+
.option('-t, --team <id>', 'Filter by team ID')
|
|
209
|
+
.option('-p, --project <id>', 'Filter by project ID')
|
|
210
|
+
.option('-a, --assignee <id>', 'Filter by assignee ID')
|
|
211
|
+
.option('-l, --limit <n>', 'Maximum number to return', '50')
|
|
212
|
+
.action(async (opts) => {
|
|
213
|
+
try {
|
|
214
|
+
const client = getClient();
|
|
215
|
+
const format = program.opts().format as OutputFormat;
|
|
216
|
+
const issues = await client.issues.list({
|
|
217
|
+
teamId: opts.team || getDefaultTeamId(),
|
|
218
|
+
projectId: opts.project,
|
|
219
|
+
assigneeId: opts.assignee,
|
|
220
|
+
first: parseInt(opts.limit, 10),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (format === 'json') {
|
|
224
|
+
print(issues, format);
|
|
225
|
+
} else {
|
|
226
|
+
print(issues.map(i => ({
|
|
227
|
+
id: i.identifier,
|
|
228
|
+
title: i.title.substring(0, 50) + (i.title.length > 50 ? '...' : ''),
|
|
229
|
+
status: i.state?.name || 'Unknown',
|
|
230
|
+
priority: i.priorityLabel,
|
|
231
|
+
assignee: i.assignee?.displayName || 'Unassigned',
|
|
232
|
+
})), format);
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
error((e as Error).message);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
issuesCmd
|
|
241
|
+
.command('get <id>')
|
|
242
|
+
.description('Get issue details')
|
|
243
|
+
.action(async (id: string) => {
|
|
244
|
+
try {
|
|
245
|
+
const client = getClient();
|
|
246
|
+
const format = program.opts().format as OutputFormat;
|
|
247
|
+
const issue = await client.issues.get(id);
|
|
248
|
+
print(issue, format);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
error((e as Error).message);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
issuesCmd
|
|
256
|
+
.command('create')
|
|
257
|
+
.description('Create a new issue')
|
|
258
|
+
.requiredOption('--title <title>', 'Issue title')
|
|
259
|
+
.requiredOption('--team <id>', 'Team ID')
|
|
260
|
+
.option('--description <text>', 'Issue description')
|
|
261
|
+
.option('--project <id>', 'Project ID')
|
|
262
|
+
.option('--assignee <id>', 'Assignee user ID')
|
|
263
|
+
.option('--priority <n>', 'Priority (0-4)', '0')
|
|
264
|
+
.action(async (opts) => {
|
|
265
|
+
try {
|
|
266
|
+
const client = getClient();
|
|
267
|
+
const format = program.opts().format as OutputFormat;
|
|
268
|
+
const issue = await client.issues.create({
|
|
269
|
+
title: opts.title,
|
|
270
|
+
teamId: opts.team || getDefaultTeamId()!,
|
|
271
|
+
description: opts.description,
|
|
272
|
+
projectId: opts.project,
|
|
273
|
+
assigneeId: opts.assignee,
|
|
274
|
+
priority: parseInt(opts.priority, 10) as LinearPriority,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
success(`Issue created: ${issue.identifier}`);
|
|
278
|
+
print({
|
|
279
|
+
id: issue.identifier,
|
|
280
|
+
title: issue.title,
|
|
281
|
+
url: issue.url,
|
|
282
|
+
}, format);
|
|
283
|
+
} catch (e) {
|
|
284
|
+
error((e as Error).message);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
issuesCmd
|
|
290
|
+
.command('update <id>')
|
|
291
|
+
.description('Update an issue')
|
|
292
|
+
.option('--title <title>', 'New title')
|
|
293
|
+
.option('--description <text>', 'New description')
|
|
294
|
+
.option('--state <id>', 'New state ID')
|
|
295
|
+
.option('--assignee <id>', 'New assignee ID')
|
|
296
|
+
.option('--priority <n>', 'New priority (0-4)')
|
|
297
|
+
.action(async (id: string, opts) => {
|
|
298
|
+
try {
|
|
299
|
+
const client = getClient();
|
|
300
|
+
const format = program.opts().format as OutputFormat;
|
|
301
|
+
|
|
302
|
+
const updates: Record<string, unknown> = {};
|
|
303
|
+
if (opts.title) updates.title = opts.title;
|
|
304
|
+
if (opts.description) updates.description = opts.description;
|
|
305
|
+
if (opts.state) updates.stateId = opts.state;
|
|
306
|
+
if (opts.assignee) updates.assigneeId = opts.assignee;
|
|
307
|
+
if (opts.priority) updates.priority = parseInt(opts.priority, 10);
|
|
308
|
+
|
|
309
|
+
const issue = await client.issues.update(id, updates);
|
|
310
|
+
success(`Issue updated: ${issue.identifier}`);
|
|
311
|
+
print({
|
|
312
|
+
id: issue.identifier,
|
|
313
|
+
title: issue.title,
|
|
314
|
+
status: issue.state?.name,
|
|
315
|
+
}, format);
|
|
316
|
+
} catch (e) {
|
|
317
|
+
error((e as Error).message);
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
issuesCmd
|
|
323
|
+
.command('archive <id>')
|
|
324
|
+
.description('Archive an issue')
|
|
325
|
+
.action(async (id: string) => {
|
|
326
|
+
try {
|
|
327
|
+
const client = getClient();
|
|
328
|
+
await client.issues.archive(id);
|
|
329
|
+
success(`Issue ${id} archived`);
|
|
330
|
+
} catch (e) {
|
|
331
|
+
error((e as Error).message);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
issuesCmd
|
|
337
|
+
.command('search <query>')
|
|
338
|
+
.description('Search issues')
|
|
339
|
+
.option('-l, --limit <n>', 'Maximum results', '20')
|
|
340
|
+
.action(async (query: string, opts) => {
|
|
341
|
+
try {
|
|
342
|
+
const client = getClient();
|
|
343
|
+
const format = program.opts().format as OutputFormat;
|
|
344
|
+
const issues = await client.issues.search(query, parseInt(opts.limit, 10));
|
|
345
|
+
|
|
346
|
+
if (format === 'json') {
|
|
347
|
+
print(issues, format);
|
|
348
|
+
} else {
|
|
349
|
+
print(issues.map(i => ({
|
|
350
|
+
id: i.identifier,
|
|
351
|
+
title: i.title.substring(0, 50) + (i.title.length > 50 ? '...' : ''),
|
|
352
|
+
status: i.state?.name || 'Unknown',
|
|
353
|
+
team: i.team?.key || 'Unknown',
|
|
354
|
+
})), format);
|
|
355
|
+
}
|
|
356
|
+
} catch (e) {
|
|
357
|
+
error((e as Error).message);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// ============================================
|
|
363
|
+
// Project Commands
|
|
364
|
+
// ============================================
|
|
365
|
+
|
|
366
|
+
const projectsCmd = program
|
|
367
|
+
.command('projects')
|
|
368
|
+
.description('Project commands');
|
|
369
|
+
|
|
370
|
+
projectsCmd
|
|
371
|
+
.command('list')
|
|
372
|
+
.description('List projects')
|
|
373
|
+
.option('-l, --limit <n>', 'Maximum number to return', '50')
|
|
374
|
+
.action(async (opts) => {
|
|
375
|
+
try {
|
|
376
|
+
const client = getClient();
|
|
377
|
+
const format = program.opts().format as OutputFormat;
|
|
378
|
+
const projects = await client.projects.list({
|
|
379
|
+
first: parseInt(opts.limit, 10),
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
if (format === 'json') {
|
|
383
|
+
print(projects, format);
|
|
384
|
+
} else {
|
|
385
|
+
print(projects.map(p => ({
|
|
386
|
+
id: p.id,
|
|
387
|
+
name: p.name,
|
|
388
|
+
state: p.state,
|
|
389
|
+
progress: `${Math.round(p.progress * 100)}%`,
|
|
390
|
+
lead: p.lead?.displayName || 'None',
|
|
391
|
+
})), format);
|
|
392
|
+
}
|
|
393
|
+
} catch (e) {
|
|
394
|
+
error((e as Error).message);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
projectsCmd
|
|
400
|
+
.command('get <id>')
|
|
401
|
+
.description('Get project details')
|
|
402
|
+
.action(async (id: string) => {
|
|
403
|
+
try {
|
|
404
|
+
const client = getClient();
|
|
405
|
+
const format = program.opts().format as OutputFormat;
|
|
406
|
+
const project = await client.projects.get(id);
|
|
407
|
+
print(project, format);
|
|
408
|
+
} catch (e) {
|
|
409
|
+
error((e as Error).message);
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// ============================================
|
|
415
|
+
// Team Commands
|
|
416
|
+
// ============================================
|
|
417
|
+
|
|
418
|
+
const teamsCmd = program
|
|
419
|
+
.command('teams')
|
|
420
|
+
.description('Team commands');
|
|
421
|
+
|
|
422
|
+
teamsCmd
|
|
423
|
+
.command('list')
|
|
424
|
+
.description('List teams')
|
|
425
|
+
.action(async () => {
|
|
426
|
+
try {
|
|
427
|
+
const client = getClient();
|
|
428
|
+
const format = program.opts().format as OutputFormat;
|
|
429
|
+
const teams = await client.teams.list();
|
|
430
|
+
|
|
431
|
+
if (format === 'json') {
|
|
432
|
+
print(teams, format);
|
|
433
|
+
} else {
|
|
434
|
+
print(teams.map(t => ({
|
|
435
|
+
id: t.id,
|
|
436
|
+
key: t.key,
|
|
437
|
+
name: t.name,
|
|
438
|
+
private: t.private ? 'yes' : 'no',
|
|
439
|
+
})), format);
|
|
440
|
+
}
|
|
441
|
+
} catch (e) {
|
|
442
|
+
error((e as Error).message);
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
teamsCmd
|
|
448
|
+
.command('get <id>')
|
|
449
|
+
.description('Get team details')
|
|
450
|
+
.action(async (id: string) => {
|
|
451
|
+
try {
|
|
452
|
+
const client = getClient();
|
|
453
|
+
const format = program.opts().format as OutputFormat;
|
|
454
|
+
const team = await client.teams.get(id);
|
|
455
|
+
print(team, format);
|
|
456
|
+
} catch (e) {
|
|
457
|
+
error((e as Error).message);
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
teamsCmd
|
|
463
|
+
.command('states <teamId>')
|
|
464
|
+
.description('List workflow states for a team')
|
|
465
|
+
.action(async (teamId: string) => {
|
|
466
|
+
try {
|
|
467
|
+
const client = getClient();
|
|
468
|
+
const format = program.opts().format as OutputFormat;
|
|
469
|
+
const states = await client.teams.getWorkflowStates(teamId);
|
|
470
|
+
|
|
471
|
+
if (format === 'json') {
|
|
472
|
+
print(states, format);
|
|
473
|
+
} else {
|
|
474
|
+
print(states.map(s => ({
|
|
475
|
+
id: s.id,
|
|
476
|
+
name: s.name,
|
|
477
|
+
type: s.type,
|
|
478
|
+
color: s.color,
|
|
479
|
+
})), format);
|
|
480
|
+
}
|
|
481
|
+
} catch (e) {
|
|
482
|
+
error((e as Error).message);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// ============================================
|
|
488
|
+
// User Commands
|
|
489
|
+
// ============================================
|
|
490
|
+
|
|
491
|
+
const usersCmd = program
|
|
492
|
+
.command('users')
|
|
493
|
+
.description('User commands');
|
|
494
|
+
|
|
495
|
+
usersCmd
|
|
496
|
+
.command('list')
|
|
497
|
+
.description('List users')
|
|
498
|
+
.option('-l, --limit <n>', 'Maximum number to return', '100')
|
|
499
|
+
.option('--all', 'Include inactive users')
|
|
500
|
+
.action(async (opts) => {
|
|
501
|
+
try {
|
|
502
|
+
const client = getClient();
|
|
503
|
+
const format = program.opts().format as OutputFormat;
|
|
504
|
+
|
|
505
|
+
let users;
|
|
506
|
+
if (opts.all) {
|
|
507
|
+
users = await client.users.list({ first: parseInt(opts.limit, 10) });
|
|
508
|
+
} else {
|
|
509
|
+
users = await client.users.listActive({ first: parseInt(opts.limit, 10) });
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (format === 'json') {
|
|
513
|
+
print(users, format);
|
|
514
|
+
} else {
|
|
515
|
+
print(users.map(u => ({
|
|
516
|
+
id: u.id,
|
|
517
|
+
name: u.name,
|
|
518
|
+
displayName: u.displayName,
|
|
519
|
+
email: u.email,
|
|
520
|
+
admin: u.admin ? 'yes' : 'no',
|
|
521
|
+
active: u.active ? 'yes' : 'no',
|
|
522
|
+
})), format);
|
|
523
|
+
}
|
|
524
|
+
} catch (e) {
|
|
525
|
+
error((e as Error).message);
|
|
526
|
+
process.exit(1);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
usersCmd
|
|
531
|
+
.command('get <id>')
|
|
532
|
+
.description('Get user details')
|
|
533
|
+
.action(async (id: string) => {
|
|
534
|
+
try {
|
|
535
|
+
const client = getClient();
|
|
536
|
+
const format = program.opts().format as OutputFormat;
|
|
537
|
+
const user = await client.users.get(id);
|
|
538
|
+
print(user, format);
|
|
539
|
+
} catch (e) {
|
|
540
|
+
error((e as Error).message);
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
usersCmd
|
|
546
|
+
.command('me')
|
|
547
|
+
.description('Show current authenticated user')
|
|
548
|
+
.action(async () => {
|
|
549
|
+
try {
|
|
550
|
+
const client = getClient();
|
|
551
|
+
const format = program.opts().format as OutputFormat;
|
|
552
|
+
const user = await client.users.me();
|
|
553
|
+
print(user, format);
|
|
554
|
+
} catch (e) {
|
|
555
|
+
error((e as Error).message);
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
program.parse();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Main library exports
|
|
2
|
+
export { Linear, LinearClient, IssuesApi, ProjectsApi, TeamsApi, UsersApi } from './api';
|
|
3
|
+
|
|
4
|
+
// Type exports
|
|
5
|
+
export type {
|
|
6
|
+
LinearConfig,
|
|
7
|
+
LinearUser,
|
|
8
|
+
LinearTeam,
|
|
9
|
+
LinearIssue,
|
|
10
|
+
LinearProject,
|
|
11
|
+
LinearComment,
|
|
12
|
+
LinearCycle,
|
|
13
|
+
LinearLabel,
|
|
14
|
+
LinearWorkflowState,
|
|
15
|
+
LinearPriority,
|
|
16
|
+
GraphQLResponse,
|
|
17
|
+
Connection,
|
|
18
|
+
PageInfo,
|
|
19
|
+
ListOptions,
|
|
20
|
+
IssueListOptions,
|
|
21
|
+
IssueFilter,
|
|
22
|
+
CreateIssueInput,
|
|
23
|
+
UpdateIssueInput,
|
|
24
|
+
OutputFormat,
|
|
25
|
+
} from './types';
|
|
26
|
+
|
|
27
|
+
export { LinearApiError } from './types';
|