@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.
Files changed (127) hide show
  1. package/bin/index.js +71 -1
  2. package/bin/mcp.js +71 -1
  3. package/bin/serve.js +70 -0
  4. package/connectors/connect-asana/.env.example +11 -0
  5. package/connectors/connect-asana/CLAUDE.md +128 -0
  6. package/connectors/connect-asana/README.md +193 -0
  7. package/connectors/connect-asana/package.json +52 -0
  8. package/connectors/connect-asana/src/api/client.ts +119 -0
  9. package/connectors/connect-asana/src/api/index.ts +319 -0
  10. package/connectors/connect-asana/src/cli/index.ts +731 -0
  11. package/connectors/connect-asana/src/index.ts +19 -0
  12. package/connectors/connect-asana/src/types/index.ts +270 -0
  13. package/connectors/connect-asana/src/utils/config.ts +171 -0
  14. package/connectors/connect-asana/src/utils/output.ts +119 -0
  15. package/connectors/connect-asana/tsconfig.json +16 -0
  16. package/connectors/connect-clickup/.env.example +11 -0
  17. package/connectors/connect-clickup/CLAUDE.md +128 -0
  18. package/connectors/connect-clickup/README.md +193 -0
  19. package/connectors/connect-clickup/package.json +52 -0
  20. package/connectors/connect-clickup/src/api/client.ts +116 -0
  21. package/connectors/connect-clickup/src/api/index.ts +400 -0
  22. package/connectors/connect-clickup/src/cli/index.ts +625 -0
  23. package/connectors/connect-clickup/src/index.ts +19 -0
  24. package/connectors/connect-clickup/src/types/index.ts +591 -0
  25. package/connectors/connect-clickup/src/utils/config.ts +157 -0
  26. package/connectors/connect-clickup/src/utils/output.ts +119 -0
  27. package/connectors/connect-clickup/tsconfig.json +16 -0
  28. package/connectors/connect-confluence/.env.example +11 -0
  29. package/connectors/connect-confluence/CLAUDE.md +272 -0
  30. package/connectors/connect-confluence/README.md +193 -0
  31. package/connectors/connect-confluence/package.json +53 -0
  32. package/connectors/connect-confluence/scripts/release.ts +179 -0
  33. package/connectors/connect-confluence/src/api/client.ts +213 -0
  34. package/connectors/connect-confluence/src/api/example.ts +48 -0
  35. package/connectors/connect-confluence/src/api/index.ts +51 -0
  36. package/connectors/connect-confluence/src/cli/index.ts +254 -0
  37. package/connectors/connect-confluence/src/index.ts +103 -0
  38. package/connectors/connect-confluence/src/types/index.ts +237 -0
  39. package/connectors/connect-confluence/src/utils/auth.ts +274 -0
  40. package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
  41. package/connectors/connect-confluence/src/utils/config.ts +326 -0
  42. package/connectors/connect-confluence/src/utils/output.ts +175 -0
  43. package/connectors/connect-confluence/src/utils/settings.ts +114 -0
  44. package/connectors/connect-confluence/src/utils/storage.ts +198 -0
  45. package/connectors/connect-confluence/tsconfig.json +16 -0
  46. package/connectors/connect-jira/.env.example +11 -0
  47. package/connectors/connect-jira/CLAUDE.md +128 -0
  48. package/connectors/connect-jira/README.md +193 -0
  49. package/connectors/connect-jira/package.json +53 -0
  50. package/connectors/connect-jira/src/api/client.ts +131 -0
  51. package/connectors/connect-jira/src/api/index.ts +266 -0
  52. package/connectors/connect-jira/src/cli/index.ts +653 -0
  53. package/connectors/connect-jira/src/index.ts +23 -0
  54. package/connectors/connect-jira/src/types/index.ts +448 -0
  55. package/connectors/connect-jira/src/utils/config.ts +179 -0
  56. package/connectors/connect-jira/src/utils/output.ts +119 -0
  57. package/connectors/connect-jira/tsconfig.json +16 -0
  58. package/connectors/connect-linear/CLAUDE.md +88 -0
  59. package/connectors/connect-linear/README.md +201 -0
  60. package/connectors/connect-linear/package.json +45 -0
  61. package/connectors/connect-linear/src/api/client.ts +62 -0
  62. package/connectors/connect-linear/src/api/index.ts +46 -0
  63. package/connectors/connect-linear/src/api/issues.ts +247 -0
  64. package/connectors/connect-linear/src/api/projects.ts +179 -0
  65. package/connectors/connect-linear/src/api/teams.ts +125 -0
  66. package/connectors/connect-linear/src/api/users.ts +112 -0
  67. package/connectors/connect-linear/src/cli/index.ts +560 -0
  68. package/connectors/connect-linear/src/index.ts +27 -0
  69. package/connectors/connect-linear/src/types/index.ts +275 -0
  70. package/connectors/connect-linear/src/utils/config.ts +249 -0
  71. package/connectors/connect-linear/src/utils/output.ts +119 -0
  72. package/connectors/connect-linear/tsconfig.json +16 -0
  73. package/connectors/connect-slack/.env.example +7 -0
  74. package/connectors/connect-slack/CLAUDE.md +69 -0
  75. package/connectors/connect-slack/README.md +150 -0
  76. package/connectors/connect-slack/package.json +44 -0
  77. package/connectors/connect-slack/src/api/channels.ts +112 -0
  78. package/connectors/connect-slack/src/api/client.ts +97 -0
  79. package/connectors/connect-slack/src/api/index.ts +42 -0
  80. package/connectors/connect-slack/src/api/messages.ts +127 -0
  81. package/connectors/connect-slack/src/api/users.ts +110 -0
  82. package/connectors/connect-slack/src/cli/index.ts +494 -0
  83. package/connectors/connect-slack/src/index.ts +21 -0
  84. package/connectors/connect-slack/src/types/index.ts +263 -0
  85. package/connectors/connect-slack/src/utils/config.ts +297 -0
  86. package/connectors/connect-slack/src/utils/output.ts +119 -0
  87. package/connectors/connect-slack/tsconfig.json +16 -0
  88. package/connectors/connect-telegram/.env.example +2 -0
  89. package/connectors/connect-telegram/package.json +49 -0
  90. package/connectors/connect-todoist/.env.example +11 -0
  91. package/connectors/connect-todoist/CLAUDE.md +104 -0
  92. package/connectors/connect-todoist/README.md +193 -0
  93. package/connectors/connect-todoist/package.json +52 -0
  94. package/connectors/connect-todoist/src/api/client.ts +117 -0
  95. package/connectors/connect-todoist/src/api/index.ts +188 -0
  96. package/connectors/connect-todoist/src/cli/index.ts +990 -0
  97. package/connectors/connect-todoist/src/index.ts +21 -0
  98. package/connectors/connect-todoist/src/types/index.ts +240 -0
  99. package/connectors/connect-todoist/src/utils/config.ts +157 -0
  100. package/connectors/connect-todoist/src/utils/output.ts +119 -0
  101. package/connectors/connect-todoist/tsconfig.json +16 -0
  102. package/connectors/connect-trello/.env.example +11 -0
  103. package/connectors/connect-trello/CLAUDE.md +128 -0
  104. package/connectors/connect-trello/README.md +193 -0
  105. package/connectors/connect-trello/package.json +53 -0
  106. package/connectors/connect-trello/src/api/client.ts +128 -0
  107. package/connectors/connect-trello/src/api/index.ts +278 -0
  108. package/connectors/connect-trello/src/cli/index.ts +737 -0
  109. package/connectors/connect-trello/src/index.ts +21 -0
  110. package/connectors/connect-trello/src/types/index.ts +314 -0
  111. package/connectors/connect-trello/src/utils/config.ts +182 -0
  112. package/connectors/connect-trello/src/utils/output.ts +119 -0
  113. package/connectors/connect-trello/tsconfig.json +16 -0
  114. package/connectors/connect-whatsapp/.env.example +11 -0
  115. package/connectors/connect-whatsapp/CLAUDE.md +113 -0
  116. package/connectors/connect-whatsapp/README.md +193 -0
  117. package/connectors/connect-whatsapp/package.json +53 -0
  118. package/connectors/connect-whatsapp/src/api/client.ts +133 -0
  119. package/connectors/connect-whatsapp/src/api/index.ts +365 -0
  120. package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
  121. package/connectors/connect-whatsapp/src/index.ts +25 -0
  122. package/connectors/connect-whatsapp/src/types/index.ts +502 -0
  123. package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
  124. package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
  125. package/connectors/connect-whatsapp/tsconfig.json +16 -0
  126. package/dist/index.js +70 -0
  127. package/package.json +1 -1
@@ -0,0 +1,653 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { Jira } from '../api';
5
+ import {
6
+ getEmail,
7
+ setEmail,
8
+ getApiToken,
9
+ setApiToken,
10
+ getDomain,
11
+ setDomain,
12
+ clearConfig,
13
+ getConfigDir,
14
+ setProfileOverride,
15
+ getCurrentProfile,
16
+ setCurrentProfile,
17
+ listProfiles,
18
+ createProfile,
19
+ deleteProfile,
20
+ profileExists,
21
+ loadProfile,
22
+ } from '../utils/config';
23
+ import type { OutputFormat } from '../utils/output';
24
+ import { success, error, info, print } from '../utils/output';
25
+
26
+ const CONNECTOR_NAME = 'connect-jira';
27
+ const VERSION = '0.0.1';
28
+
29
+ const program = new Command();
30
+
31
+ program
32
+ .name(CONNECTOR_NAME)
33
+ .description('Jira connector CLI - Projects, issues, boards, and sprints management')
34
+ .version(VERSION)
35
+ .option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
36
+ .option('-p, --profile <profile>', 'Use a specific profile')
37
+ .hook('preAction', (thisCommand) => {
38
+ const opts = thisCommand.opts();
39
+ if (opts.profile) {
40
+ if (!profileExists(opts.profile)) {
41
+ error(`Profile "${opts.profile}" does not exist. Create it with "${CONNECTOR_NAME} profile create ${opts.profile}"`);
42
+ process.exit(1);
43
+ }
44
+ setProfileOverride(opts.profile);
45
+ }
46
+ });
47
+
48
+ function getFormat(cmd: Command): OutputFormat {
49
+ const parent = cmd.parent;
50
+ return (parent?.opts().format || 'pretty') as OutputFormat;
51
+ }
52
+
53
+ function getClient(): Jira {
54
+ const email = getEmail();
55
+ const apiToken = getApiToken();
56
+ const domain = getDomain();
57
+
58
+ if (!email || !apiToken || !domain) {
59
+ error(`Configuration incomplete. Run "${CONNECTOR_NAME} config setup" or set JIRA_EMAIL, JIRA_API_TOKEN, JIRA_DOMAIN environment variables.`);
60
+ process.exit(1);
61
+ }
62
+ return new Jira({ email, apiToken, domain });
63
+ }
64
+
65
+ // ============================================
66
+ // Profile Commands
67
+ // ============================================
68
+ const profileCmd = program
69
+ .command('profile')
70
+ .description('Manage configuration profiles');
71
+
72
+ profileCmd
73
+ .command('list')
74
+ .description('List all profiles')
75
+ .action(() => {
76
+ const profiles = listProfiles();
77
+ const current = getCurrentProfile();
78
+
79
+ if (profiles.length === 0) {
80
+ info('No profiles found. Use "profile create <name>" to create one.');
81
+ return;
82
+ }
83
+
84
+ success(`Profiles:`);
85
+ profiles.forEach(p => {
86
+ const isActive = p === current ? chalk.green(' (active)') : '';
87
+ console.log(` ${p}${isActive}`);
88
+ });
89
+ });
90
+
91
+ profileCmd
92
+ .command('use <name>')
93
+ .description('Switch to a profile')
94
+ .action((name: string) => {
95
+ if (!profileExists(name)) {
96
+ error(`Profile "${name}" does not exist. Create it with "profile create ${name}"`);
97
+ process.exit(1);
98
+ }
99
+ setCurrentProfile(name);
100
+ success(`Switched to profile: ${name}`);
101
+ });
102
+
103
+ profileCmd
104
+ .command('create <name>')
105
+ .description('Create a new profile')
106
+ .option('--email <email>', 'Jira email')
107
+ .option('--token <token>', 'API token')
108
+ .option('--domain <domain>', 'Jira domain (e.g., mycompany.atlassian.net)')
109
+ .option('--use', 'Switch to this profile after creation')
110
+ .action((name: string, opts) => {
111
+ if (profileExists(name)) {
112
+ error(`Profile "${name}" already exists`);
113
+ process.exit(1);
114
+ }
115
+
116
+ createProfile(name, {
117
+ email: opts.email,
118
+ apiToken: opts.token,
119
+ domain: opts.domain,
120
+ });
121
+ success(`Profile "${name}" created`);
122
+
123
+ if (opts.use) {
124
+ setCurrentProfile(name);
125
+ info(`Switched to profile: ${name}`);
126
+ }
127
+ });
128
+
129
+ profileCmd
130
+ .command('delete <name>')
131
+ .description('Delete a profile')
132
+ .action((name: string) => {
133
+ if (name === 'default') {
134
+ error('Cannot delete the default profile');
135
+ process.exit(1);
136
+ }
137
+ if (deleteProfile(name)) {
138
+ success(`Profile "${name}" deleted`);
139
+ } else {
140
+ error(`Profile "${name}" not found`);
141
+ process.exit(1);
142
+ }
143
+ });
144
+
145
+ profileCmd
146
+ .command('show [name]')
147
+ .description('Show profile configuration')
148
+ .action((name?: string) => {
149
+ const profileName = name || getCurrentProfile();
150
+ const config = loadProfile(profileName);
151
+ const active = getCurrentProfile();
152
+
153
+ console.log(chalk.bold(`Profile: ${profileName}${profileName === active ? chalk.green(' (active)') : ''}`));
154
+ info(`Email: ${config.email || chalk.gray('not set')}`);
155
+ info(`API Token: ${config.apiToken ? `${config.apiToken.substring(0, 8)}...` : chalk.gray('not set')}`);
156
+ info(`Domain: ${config.domain || chalk.gray('not set')}`);
157
+ });
158
+
159
+ // ============================================
160
+ // Config Commands
161
+ // ============================================
162
+ const configCmd = program
163
+ .command('config')
164
+ .description('Manage CLI configuration');
165
+
166
+ configCmd
167
+ .command('setup')
168
+ .description('Configure Jira credentials')
169
+ .requiredOption('--email <email>', 'Jira email')
170
+ .requiredOption('--token <token>', 'API token')
171
+ .requiredOption('--domain <domain>', 'Jira domain (e.g., mycompany.atlassian.net)')
172
+ .action((opts) => {
173
+ setEmail(opts.email);
174
+ setApiToken(opts.token);
175
+ setDomain(opts.domain);
176
+ success(`Configuration saved to profile: ${getCurrentProfile()}`);
177
+ });
178
+
179
+ configCmd
180
+ .command('set-email <email>')
181
+ .description('Set Jira email')
182
+ .action((email: string) => {
183
+ setEmail(email);
184
+ success(`Email saved to profile: ${getCurrentProfile()}`);
185
+ });
186
+
187
+ configCmd
188
+ .command('set-token <token>')
189
+ .description('Set API token')
190
+ .action((token: string) => {
191
+ setApiToken(token);
192
+ success(`API token saved to profile: ${getCurrentProfile()}`);
193
+ });
194
+
195
+ configCmd
196
+ .command('set-domain <domain>')
197
+ .description('Set Jira domain')
198
+ .action((domain: string) => {
199
+ setDomain(domain);
200
+ success(`Domain saved to profile: ${getCurrentProfile()}`);
201
+ });
202
+
203
+ configCmd
204
+ .command('show')
205
+ .description('Show current configuration')
206
+ .action(() => {
207
+ const profileName = getCurrentProfile();
208
+ const email = getEmail();
209
+ const apiToken = getApiToken();
210
+ const domain = getDomain();
211
+
212
+ console.log(chalk.bold(`Active Profile: ${profileName}`));
213
+ info(`Config directory: ${getConfigDir()}`);
214
+ info(`Email: ${email || chalk.gray('not set')}`);
215
+ info(`API Token: ${apiToken ? `${apiToken.substring(0, 8)}...` : chalk.gray('not set')}`);
216
+ info(`Domain: ${domain || chalk.gray('not set')}`);
217
+ });
218
+
219
+ configCmd
220
+ .command('clear')
221
+ .description('Clear configuration')
222
+ .action(() => {
223
+ clearConfig();
224
+ success(`Configuration cleared for profile: ${getCurrentProfile()}`);
225
+ });
226
+
227
+ // ============================================
228
+ // User Commands
229
+ // ============================================
230
+ const userCmd = program
231
+ .command('user')
232
+ .description('User operations');
233
+
234
+ userCmd
235
+ .command('me')
236
+ .description('Get current authenticated user')
237
+ .action(async () => {
238
+ try {
239
+ const client = getClient();
240
+ const result = await client.getMyself();
241
+ print(result, getFormat(userCmd));
242
+ } catch (err) {
243
+ error(String(err));
244
+ process.exit(1);
245
+ }
246
+ });
247
+
248
+ userCmd
249
+ .command('search')
250
+ .description('Search users')
251
+ .option('-q, --query <query>', 'Search query')
252
+ .option('-l, --limit <number>', 'Maximum results', '50')
253
+ .action(async (opts) => {
254
+ try {
255
+ const client = getClient();
256
+ const result = await client.searchUsers({
257
+ query: opts.query,
258
+ maxResults: parseInt(opts.limit),
259
+ });
260
+ print(result, getFormat(userCmd));
261
+ } catch (err) {
262
+ error(String(err));
263
+ process.exit(1);
264
+ }
265
+ });
266
+
267
+ // ============================================
268
+ // Project Commands
269
+ // ============================================
270
+ const projectCmd = program
271
+ .command('project')
272
+ .description('Project operations');
273
+
274
+ projectCmd
275
+ .command('list')
276
+ .description('List projects')
277
+ .option('-l, --limit <number>', 'Maximum results', '50')
278
+ .option('--expand <fields>', 'Expand fields (comma-separated)')
279
+ .action(async (opts) => {
280
+ try {
281
+ const client = getClient();
282
+ const result = await client.listProjects({
283
+ maxResults: parseInt(opts.limit),
284
+ expand: opts.expand,
285
+ });
286
+ print(result.values, getFormat(projectCmd));
287
+ } catch (err) {
288
+ error(String(err));
289
+ process.exit(1);
290
+ }
291
+ });
292
+
293
+ projectCmd
294
+ .command('get <projectKeyOrId>')
295
+ .description('Get a project by key or ID')
296
+ .option('--expand <fields>', 'Expand fields (comma-separated)')
297
+ .action(async (projectKeyOrId: string, opts) => {
298
+ try {
299
+ const client = getClient();
300
+ const result = await client.getProject(projectKeyOrId, { expand: opts.expand });
301
+ print(result, getFormat(projectCmd));
302
+ } catch (err) {
303
+ error(String(err));
304
+ process.exit(1);
305
+ }
306
+ });
307
+
308
+ // ============================================
309
+ // Issue Commands
310
+ // ============================================
311
+ const issueCmd = program
312
+ .command('issue')
313
+ .description('Issue operations');
314
+
315
+ issueCmd
316
+ .command('search')
317
+ .description('Search issues with JQL')
318
+ .requiredOption('-j, --jql <jql>', 'JQL query')
319
+ .option('-l, --limit <number>', 'Maximum results', '50')
320
+ .option('--fields <fields>', 'Fields to return (comma-separated)')
321
+ .option('--expand <expand>', 'Expand fields')
322
+ .action(async (opts) => {
323
+ try {
324
+ const client = getClient();
325
+ const result = await client.searchIssues({
326
+ jql: opts.jql,
327
+ maxResults: parseInt(opts.limit),
328
+ fields: opts.fields ? opts.fields.split(',') : undefined,
329
+ expand: opts.expand,
330
+ });
331
+ print(result.issues, getFormat(issueCmd));
332
+ } catch (err) {
333
+ error(String(err));
334
+ process.exit(1);
335
+ }
336
+ });
337
+
338
+ issueCmd
339
+ .command('get <issueKeyOrId>')
340
+ .description('Get an issue by key or ID')
341
+ .option('--fields <fields>', 'Fields to return (comma-separated)')
342
+ .option('--expand <expand>', 'Expand fields')
343
+ .action(async (issueKeyOrId: string, opts) => {
344
+ try {
345
+ const client = getClient();
346
+ const result = await client.getIssue(issueKeyOrId, {
347
+ fields: opts.fields ? opts.fields.split(',') : undefined,
348
+ expand: opts.expand,
349
+ });
350
+ print(result, getFormat(issueCmd));
351
+ } catch (err) {
352
+ error(String(err));
353
+ process.exit(1);
354
+ }
355
+ });
356
+
357
+ issueCmd
358
+ .command('create')
359
+ .description('Create an issue')
360
+ .requiredOption('-p, --project <key>', 'Project key')
361
+ .requiredOption('-t, --type <type>', 'Issue type (e.g., Task, Bug, Story)')
362
+ .requiredOption('-s, --summary <summary>', 'Issue summary')
363
+ .option('-d, --description <description>', 'Issue description')
364
+ .option('--assignee <accountId>', 'Assignee account ID')
365
+ .option('--priority <name>', 'Priority name')
366
+ .option('--labels <labels>', 'Labels (comma-separated)')
367
+ .action(async (opts) => {
368
+ try {
369
+ const client = getClient();
370
+ const result = await client.createIssue({
371
+ fields: {
372
+ project: { key: opts.project },
373
+ issuetype: { name: opts.type },
374
+ summary: opts.summary,
375
+ description: opts.description,
376
+ assignee: opts.assignee ? { accountId: opts.assignee } : undefined,
377
+ priority: opts.priority ? { name: opts.priority } : undefined,
378
+ labels: opts.labels ? opts.labels.split(',') : undefined,
379
+ },
380
+ });
381
+ success(`Issue created: ${result.key}`);
382
+ print(result, getFormat(issueCmd));
383
+ } catch (err) {
384
+ error(String(err));
385
+ process.exit(1);
386
+ }
387
+ });
388
+
389
+ issueCmd
390
+ .command('update <issueKeyOrId>')
391
+ .description('Update an issue')
392
+ .option('-s, --summary <summary>', 'Issue summary')
393
+ .option('-d, --description <description>', 'Issue description')
394
+ .option('--priority <name>', 'Priority name')
395
+ .option('--labels <labels>', 'Labels (comma-separated)')
396
+ .action(async (issueKeyOrId: string, opts) => {
397
+ try {
398
+ const client = getClient();
399
+ const fields: Record<string, unknown> = {};
400
+ if (opts.summary) fields.summary = opts.summary;
401
+ if (opts.description) fields.description = opts.description;
402
+ if (opts.priority) fields.priority = { name: opts.priority };
403
+ if (opts.labels) fields.labels = opts.labels.split(',');
404
+
405
+ await client.updateIssue(issueKeyOrId, { fields });
406
+ success(`Issue ${issueKeyOrId} updated!`);
407
+ } catch (err) {
408
+ error(String(err));
409
+ process.exit(1);
410
+ }
411
+ });
412
+
413
+ issueCmd
414
+ .command('delete <issueKeyOrId>')
415
+ .description('Delete an issue')
416
+ .option('--delete-subtasks', 'Also delete subtasks')
417
+ .action(async (issueKeyOrId: string, opts) => {
418
+ try {
419
+ const client = getClient();
420
+ await client.deleteIssue(issueKeyOrId, { deleteSubtasks: opts.deleteSubtasks });
421
+ success(`Issue ${issueKeyOrId} deleted!`);
422
+ } catch (err) {
423
+ error(String(err));
424
+ process.exit(1);
425
+ }
426
+ });
427
+
428
+ issueCmd
429
+ .command('assign <issueKeyOrId>')
430
+ .description('Assign an issue')
431
+ .option('-u, --user <accountId>', 'User account ID (omit to unassign)')
432
+ .action(async (issueKeyOrId: string, opts) => {
433
+ try {
434
+ const client = getClient();
435
+ await client.assignIssue(issueKeyOrId, opts.user || null);
436
+ success(`Issue ${issueKeyOrId} ${opts.user ? 'assigned' : 'unassigned'}!`);
437
+ } catch (err) {
438
+ error(String(err));
439
+ process.exit(1);
440
+ }
441
+ });
442
+
443
+ // ============================================
444
+ // Transition Commands
445
+ // ============================================
446
+ const transitionCmd = program
447
+ .command('transition')
448
+ .description('Issue transition operations');
449
+
450
+ transitionCmd
451
+ .command('list <issueKeyOrId>')
452
+ .description('List available transitions')
453
+ .action(async (issueKeyOrId: string) => {
454
+ try {
455
+ const client = getClient();
456
+ const result = await client.getTransitions(issueKeyOrId);
457
+ print(result.transitions, getFormat(transitionCmd));
458
+ } catch (err) {
459
+ error(String(err));
460
+ process.exit(1);
461
+ }
462
+ });
463
+
464
+ transitionCmd
465
+ .command('do <issueKeyOrId> <transitionId>')
466
+ .description('Transition an issue')
467
+ .action(async (issueKeyOrId: string, transitionId: string) => {
468
+ try {
469
+ const client = getClient();
470
+ await client.transitionIssue(issueKeyOrId, { transition: { id: transitionId } });
471
+ success(`Issue ${issueKeyOrId} transitioned!`);
472
+ } catch (err) {
473
+ error(String(err));
474
+ process.exit(1);
475
+ }
476
+ });
477
+
478
+ // ============================================
479
+ // Comment Commands
480
+ // ============================================
481
+ const commentCmd = program
482
+ .command('comment')
483
+ .description('Comment operations');
484
+
485
+ commentCmd
486
+ .command('list <issueKeyOrId>')
487
+ .description('List comments on an issue')
488
+ .option('-l, --limit <number>', 'Maximum results', '50')
489
+ .action(async (issueKeyOrId: string, opts) => {
490
+ try {
491
+ const client = getClient();
492
+ const result = await client.getComments(issueKeyOrId, {
493
+ maxResults: parseInt(opts.limit),
494
+ });
495
+ print(result.comments, getFormat(commentCmd));
496
+ } catch (err) {
497
+ error(String(err));
498
+ process.exit(1);
499
+ }
500
+ });
501
+
502
+ commentCmd
503
+ .command('add <issueKeyOrId>')
504
+ .description('Add a comment to an issue')
505
+ .requiredOption('-b, --body <text>', 'Comment body')
506
+ .action(async (issueKeyOrId: string, opts) => {
507
+ try {
508
+ const client = getClient();
509
+ const result = await client.addComment(issueKeyOrId, { body: opts.body });
510
+ success('Comment added!');
511
+ print(result, getFormat(commentCmd));
512
+ } catch (err) {
513
+ error(String(err));
514
+ process.exit(1);
515
+ }
516
+ });
517
+
518
+ commentCmd
519
+ .command('delete <issueKeyOrId> <commentId>')
520
+ .description('Delete a comment')
521
+ .action(async (issueKeyOrId: string, commentId: string) => {
522
+ try {
523
+ const client = getClient();
524
+ await client.deleteComment(issueKeyOrId, commentId);
525
+ success(`Comment ${commentId} deleted!`);
526
+ } catch (err) {
527
+ error(String(err));
528
+ process.exit(1);
529
+ }
530
+ });
531
+
532
+ // ============================================
533
+ // Board Commands
534
+ // ============================================
535
+ const boardCmd = program
536
+ .command('board')
537
+ .description('Board operations (Agile)');
538
+
539
+ boardCmd
540
+ .command('list')
541
+ .description('List boards')
542
+ .option('-l, --limit <number>', 'Maximum results', '50')
543
+ .option('--type <type>', 'Board type: scrum, kanban, simple')
544
+ .option('--name <name>', 'Filter by name')
545
+ .option('--project <projectKeyOrId>', 'Filter by project')
546
+ .action(async (opts) => {
547
+ try {
548
+ const client = getClient();
549
+ const result = await client.listBoards({
550
+ maxResults: parseInt(opts.limit),
551
+ type: opts.type,
552
+ name: opts.name,
553
+ projectKeyOrId: opts.project,
554
+ });
555
+ print(result.values, getFormat(boardCmd));
556
+ } catch (err) {
557
+ error(String(err));
558
+ process.exit(1);
559
+ }
560
+ });
561
+
562
+ boardCmd
563
+ .command('get <boardId>')
564
+ .description('Get a board by ID')
565
+ .action(async (boardId: string) => {
566
+ try {
567
+ const client = getClient();
568
+ const result = await client.getBoard(parseInt(boardId));
569
+ print(result, getFormat(boardCmd));
570
+ } catch (err) {
571
+ error(String(err));
572
+ process.exit(1);
573
+ }
574
+ });
575
+
576
+ boardCmd
577
+ .command('issues <boardId>')
578
+ .description('List issues on a board')
579
+ .option('-l, --limit <number>', 'Maximum results', '50')
580
+ .option('-j, --jql <jql>', 'Additional JQL filter')
581
+ .action(async (boardId: string, opts) => {
582
+ try {
583
+ const client = getClient();
584
+ const result = await client.getBoardIssues(parseInt(boardId), {
585
+ maxResults: parseInt(opts.limit),
586
+ jql: opts.jql,
587
+ });
588
+ print(result.issues, getFormat(boardCmd));
589
+ } catch (err) {
590
+ error(String(err));
591
+ process.exit(1);
592
+ }
593
+ });
594
+
595
+ // ============================================
596
+ // Sprint Commands
597
+ // ============================================
598
+ const sprintCmd = program
599
+ .command('sprint')
600
+ .description('Sprint operations');
601
+
602
+ sprintCmd
603
+ .command('list <boardId>')
604
+ .description('List sprints for a board')
605
+ .option('-l, --limit <number>', 'Maximum results', '50')
606
+ .option('--state <state>', 'Sprint state: future, active, closed')
607
+ .action(async (boardId: string, opts) => {
608
+ try {
609
+ const client = getClient();
610
+ const result = await client.listSprints(parseInt(boardId), {
611
+ maxResults: parseInt(opts.limit),
612
+ state: opts.state,
613
+ });
614
+ print(result.values, getFormat(sprintCmd));
615
+ } catch (err) {
616
+ error(String(err));
617
+ process.exit(1);
618
+ }
619
+ });
620
+
621
+ sprintCmd
622
+ .command('get <sprintId>')
623
+ .description('Get a sprint by ID')
624
+ .action(async (sprintId: string) => {
625
+ try {
626
+ const client = getClient();
627
+ const result = await client.getSprint(parseInt(sprintId));
628
+ print(result, getFormat(sprintCmd));
629
+ } catch (err) {
630
+ error(String(err));
631
+ process.exit(1);
632
+ }
633
+ });
634
+
635
+ sprintCmd
636
+ .command('issues <sprintId>')
637
+ .description('List issues in a sprint')
638
+ .option('-l, --limit <number>', 'Maximum results', '50')
639
+ .action(async (sprintId: string, opts) => {
640
+ try {
641
+ const client = getClient();
642
+ const result = await client.getSprintIssues(parseInt(sprintId), {
643
+ maxResults: parseInt(opts.limit),
644
+ });
645
+ print(result.issues, getFormat(sprintCmd));
646
+ } catch (err) {
647
+ error(String(err));
648
+ process.exit(1);
649
+ }
650
+ });
651
+
652
+ // Parse and execute
653
+ program.parse();
@@ -0,0 +1,23 @@
1
+ // Jira Connector
2
+ // TypeScript wrapper for Jira projects, issues, boards, and sprints API
3
+
4
+ export { Jira } from './api';
5
+ export * from './types';
6
+ export { JiraClient } from './api';
7
+
8
+ export {
9
+ getEmail,
10
+ setEmail,
11
+ getApiToken,
12
+ setApiToken,
13
+ getDomain,
14
+ setDomain,
15
+ getCurrentProfile,
16
+ setCurrentProfile,
17
+ listProfiles,
18
+ createProfile,
19
+ deleteProfile,
20
+ loadProfile,
21
+ saveProfile,
22
+ clearConfig,
23
+ } from './utils/config';