@fractary/core-cli 0.1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -0
  3. package/dist/cli.d.ts +8 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +86 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/docs/index.d.ts +11 -0
  8. package/dist/commands/docs/index.d.ts.map +1 -0
  9. package/dist/commands/docs/index.js +276 -0
  10. package/dist/commands/docs/index.js.map +1 -0
  11. package/dist/commands/file/index.d.ts +11 -0
  12. package/dist/commands/file/index.d.ts.map +1 -0
  13. package/dist/commands/file/index.js +210 -0
  14. package/dist/commands/file/index.js.map +1 -0
  15. package/dist/commands/logs/index.d.ts +11 -0
  16. package/dist/commands/logs/index.d.ts.map +1 -0
  17. package/dist/commands/logs/index.js +277 -0
  18. package/dist/commands/logs/index.js.map +1 -0
  19. package/dist/commands/logs/utils.d.ts +11 -0
  20. package/dist/commands/logs/utils.d.ts.map +1 -0
  21. package/dist/commands/logs/utils.js +39 -0
  22. package/dist/commands/logs/utils.js.map +1 -0
  23. package/dist/commands/repo/branch.d.ts +6 -0
  24. package/dist/commands/repo/branch.d.ts.map +1 -0
  25. package/dist/commands/repo/branch.js +128 -0
  26. package/dist/commands/repo/branch.js.map +1 -0
  27. package/dist/commands/repo/commit.d.ts +6 -0
  28. package/dist/commands/repo/commit.d.ts.map +1 -0
  29. package/dist/commands/repo/commit.js +51 -0
  30. package/dist/commands/repo/commit.js.map +1 -0
  31. package/dist/commands/repo/index.d.ts +11 -0
  32. package/dist/commands/repo/index.d.ts.map +1 -0
  33. package/dist/commands/repo/index.js +33 -0
  34. package/dist/commands/repo/index.js.map +1 -0
  35. package/dist/commands/repo/pr.d.ts +6 -0
  36. package/dist/commands/repo/pr.d.ts.map +1 -0
  37. package/dist/commands/repo/pr.js +165 -0
  38. package/dist/commands/repo/pr.js.map +1 -0
  39. package/dist/commands/repo/status.d.ts +8 -0
  40. package/dist/commands/repo/status.d.ts.map +1 -0
  41. package/dist/commands/repo/status.js +123 -0
  42. package/dist/commands/repo/status.js.map +1 -0
  43. package/dist/commands/repo/tag.d.ts +6 -0
  44. package/dist/commands/repo/tag.d.ts.map +1 -0
  45. package/dist/commands/repo/tag.js +125 -0
  46. package/dist/commands/repo/tag.js.map +1 -0
  47. package/dist/commands/repo/worktree.d.ts +6 -0
  48. package/dist/commands/repo/worktree.d.ts.map +1 -0
  49. package/dist/commands/repo/worktree.js +137 -0
  50. package/dist/commands/repo/worktree.js.map +1 -0
  51. package/dist/commands/spec/index.d.ts +11 -0
  52. package/dist/commands/spec/index.d.ts.map +1 -0
  53. package/dist/commands/spec/index.js +264 -0
  54. package/dist/commands/spec/index.js.map +1 -0
  55. package/dist/commands/work/index.d.ts +11 -0
  56. package/dist/commands/work/index.d.ts.map +1 -0
  57. package/dist/commands/work/index.js +773 -0
  58. package/dist/commands/work/index.js.map +1 -0
  59. package/dist/index.d.ts +16 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +51 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/sdk/factory.d.ts +51 -0
  64. package/dist/sdk/factory.d.ts.map +1 -0
  65. package/dist/sdk/factory.js +188 -0
  66. package/dist/sdk/factory.js.map +1 -0
  67. package/dist/utils/config.d.ts +71 -0
  68. package/dist/utils/config.d.ts.map +1 -0
  69. package/dist/utils/config.js +219 -0
  70. package/dist/utils/config.js.map +1 -0
  71. package/dist/utils/errors.d.ts +42 -0
  72. package/dist/utils/errors.d.ts.map +1 -0
  73. package/dist/utils/errors.js +144 -0
  74. package/dist/utils/errors.js.map +1 -0
  75. package/dist/utils/output.d.ts +115 -0
  76. package/dist/utils/output.d.ts.map +1 -0
  77. package/dist/utils/output.js +201 -0
  78. package/dist/utils/output.js.map +1 -0
  79. package/package.json +78 -0
@@ -0,0 +1,773 @@
1
+ "use strict";
2
+ /**
3
+ * Work subcommand - Work tracking operations
4
+ *
5
+ * Provides issue, comment, label, and milestone operations via @fractary/core WorkManager.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.createWorkCommand = createWorkCommand;
12
+ const commander_1 = require("commander");
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const fs_1 = require("fs");
15
+ const path_1 = __importDefault(require("path"));
16
+ const factory_1 = require("../../sdk/factory");
17
+ const errors_1 = require("../../utils/errors");
18
+ /**
19
+ * Create the work command tree
20
+ */
21
+ function createWorkCommand() {
22
+ const work = new commander_1.Command('work').description('Work item tracking operations');
23
+ // Issue operations
24
+ const issue = new commander_1.Command('issue').description('Issue operations');
25
+ issue.addCommand(createIssueFetchCommand());
26
+ issue.addCommand(createIssueCreateCommand());
27
+ issue.addCommand(createIssueUpdateCommand());
28
+ issue.addCommand(createIssueCloseCommand());
29
+ issue.addCommand(createIssueReopenCommand());
30
+ issue.addCommand(createIssueAssignCommand());
31
+ issue.addCommand(createIssueClassifyCommand());
32
+ issue.addCommand(createIssueSearchCommand());
33
+ // Comment operations
34
+ const comment = new commander_1.Command('comment').description('Comment operations');
35
+ comment.addCommand(createCommentCreateCommand());
36
+ comment.addCommand(createCommentListCommand());
37
+ // Label operations
38
+ const label = new commander_1.Command('label').description('Label operations');
39
+ label.addCommand(createLabelAddCommand());
40
+ label.addCommand(createLabelRemoveCommand());
41
+ label.addCommand(createLabelListCommand());
42
+ // Milestone operations
43
+ const milestone = new commander_1.Command('milestone').description('Milestone operations');
44
+ milestone.addCommand(createMilestoneCreateCommand());
45
+ milestone.addCommand(createMilestoneListCommand());
46
+ milestone.addCommand(createMilestoneSetCommand());
47
+ work.addCommand(issue);
48
+ work.addCommand(comment);
49
+ work.addCommand(label);
50
+ work.addCommand(milestone);
51
+ work.addCommand(createInitCommand());
52
+ return work;
53
+ }
54
+ // Issue Commands
55
+ function createIssueFetchCommand() {
56
+ return new commander_1.Command('fetch')
57
+ .description('Fetch a work item by ID')
58
+ .argument('<number>', 'Issue number')
59
+ .option('--json', 'Output as JSON')
60
+ .option('--verbose', 'Show additional details')
61
+ .action(async (number, options) => {
62
+ try {
63
+ const workManager = await (0, factory_1.getWorkManager)();
64
+ const issue = await workManager.fetchIssue(parseInt(number, 10));
65
+ if (options.json) {
66
+ console.log(JSON.stringify({ status: 'success', data: issue }, null, 2));
67
+ }
68
+ else {
69
+ console.log(chalk_1.default.bold(`#${issue.number}: ${issue.title}`));
70
+ console.log(chalk_1.default.gray(`State: ${issue.state}`));
71
+ if (issue.body) {
72
+ console.log('\n' + issue.body);
73
+ }
74
+ if (options.verbose) {
75
+ if (issue.labels && issue.labels.length > 0) {
76
+ console.log(chalk_1.default.gray('\nLabels:'), issue.labels.map((l) => (typeof l === 'string' ? l : l.name)).join(', '));
77
+ }
78
+ if (issue.assignees && issue.assignees.length > 0) {
79
+ console.log(chalk_1.default.gray('Assignees:'), issue.assignees.join(', '));
80
+ }
81
+ }
82
+ }
83
+ }
84
+ catch (error) {
85
+ (0, errors_1.handleError)(error, options);
86
+ }
87
+ });
88
+ }
89
+ function createIssueCreateCommand() {
90
+ return new commander_1.Command('create')
91
+ .description('Create a new work item')
92
+ .requiredOption('--title <title>', 'Issue title')
93
+ .option('--body <body>', 'Issue body')
94
+ .option('--labels <labels>', 'Comma-separated labels')
95
+ .option('--assignees <assignees>', 'Comma-separated assignees')
96
+ .option('--json', 'Output as JSON')
97
+ .action(async (options) => {
98
+ try {
99
+ const workManager = await (0, factory_1.getWorkManager)();
100
+ const issue = await workManager.createIssue({
101
+ title: options.title,
102
+ body: options.body,
103
+ labels: options.labels?.split(',').map((l) => l.trim()),
104
+ assignees: options.assignees?.split(',').map((a) => a.trim()),
105
+ });
106
+ if (options.json) {
107
+ console.log(JSON.stringify({ status: 'success', data: issue }, null, 2));
108
+ }
109
+ else {
110
+ console.log(chalk_1.default.green(`✓ Created issue #${issue.number}: ${issue.title}`));
111
+ }
112
+ }
113
+ catch (error) {
114
+ (0, errors_1.handleError)(error, options);
115
+ }
116
+ });
117
+ }
118
+ function createIssueUpdateCommand() {
119
+ return new commander_1.Command('update')
120
+ .description('Update a work item')
121
+ .argument('<number>', 'Issue number')
122
+ .option('--title <title>', 'New title')
123
+ .option('--body <body>', 'New body')
124
+ .option('--state <state>', 'New state (open, closed)')
125
+ .option('--json', 'Output as JSON')
126
+ .action(async (number, options) => {
127
+ try {
128
+ const workManager = await (0, factory_1.getWorkManager)();
129
+ const issue = await workManager.updateIssue(parseInt(number, 10), {
130
+ title: options.title,
131
+ body: options.body,
132
+ state: options.state,
133
+ });
134
+ if (options.json) {
135
+ console.log(JSON.stringify({ status: 'success', data: issue }, null, 2));
136
+ }
137
+ else {
138
+ console.log(chalk_1.default.green(`✓ Updated issue #${issue.number}`));
139
+ }
140
+ }
141
+ catch (error) {
142
+ (0, errors_1.handleError)(error, options);
143
+ }
144
+ });
145
+ }
146
+ function createIssueCloseCommand() {
147
+ return new commander_1.Command('close')
148
+ .description('Close a work item')
149
+ .argument('<number>', 'Issue number')
150
+ .option('--comment <text>', 'Add closing comment')
151
+ .option('--json', 'Output as JSON')
152
+ .action(async (number, options) => {
153
+ try {
154
+ const workManager = await (0, factory_1.getWorkManager)();
155
+ // Add comment if provided
156
+ if (options.comment) {
157
+ await workManager.createComment(parseInt(number, 10), options.comment);
158
+ }
159
+ const issue = await workManager.closeIssue(parseInt(number, 10));
160
+ if (options.json) {
161
+ console.log(JSON.stringify({ status: 'success', data: issue }, null, 2));
162
+ }
163
+ else {
164
+ console.log(chalk_1.default.green(`✓ Closed issue #${number}`));
165
+ }
166
+ }
167
+ catch (error) {
168
+ (0, errors_1.handleError)(error, options);
169
+ }
170
+ });
171
+ }
172
+ function createIssueReopenCommand() {
173
+ return new commander_1.Command('reopen')
174
+ .description('Reopen a closed work item')
175
+ .argument('<number>', 'Issue number')
176
+ .option('--comment <text>', 'Add comment when reopening')
177
+ .option('--json', 'Output as JSON')
178
+ .action(async (number, options) => {
179
+ try {
180
+ const workManager = await (0, factory_1.getWorkManager)();
181
+ // Add comment if provided
182
+ if (options.comment) {
183
+ await workManager.createComment(parseInt(number, 10), options.comment);
184
+ }
185
+ const issue = await workManager.reopenIssue(parseInt(number, 10));
186
+ if (options.json) {
187
+ console.log(JSON.stringify({
188
+ status: 'success',
189
+ data: {
190
+ number: issue.number,
191
+ state: issue.state,
192
+ url: issue.url,
193
+ },
194
+ }, null, 2));
195
+ }
196
+ else {
197
+ console.log(chalk_1.default.green(`✓ Reopened issue #${number}`));
198
+ }
199
+ }
200
+ catch (error) {
201
+ (0, errors_1.handleError)(error, options);
202
+ }
203
+ });
204
+ }
205
+ function createIssueAssignCommand() {
206
+ return new commander_1.Command('assign')
207
+ .description('Assign or unassign a work item')
208
+ .argument('<number>', 'Issue number')
209
+ .option('--user <username>', 'User to assign (use @me for self, omit to unassign)')
210
+ .option('--json', 'Output as JSON')
211
+ .action(async (number, options) => {
212
+ try {
213
+ const workManager = await (0, factory_1.getWorkManager)();
214
+ let issue;
215
+ if (options.user) {
216
+ issue = await workManager.assignIssue(parseInt(number, 10), options.user);
217
+ }
218
+ else {
219
+ issue = await workManager.unassignIssue(parseInt(number, 10));
220
+ }
221
+ if (options.json) {
222
+ console.log(JSON.stringify({
223
+ status: 'success',
224
+ data: {
225
+ number: issue.number,
226
+ assignees: issue.assignees || [],
227
+ url: issue.url,
228
+ },
229
+ }, null, 2));
230
+ }
231
+ else {
232
+ if (options.user) {
233
+ console.log(chalk_1.default.green(`✓ Assigned issue #${number} to ${options.user}`));
234
+ }
235
+ else {
236
+ console.log(chalk_1.default.green(`✓ Unassigned issue #${number}`));
237
+ }
238
+ }
239
+ }
240
+ catch (error) {
241
+ (0, errors_1.handleError)(error, options);
242
+ }
243
+ });
244
+ }
245
+ function createIssueSearchCommand() {
246
+ return new commander_1.Command('search')
247
+ .description('Search work items')
248
+ .requiredOption('--query <query>', 'Search query')
249
+ .option('--state <state>', 'Filter by state (open, closed, all)', 'open')
250
+ .option('--labels <labels>', 'Filter by labels (comma-separated)')
251
+ .option('--limit <n>', 'Max results', '10')
252
+ .option('--json', 'Output as JSON')
253
+ .action(async (options) => {
254
+ try {
255
+ const workManager = await (0, factory_1.getWorkManager)();
256
+ const issues = await workManager.searchIssues(options.query, {
257
+ state: options.state,
258
+ labels: options.labels?.split(',').map((l) => l.trim()),
259
+ });
260
+ const limitedIssues = options.limit ? issues.slice(0, parseInt(options.limit, 10)) : issues;
261
+ if (options.json) {
262
+ console.log(JSON.stringify({ status: 'success', data: limitedIssues }, null, 2));
263
+ }
264
+ else {
265
+ if (limitedIssues.length === 0) {
266
+ console.log(chalk_1.default.yellow('No issues found'));
267
+ }
268
+ else {
269
+ limitedIssues.forEach((issue) => {
270
+ console.log(`#${issue.number} ${issue.title} [${issue.state}]`);
271
+ });
272
+ }
273
+ }
274
+ }
275
+ catch (error) {
276
+ (0, errors_1.handleError)(error, options);
277
+ }
278
+ });
279
+ }
280
+ function createIssueClassifyCommand() {
281
+ return new commander_1.Command('classify')
282
+ .description('Classify work item type (feature, bug, chore, patch)')
283
+ .argument('<number>', 'Issue number')
284
+ .option('--json', 'Output as JSON')
285
+ .action(async (number, options) => {
286
+ try {
287
+ const workManager = await (0, factory_1.getWorkManager)();
288
+ const issue = await workManager.fetchIssue(parseInt(number, 10));
289
+ const result = classifyWorkType(issue);
290
+ if (options.json) {
291
+ console.log(JSON.stringify({
292
+ status: 'success',
293
+ data: {
294
+ number: parseInt(number, 10),
295
+ work_type: result.work_type,
296
+ confidence: result.confidence,
297
+ signals: result.signals,
298
+ },
299
+ }, null, 2));
300
+ }
301
+ else {
302
+ console.log(result.work_type);
303
+ if (result.confidence < 0.5) {
304
+ console.log(chalk_1.default.red(`⚠ LOW CONFIDENCE: ${Math.round(result.confidence * 100)}% - review manually`));
305
+ }
306
+ else if (result.confidence < 0.8) {
307
+ console.log(chalk_1.default.yellow(`(confidence: ${Math.round(result.confidence * 100)}%)`));
308
+ }
309
+ }
310
+ }
311
+ catch (error) {
312
+ (0, errors_1.handleError)(error, options);
313
+ }
314
+ });
315
+ }
316
+ // Comment Commands
317
+ function createCommentCreateCommand() {
318
+ return new commander_1.Command('create')
319
+ .description('Add a comment to a work item')
320
+ .argument('<number>', 'Issue number')
321
+ .requiredOption('--body <text>', 'Comment body')
322
+ .option('--json', 'Output as JSON')
323
+ .action(async (number, options) => {
324
+ try {
325
+ const workManager = await (0, factory_1.getWorkManager)();
326
+ const comment = await workManager.createComment(parseInt(number, 10), options.body);
327
+ if (options.json) {
328
+ console.log(JSON.stringify({ status: 'success', data: comment }, null, 2));
329
+ }
330
+ else {
331
+ console.log(chalk_1.default.green(`✓ Added comment to issue #${number}`));
332
+ }
333
+ }
334
+ catch (error) {
335
+ (0, errors_1.handleError)(error, options);
336
+ }
337
+ });
338
+ }
339
+ function createCommentListCommand() {
340
+ return new commander_1.Command('list')
341
+ .description('List comments on a work item')
342
+ .argument('<number>', 'Issue number')
343
+ .option('--limit <n>', 'Max comments to show')
344
+ .option('--json', 'Output as JSON')
345
+ .action(async (number, options) => {
346
+ try {
347
+ const workManager = await (0, factory_1.getWorkManager)();
348
+ const comments = await workManager.listComments(parseInt(number, 10));
349
+ const limitedComments = options.limit
350
+ ? comments.slice(0, parseInt(options.limit, 10))
351
+ : comments;
352
+ if (options.json) {
353
+ console.log(JSON.stringify({ status: 'success', data: limitedComments }, null, 2));
354
+ }
355
+ else {
356
+ if (limitedComments.length === 0) {
357
+ console.log(chalk_1.default.yellow('No comments'));
358
+ }
359
+ else {
360
+ limitedComments.forEach((comment, idx) => {
361
+ console.log(chalk_1.default.bold(`\nComment #${idx + 1} by ${comment.author}:`));
362
+ console.log(comment.body);
363
+ });
364
+ }
365
+ }
366
+ }
367
+ catch (error) {
368
+ (0, errors_1.handleError)(error, options);
369
+ }
370
+ });
371
+ }
372
+ // Label Commands
373
+ function createLabelAddCommand() {
374
+ return new commander_1.Command('add')
375
+ .description('Add labels to a work item')
376
+ .argument('<number>', 'Issue number')
377
+ .requiredOption('--labels <labels>', 'Comma-separated labels to add')
378
+ .option('--json', 'Output as JSON')
379
+ .action(async (number, options) => {
380
+ try {
381
+ const workManager = await (0, factory_1.getWorkManager)();
382
+ const labels = options.labels.split(',').map((l) => l.trim());
383
+ const result = await workManager.addLabels(parseInt(number, 10), labels);
384
+ if (options.json) {
385
+ console.log(JSON.stringify({ status: 'success', data: result }, null, 2));
386
+ }
387
+ else {
388
+ console.log(chalk_1.default.green(`✓ Added labels to issue #${number}`));
389
+ }
390
+ }
391
+ catch (error) {
392
+ (0, errors_1.handleError)(error, options);
393
+ }
394
+ });
395
+ }
396
+ function createLabelRemoveCommand() {
397
+ return new commander_1.Command('remove')
398
+ .description('Remove labels from a work item')
399
+ .argument('<number>', 'Issue number')
400
+ .requiredOption('--labels <labels>', 'Comma-separated labels to remove')
401
+ .option('--json', 'Output as JSON')
402
+ .action(async (number, options) => {
403
+ try {
404
+ const workManager = await (0, factory_1.getWorkManager)();
405
+ const labels = options.labels.split(',').map((l) => l.trim());
406
+ const result = await workManager.removeLabels(parseInt(number, 10), labels);
407
+ if (options.json) {
408
+ console.log(JSON.stringify({ status: 'success', data: result }, null, 2));
409
+ }
410
+ else {
411
+ console.log(chalk_1.default.green(`✓ Removed labels from issue #${number}`));
412
+ }
413
+ }
414
+ catch (error) {
415
+ (0, errors_1.handleError)(error, options);
416
+ }
417
+ });
418
+ }
419
+ function createLabelListCommand() {
420
+ return new commander_1.Command('list')
421
+ .description('List all available labels or labels on an issue')
422
+ .option('--issue <number>', 'Show labels for specific issue')
423
+ .option('--json', 'Output as JSON')
424
+ .action(async (options) => {
425
+ try {
426
+ const workManager = await (0, factory_1.getWorkManager)();
427
+ if (options.issue) {
428
+ const issue = await workManager.fetchIssue(parseInt(options.issue, 10));
429
+ const labels = issue.labels || [];
430
+ if (options.json) {
431
+ console.log(JSON.stringify({ status: 'success', data: labels }, null, 2));
432
+ }
433
+ else {
434
+ if (labels.length === 0) {
435
+ console.log(chalk_1.default.yellow('No labels'));
436
+ }
437
+ else {
438
+ labels.forEach((label) => {
439
+ const name = typeof label === 'string' ? label : label.name;
440
+ console.log(` • ${name}`);
441
+ });
442
+ }
443
+ }
444
+ }
445
+ else {
446
+ const labels = await workManager.listLabels();
447
+ if (options.json) {
448
+ console.log(JSON.stringify({ status: 'success', data: labels }, null, 2));
449
+ }
450
+ else {
451
+ if (labels.length === 0) {
452
+ console.log(chalk_1.default.yellow('No labels available'));
453
+ }
454
+ else {
455
+ labels.forEach((label) => {
456
+ const name = typeof label === 'string' ? label : label.name;
457
+ console.log(` • ${name}`);
458
+ });
459
+ }
460
+ }
461
+ }
462
+ }
463
+ catch (error) {
464
+ (0, errors_1.handleError)(error, options);
465
+ }
466
+ });
467
+ }
468
+ // Milestone Commands
469
+ function createMilestoneCreateCommand() {
470
+ return new commander_1.Command('create')
471
+ .description('Create a new milestone')
472
+ .requiredOption('--title <title>', 'Milestone title')
473
+ .option('--description <desc>', 'Milestone description')
474
+ .option('--due-on <date>', 'Due date (YYYY-MM-DD)')
475
+ .option('--json', 'Output as JSON')
476
+ .action(async (options) => {
477
+ try {
478
+ const workManager = await (0, factory_1.getWorkManager)();
479
+ const milestone = await workManager.createMilestone({
480
+ title: options.title,
481
+ description: options.description,
482
+ due_on: options.dueOn,
483
+ });
484
+ if (options.json) {
485
+ console.log(JSON.stringify({ status: 'success', data: milestone }, null, 2));
486
+ }
487
+ else {
488
+ console.log(chalk_1.default.green(`✓ Created milestone: ${milestone.title}`));
489
+ }
490
+ }
491
+ catch (error) {
492
+ (0, errors_1.handleError)(error, options);
493
+ }
494
+ });
495
+ }
496
+ function createMilestoneListCommand() {
497
+ return new commander_1.Command('list')
498
+ .description('List milestones')
499
+ .option('--state <state>', 'Filter by state (open, closed, all)', 'open')
500
+ .option('--json', 'Output as JSON')
501
+ .action(async (options) => {
502
+ try {
503
+ const workManager = await (0, factory_1.getWorkManager)();
504
+ const milestones = await workManager.listMilestones(options.state || 'all');
505
+ if (options.json) {
506
+ console.log(JSON.stringify({ status: 'success', data: milestones }, null, 2));
507
+ }
508
+ else {
509
+ if (milestones.length === 0) {
510
+ console.log(chalk_1.default.yellow('No milestones'));
511
+ }
512
+ else {
513
+ milestones.forEach((milestone) => {
514
+ console.log(` • ${milestone.title} [${milestone.state}]`);
515
+ });
516
+ }
517
+ }
518
+ }
519
+ catch (error) {
520
+ (0, errors_1.handleError)(error, options);
521
+ }
522
+ });
523
+ }
524
+ function createMilestoneSetCommand() {
525
+ return new commander_1.Command('set')
526
+ .description('Set milestone for a work item')
527
+ .argument('<number>', 'Issue number')
528
+ .requiredOption('--milestone <title>', 'Milestone title')
529
+ .option('--json', 'Output as JSON')
530
+ .action(async (number, options) => {
531
+ try {
532
+ const workManager = await (0, factory_1.getWorkManager)();
533
+ const issue = await workManager.setMilestone(parseInt(number, 10), options.milestone);
534
+ if (options.json) {
535
+ console.log(JSON.stringify({ status: 'success', data: issue }, null, 2));
536
+ }
537
+ else {
538
+ console.log(chalk_1.default.green(`✓ Set milestone for issue #${number}`));
539
+ }
540
+ }
541
+ catch (error) {
542
+ (0, errors_1.handleError)(error, options);
543
+ }
544
+ });
545
+ }
546
+ function createInitCommand() {
547
+ return new commander_1.Command('init')
548
+ .description('Initialize work tracking configuration')
549
+ .option('--platform <name>', 'Platform (github, gitlab, bitbucket, jira, linear)')
550
+ .option('--project <name>', 'Project name (for Jira/Linear)')
551
+ .option('--yes', 'Skip confirmation prompts')
552
+ .option('--json', 'Output as JSON')
553
+ .action(async (options) => {
554
+ try {
555
+ let platform = options.platform;
556
+ if (!platform) {
557
+ platform = await detectPlatformFromGit();
558
+ if (!options.json) {
559
+ console.log(chalk_1.default.gray(`Detected platform: ${platform}`));
560
+ }
561
+ }
562
+ const config = await buildWorkConfig(platform, {
563
+ project: options.project,
564
+ });
565
+ const configPath = await writeWorkConfig(config);
566
+ if (options.json) {
567
+ console.log(JSON.stringify({
568
+ status: 'success',
569
+ data: {
570
+ platform: config.work.platform,
571
+ configPath,
572
+ repository: config.work.repository,
573
+ project: config.work.project,
574
+ },
575
+ }, null, 2));
576
+ }
577
+ else {
578
+ console.log(chalk_1.default.green(`✓ Work tracking initialized`));
579
+ console.log(chalk_1.default.gray(`Platform: ${config.work.platform}`));
580
+ console.log(chalk_1.default.gray(`Config: ${configPath}`));
581
+ }
582
+ }
583
+ catch (error) {
584
+ (0, errors_1.handleError)(error, options);
585
+ }
586
+ });
587
+ }
588
+ function classifyWorkType(issue) {
589
+ const title = (issue.title || '').toLowerCase();
590
+ const labels = (issue.labels || []).map((l) => typeof l === 'string' ? l.toLowerCase() : l.name.toLowerCase());
591
+ const signals = {
592
+ labels: labels,
593
+ title_keywords: [],
594
+ has_bug_markers: false,
595
+ };
596
+ // Label-based classification (highest priority)
597
+ const labelScores = {
598
+ bug: { type: 'bug', score: 0.95 },
599
+ defect: { type: 'bug', score: 0.95 },
600
+ regression: { type: 'bug', score: 0.9 },
601
+ enhancement: { type: 'feature', score: 0.9 },
602
+ feature: { type: 'feature', score: 0.95 },
603
+ 'new feature': { type: 'feature', score: 0.95 },
604
+ chore: { type: 'chore', score: 0.9 },
605
+ maintenance: { type: 'chore', score: 0.85 },
606
+ dependencies: { type: 'chore', score: 0.8 },
607
+ hotfix: { type: 'patch', score: 0.95 },
608
+ urgent: { type: 'patch', score: 0.7 },
609
+ security: { type: 'patch', score: 0.85 },
610
+ critical: { type: 'patch', score: 0.8 },
611
+ };
612
+ // Check labels first
613
+ for (const label of labels) {
614
+ if (labelScores[label]) {
615
+ return {
616
+ work_type: labelScores[label].type,
617
+ confidence: labelScores[label].score,
618
+ signals,
619
+ };
620
+ }
621
+ }
622
+ // Title keyword analysis
623
+ const bugKeywords = ['fix', 'bug', 'error', 'crash', 'broken', 'issue', 'problem'];
624
+ const featureKeywords = ['add', 'implement', 'new', 'create', 'feature', 'support'];
625
+ const choreKeywords = ['update', 'upgrade', 'refactor', 'clean', 'remove', 'deprecate', 'migrate'];
626
+ const patchKeywords = ['hotfix', 'urgent', 'critical', 'security'];
627
+ let workType = 'feature';
628
+ let confidence = 0.5;
629
+ // Check for patch markers (highest urgency)
630
+ for (const keyword of patchKeywords) {
631
+ if (title.includes(keyword)) {
632
+ signals.title_keywords.push(keyword);
633
+ workType = 'patch';
634
+ confidence = 0.85;
635
+ return { work_type: workType, confidence, signals };
636
+ }
637
+ }
638
+ // Check for bug markers
639
+ for (const keyword of bugKeywords) {
640
+ if (title.includes(keyword)) {
641
+ signals.title_keywords.push(keyword);
642
+ signals.has_bug_markers = true;
643
+ workType = 'bug';
644
+ confidence = 0.75;
645
+ }
646
+ }
647
+ // Check for feature markers
648
+ if (workType !== 'bug') {
649
+ for (const keyword of featureKeywords) {
650
+ if (title.includes(keyword)) {
651
+ signals.title_keywords.push(keyword);
652
+ workType = 'feature';
653
+ confidence = 0.7;
654
+ }
655
+ }
656
+ }
657
+ // Check for chore markers
658
+ if (workType === 'feature') {
659
+ for (const keyword of choreKeywords) {
660
+ if (title.includes(keyword)) {
661
+ signals.title_keywords.push(keyword);
662
+ workType = 'chore';
663
+ confidence = 0.65;
664
+ }
665
+ }
666
+ }
667
+ return { work_type: workType, confidence, signals };
668
+ }
669
+ /**
670
+ * Detect platform from git remote URL
671
+ */
672
+ async function detectPlatformFromGit() {
673
+ try {
674
+ const gitConfigPath = path_1.default.join(process.cwd(), '.git', 'config');
675
+ const gitConfig = await fs_1.promises.readFile(gitConfigPath, 'utf-8');
676
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(.+)/);
677
+ if (!remoteMatch) {
678
+ throw new Error('No origin remote found');
679
+ }
680
+ const remoteUrl = remoteMatch[1].trim();
681
+ if (remoteUrl.includes('github.com')) {
682
+ return 'github';
683
+ }
684
+ else if (remoteUrl.includes('gitlab.com')) {
685
+ return 'gitlab';
686
+ }
687
+ else if (remoteUrl.includes('bitbucket.org')) {
688
+ return 'bitbucket';
689
+ }
690
+ else if (remoteUrl.includes('atlassian.net')) {
691
+ return 'jira';
692
+ }
693
+ return 'github';
694
+ }
695
+ catch {
696
+ return 'github';
697
+ }
698
+ }
699
+ /**
700
+ * Parse git remote URL to extract owner and repo
701
+ */
702
+ function parseGitRemote(url) {
703
+ // SSH format: git@github.com:owner/repo.git
704
+ const sshMatch = url.match(/@[^:]+:([^/]+)\/([^.]+)/);
705
+ if (sshMatch) {
706
+ return { owner: sshMatch[1], name: sshMatch[2] };
707
+ }
708
+ // HTTPS format: https://github.com/owner/repo.git
709
+ const httpsMatch = url.match(/https?:\/\/[^/]+\/([^/]+)\/([^/.]+)/);
710
+ if (httpsMatch) {
711
+ return { owner: httpsMatch[1], name: httpsMatch[2] };
712
+ }
713
+ return null;
714
+ }
715
+ /**
716
+ * Build work configuration
717
+ */
718
+ async function buildWorkConfig(platform, options) {
719
+ const config = {
720
+ work: {
721
+ platform,
722
+ },
723
+ };
724
+ if (platform === 'github' || platform === 'gitlab' || platform === 'bitbucket') {
725
+ try {
726
+ const gitConfigPath = path_1.default.join(process.cwd(), '.git', 'config');
727
+ const gitConfig = await fs_1.promises.readFile(gitConfigPath, 'utf-8');
728
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(.+)/);
729
+ if (remoteMatch) {
730
+ const repoInfo = parseGitRemote(remoteMatch[1].trim());
731
+ if (repoInfo) {
732
+ config.work.repository = repoInfo;
733
+ }
734
+ }
735
+ }
736
+ catch {
737
+ // Ignore errors, repository info is optional
738
+ }
739
+ }
740
+ else if (platform === 'jira' || platform === 'linear') {
741
+ if (options.project) {
742
+ config.work.project = options.project;
743
+ }
744
+ }
745
+ return config;
746
+ }
747
+ /**
748
+ * Write work configuration to file
749
+ */
750
+ async function writeWorkConfig(config) {
751
+ const configDir = path_1.default.join(process.cwd(), '.fractary', 'core');
752
+ const configPath = path_1.default.join(configDir, 'config.json');
753
+ // Ensure directory exists
754
+ await fs_1.promises.mkdir(configDir, { recursive: true });
755
+ // Read existing config if present
756
+ let existingConfig = {};
757
+ try {
758
+ const existing = await fs_1.promises.readFile(configPath, 'utf-8');
759
+ existingConfig = JSON.parse(existing);
760
+ }
761
+ catch {
762
+ // No existing config, that's fine
763
+ }
764
+ // Merge with existing config
765
+ const mergedConfig = {
766
+ ...existingConfig,
767
+ ...config,
768
+ };
769
+ // Write config
770
+ await fs_1.promises.writeFile(configPath, JSON.stringify(mergedConfig, null, 2) + '\n');
771
+ return configPath;
772
+ }
773
+ //# sourceMappingURL=index.js.map