@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,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';
|