@profoundlogic/coderflow-cli 0.3.0 → 0.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profoundlogic/coderflow-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "AI Coder CLI - Command-line interface for managing AI coding tasks",
5
5
  "main": "coder.js",
6
6
  "type": "module",
@@ -1,464 +0,0 @@
1
- /**
2
- * Command: coder jira - JIRA integration management
3
- */
4
-
5
- import readline from 'readline';
6
- import { promises as fs } from 'fs';
7
- import path from 'path';
8
- import { exec } from 'child_process';
9
- import { promisify } from 'util';
10
- import { getCoderSetupPath } from '../config.js';
11
-
12
- const execAsync = promisify(exec);
13
-
14
- /**
15
- * Prompt for input from stdin
16
- */
17
- function prompt(question) {
18
- const rl = readline.createInterface({
19
- input: process.stdin,
20
- output: process.stdout
21
- });
22
-
23
- return new Promise((resolve) => {
24
- rl.question(question, (answer) => {
25
- rl.close();
26
- resolve(answer);
27
- });
28
- });
29
- }
30
-
31
- /**
32
- * Prompt for API token (visible input for easy paste verification)
33
- */
34
- function promptSecret(question) {
35
- // Use regular prompt - hiding prevents paste from working properly
36
- // and adds no real security value for a pasted token in local CLI
37
- return prompt(question);
38
- }
39
-
40
- /**
41
- * Test JIRA connection
42
- */
43
- async function testJiraConnection(baseUrl, email, apiToken) {
44
- try {
45
- const auth = Buffer.from(`${email}:${apiToken}`).toString('base64');
46
-
47
- // Use node's fetch (Node 18+) or dynamic import
48
- const response = await fetch(`${baseUrl}/rest/api/3/myself`, {
49
- headers: {
50
- 'Authorization': `Basic ${auth}`,
51
- 'Accept': 'application/json'
52
- }
53
- });
54
-
55
- if (response.ok) {
56
- const user = await response.json();
57
- return { success: true, user };
58
- } else {
59
- const error = await response.text();
60
- return { success: false, error: `HTTP ${response.status}: ${error}` };
61
- }
62
- } catch (error) {
63
- return { success: false, error: error.message };
64
- }
65
- }
66
-
67
- /**
68
- * Open browser to JIRA API token page
69
- */
70
- async function openTokenPage() {
71
- const url = 'https://id.atlassian.com/manage-profile/security/api-tokens';
72
-
73
- try {
74
- const platform = process.platform;
75
- let command;
76
-
77
- if (platform === 'darwin') {
78
- command = `open "${url}"`;
79
- } else if (platform === 'win32') {
80
- command = `start "${url}"`;
81
- } else {
82
- command = `xdg-open "${url}"`;
83
- }
84
-
85
- await execAsync(command);
86
- return true;
87
- } catch (error) {
88
- console.warn('Could not open browser automatically.');
89
- console.log(`Please visit: ${url}`);
90
- return false;
91
- }
92
- }
93
-
94
- /**
95
- * Connect to JIRA - interactive setup
96
- */
97
- export async function connect(args) {
98
- console.log('═══════════════════════════════════════');
99
- console.log(' JIRA Integration Setup');
100
- console.log('═══════════════════════════════════════');
101
- console.log('');
102
-
103
- // Get coder-setup path
104
- let setupPath = args.setup || args['setup-path'];
105
- if (!setupPath) {
106
- setupPath = await getCoderSetupPath();
107
- }
108
-
109
- if (!setupPath) {
110
- console.error('Error: No coder-setup path configured.');
111
- console.log('');
112
- console.log('Please specify a setup path using one of:');
113
- console.log(' • coder jira connect --setup-path /path/to/setup');
114
- console.log(' • coder server start --setup /path/to/setup (then try again)');
115
- console.log(' • Set CODER_SETUP_PATH environment variable');
116
- console.log('');
117
- process.exit(1);
118
- }
119
-
120
- console.log(`Using setup: ${setupPath}`);
121
- console.log('');
122
-
123
- // Check if setup path exists
124
- try {
125
- await fs.access(setupPath);
126
- } catch (error) {
127
- console.error(`Error: Setup path does not exist: ${setupPath}`);
128
- process.exit(1);
129
- }
130
-
131
- // Load existing JIRA config if any
132
- const jiraConfigPath = path.join(setupPath, 'jira.json');
133
- let existingConfig = null;
134
- try {
135
- const content = await fs.readFile(jiraConfigPath, 'utf-8');
136
- existingConfig = JSON.parse(content);
137
- console.log('✓ Found existing JIRA configuration');
138
- console.log('');
139
- } catch {
140
- // No existing config
141
- }
142
-
143
- // Prompt for JIRA URL
144
- console.log('Step 1/4: JIRA Base URL');
145
- console.log('────────────────────────────────────────');
146
- const defaultUrl = existingConfig?.baseUrl || 'https://your-company.atlassian.net';
147
- const baseUrl = await prompt(`JIRA Base URL [${defaultUrl}]: `);
148
- const finalBaseUrl = (baseUrl.trim() || defaultUrl).replace(/\/$/, ''); // Remove trailing slash
149
- console.log('');
150
-
151
- // Prompt for email
152
- console.log('Step 2/4: JIRA Email');
153
- console.log('────────────────────────────────────────');
154
- const defaultEmail = existingConfig?.auth?.email || '';
155
- const emailPrompt = defaultEmail ? `Email [${defaultEmail}]: ` : 'Email: ';
156
- const email = await prompt(emailPrompt);
157
- const finalEmail = email.trim() || defaultEmail;
158
-
159
- if (!finalEmail) {
160
- console.error('Error: Email is required');
161
- process.exit(1);
162
- }
163
- console.log('');
164
-
165
- // Prompt for API token
166
- console.log('Step 3/4: JIRA API Token');
167
- console.log('────────────────────────────────────────');
168
- console.log('You need a personal API token from JIRA.');
169
- console.log('');
170
-
171
- const shouldOpen = await prompt('Open browser to get API token? [Y/n]: ');
172
- if (!shouldOpen.trim() || shouldOpen.toLowerCase() === 'y' || shouldOpen.toLowerCase() === 'yes') {
173
- console.log('Opening browser...');
174
- await openTokenPage();
175
- console.log('');
176
- console.log('Instructions:');
177
- console.log(' 1. Click "Create API token"');
178
- console.log(' 2. Give it a name (e.g., "CoderFlow")');
179
- console.log(' 3. Copy the token');
180
- console.log(' 4. Paste it below');
181
- console.log('');
182
- } else {
183
- console.log('');
184
- console.log('Get your API token at:');
185
- console.log('https://id.atlassian.com/manage-profile/security/api-tokens');
186
- console.log('');
187
- }
188
-
189
- const apiToken = await promptSecret('API Token: ');
190
- if (!apiToken.trim()) {
191
- console.error('Error: API Token is required');
192
- process.exit(1);
193
- }
194
- console.log('');
195
-
196
- // Test connection
197
- console.log('Step 4/4: Testing Connection');
198
- console.log('────────────────────────────────────────');
199
- console.log('Connecting to JIRA...');
200
-
201
- const testResult = await testJiraConnection(finalBaseUrl, finalEmail, apiToken.trim());
202
-
203
- if (!testResult.success) {
204
- console.error('✗ Connection failed');
205
- console.error('');
206
- console.error('Error:', testResult.error);
207
- console.error('');
208
- console.error('Please check your credentials and try again.');
209
- process.exit(1);
210
- }
211
-
212
- console.log(`✓ Connected successfully as ${testResult.user.displayName || finalEmail}`);
213
- console.log('');
214
-
215
- // Save configuration
216
- const jiraConfig = {
217
- enabled: true,
218
- baseUrl: finalBaseUrl,
219
- auth: {
220
- email: finalEmail,
221
- apiToken: apiToken.trim()
222
- }
223
- };
224
-
225
- try {
226
- await fs.writeFile(jiraConfigPath, JSON.stringify(jiraConfig, null, 2), 'utf-8');
227
- console.log('✓ Configuration saved');
228
- console.log('');
229
- console.log(`Saved to: ${jiraConfigPath}`);
230
- console.log('');
231
- console.log('═══════════════════════════════════════');
232
- console.log('✓ JIRA integration is now configured!');
233
- console.log('═══════════════════════════════════════');
234
- console.log('');
235
- console.log('The server will automatically use this configuration.');
236
- console.log('No environment variables needed!');
237
- console.log('');
238
- console.log('Next steps:');
239
- console.log(' • Start/restart the server: coder server start');
240
- console.log(' • Open the web UI and look for "Import from JIRA"');
241
- console.log('');
242
- } catch (error) {
243
- console.error('✗ Failed to save configuration');
244
- console.error('');
245
- console.error('Error:', error.message);
246
- process.exit(1);
247
- }
248
- }
249
-
250
- /**
251
- * Show JIRA connection status
252
- */
253
- export async function status(args) {
254
- let setupPath = args.setup || args['setup-path'];
255
- if (!setupPath) {
256
- setupPath = await getCoderSetupPath();
257
- }
258
-
259
- if (!setupPath) {
260
- console.log('JIRA Status: Not configured');
261
- console.log('');
262
- console.log('Run: coder jira connect --setup-path /path/to/setup');
263
- return;
264
- }
265
-
266
- const jiraConfigPath = path.join(setupPath, 'jira.json');
267
-
268
- try {
269
- const content = await fs.readFile(jiraConfigPath, 'utf-8');
270
- const config = JSON.parse(content);
271
-
272
- console.log('JIRA Status: Configured ✓');
273
- console.log('');
274
- console.log(` Base URL: ${config.baseUrl}`);
275
- console.log(` Email: ${config.auth?.email || 'Not set'}`);
276
-
277
- // Show first and last few chars to help verify token is correct
278
- const token = config.auth?.apiToken || '';
279
- const tokenDisplay = token.length > 10
280
- ? `${token.substring(0, 8)}...${token.substring(token.length - 8)}`
281
- : (token ? '*'.repeat(token.length) : 'Not set');
282
- console.log(` Token: ${tokenDisplay}`);
283
-
284
- console.log(` Enabled: ${config.enabled ? 'Yes' : 'No'}`);
285
- console.log('');
286
- console.log(`Config file: ${jiraConfigPath}`);
287
- } catch (error) {
288
- if (error.code === 'ENOENT') {
289
- console.log('JIRA Status: Not configured');
290
- console.log('');
291
- console.log('Run: coder jira connect');
292
- } else {
293
- console.error('Error reading JIRA configuration:', error.message);
294
- }
295
- }
296
- }
297
-
298
- /**
299
- * Disable JIRA integration
300
- */
301
- export async function disable(args) {
302
- let setupPath = args.setup || args['setup-path'];
303
- if (!setupPath) {
304
- setupPath = await getCoderSetupPath();
305
- }
306
-
307
- if (!setupPath) {
308
- console.error('Error: No coder-setup path configured.');
309
- process.exit(1);
310
- }
311
-
312
- const jiraConfigPath = path.join(setupPath, 'jira.json');
313
-
314
- try {
315
- const content = await fs.readFile(jiraConfigPath, 'utf-8');
316
- const config = JSON.parse(content);
317
- config.enabled = false;
318
- await fs.writeFile(jiraConfigPath, JSON.stringify(config, null, 2), 'utf-8');
319
-
320
- console.log('✓ JIRA integration disabled');
321
- console.log('');
322
- console.log('To re-enable: coder jira enable');
323
- } catch (error) {
324
- if (error.code === 'ENOENT') {
325
- console.log('JIRA is not configured yet.');
326
- } else {
327
- console.error('Error:', error.message);
328
- process.exit(1);
329
- }
330
- }
331
- }
332
-
333
- /**
334
- * Enable JIRA integration
335
- */
336
- export async function enable(args) {
337
- let setupPath = args.setup || args['setup-path'];
338
- if (!setupPath) {
339
- setupPath = await getCoderSetupPath();
340
- }
341
-
342
- if (!setupPath) {
343
- console.error('Error: No coder-setup path configured.');
344
- process.exit(1);
345
- }
346
-
347
- const jiraConfigPath = path.join(setupPath, 'jira.json');
348
-
349
- try {
350
- const content = await fs.readFile(jiraConfigPath, 'utf-8');
351
- const config = JSON.parse(content);
352
- config.enabled = true;
353
- await fs.writeFile(jiraConfigPath, JSON.stringify(config, null, 2), 'utf-8');
354
-
355
- console.log('✓ JIRA integration enabled');
356
- } catch (error) {
357
- if (error.code === 'ENOENT') {
358
- console.log('JIRA is not configured yet.');
359
- console.log('');
360
- console.log('Run: coder jira connect');
361
- } else {
362
- console.error('Error:', error.message);
363
- process.exit(1);
364
- }
365
- }
366
- }
367
-
368
- /**
369
- * Test JIRA connection with saved configuration
370
- */
371
- export async function test(args) {
372
- let setupPath = args.setup || args['setup-path'];
373
- if (!setupPath) {
374
- setupPath = await getCoderSetupPath();
375
- }
376
-
377
- if (!setupPath) {
378
- console.error('Error: No coder-setup path configured.');
379
- process.exit(1);
380
- }
381
-
382
- const jiraConfigPath = path.join(setupPath, 'jira.json');
383
-
384
- try {
385
- const content = await fs.readFile(jiraConfigPath, 'utf-8');
386
- const config = JSON.parse(content);
387
-
388
- if (!config.enabled) {
389
- console.log('JIRA integration is disabled.');
390
- console.log('Run: coder jira enable');
391
- return;
392
- }
393
-
394
- console.log('Testing JIRA connection...');
395
- console.log('');
396
-
397
- const testResult = await testJiraConnection(config.baseUrl, config.auth.email, config.auth.apiToken);
398
-
399
- if (testResult.success) {
400
- console.log('✓ Connection successful!');
401
- console.log(` User: ${testResult.user.displayName || config.auth.email}`);
402
- console.log('');
403
- } else {
404
- console.error('✗ Connection failed');
405
- console.error('');
406
- console.error('Error:', testResult.error);
407
- console.error('');
408
- process.exit(1);
409
- }
410
- } catch (error) {
411
- if (error.code === 'ENOENT') {
412
- console.log('JIRA is not configured yet.');
413
- console.log('');
414
- console.log('Run: coder jira connect');
415
- } else {
416
- console.error('Error:', error.message);
417
- process.exit(1);
418
- }
419
- }
420
- }
421
-
422
- /**
423
- * Main jira command handler
424
- */
425
- export async function jira(args) {
426
- const subcommand = args._[0];
427
-
428
- switch (subcommand) {
429
- case 'connect':
430
- await connect(args);
431
- break;
432
- case 'status':
433
- await status(args);
434
- break;
435
- case 'test':
436
- await test(args);
437
- break;
438
- case 'disable':
439
- await disable(args);
440
- break;
441
- case 'enable':
442
- await enable(args);
443
- break;
444
- default:
445
- console.log('Usage: coder jira <command>');
446
- console.log('');
447
- console.log('Commands:');
448
- console.log(' connect Set up JIRA integration (interactive)');
449
- console.log(' status Show JIRA configuration status');
450
- console.log(' test Test JIRA connection');
451
- console.log(' enable Enable JIRA integration');
452
- console.log(' disable Disable JIRA integration');
453
- console.log('');
454
- console.log('Options:');
455
- console.log(' --setup-path <path> Path to coder-setup directory');
456
- console.log('');
457
- console.log('Examples:');
458
- console.log(' coder jira connect');
459
- console.log(' coder jira status');
460
- console.log(' coder jira test');
461
- console.log(' coder jira connect --setup-path /path/to/setup');
462
- break;
463
- }
464
- }