wogiflow 1.0.11 → 1.0.13

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 (46) hide show
  1. package/.workflow/specs/architecture.md.template +24 -0
  2. package/.workflow/specs/stack.md.template +33 -0
  3. package/.workflow/specs/testing.md.template +36 -0
  4. package/README.md +90 -1
  5. package/lib/unified-wizard.js +569 -30
  6. package/package.json +1 -1
  7. package/scripts/MEMORY-ARCHITECTURE.md +150 -0
  8. package/scripts/flow +20 -19
  9. package/scripts/flow-auto-context.js +97 -3
  10. package/scripts/flow-conflict-resolver.js +735 -0
  11. package/scripts/flow-context-gatherer.js +520 -0
  12. package/scripts/flow-context-monitor.js +148 -19
  13. package/scripts/flow-damage-control.js +5 -1
  14. package/scripts/flow-export-profile +168 -1
  15. package/scripts/flow-import-profile +257 -6
  16. package/scripts/flow-instruction-richness.js +182 -18
  17. package/scripts/flow-knowledge-router.js +2 -0
  18. package/scripts/flow-knowledge-sync.js +2 -0
  19. package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
  20. package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
  21. package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
  22. package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
  23. package/scripts/flow-memory-db.js +386 -1
  24. package/scripts/flow-memory-sync.js +2 -0
  25. package/scripts/flow-model-adapter.js +53 -29
  26. package/scripts/flow-model-router.js +246 -1
  27. package/scripts/flow-morning.js +94 -0
  28. package/scripts/flow-onboard +223 -10
  29. package/scripts/flow-orchestrate-validation.js +539 -0
  30. package/scripts/flow-orchestrate.js +16 -507
  31. package/scripts/flow-pattern-extractor.js +1265 -0
  32. package/scripts/flow-prompt-composer.js +222 -2
  33. package/scripts/flow-quality-guard.js +594 -0
  34. package/scripts/flow-section-index.js +713 -0
  35. package/scripts/flow-section-resolver.js +484 -0
  36. package/scripts/flow-session-end.js +188 -2
  37. package/scripts/flow-skill-create.js +19 -3
  38. package/scripts/flow-skill-matcher.js +122 -7
  39. package/scripts/flow-statusline-setup.js +218 -0
  40. package/scripts/flow-step-review.js +19 -0
  41. package/scripts/flow-tech-debt.js +734 -0
  42. package/scripts/flow-utils.js +2 -0
  43. package/scripts/hooks/core/long-input-gate.js +293 -0
  44. package/scripts/flow-parallel-detector.js +0 -399
  45. package/scripts/flow-parallel-dispatch.js +0 -987
  46. /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
@@ -0,0 +1,734 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Technical Debt Manager
5
+ *
6
+ * Tracks, manages, and helps resolve technical debt accumulated across sessions.
7
+ * Issues are captured from session reviews and persisted for tracking over time.
8
+ *
9
+ * Usage:
10
+ * CLI:
11
+ * flow tech-debt # Show summary
12
+ * flow tech-debt list # List all open items
13
+ * flow tech-debt list --aging # Show only aging items
14
+ * flow tech-debt list --fixable # Show only auto-fixable items
15
+ * flow tech-debt fix # Run auto-fixes (batch)
16
+ * flow tech-debt dismiss <id> # Mark as won't-fix
17
+ * flow tech-debt promote <id> # Create task from debt item
18
+ *
19
+ * Programmatic:
20
+ * const { TechDebtManager } = require('./flow-tech-debt');
21
+ * const manager = new TechDebtManager();
22
+ * manager.addIssues(issues);
23
+ * manager.getStats();
24
+ */
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+ const crypto = require('crypto');
29
+
30
+ // Import shared utilities from flow-utils
31
+ const {
32
+ colors: c,
33
+ safeJsonParse,
34
+ writeJson,
35
+ fileExists,
36
+ getProjectRoot,
37
+ ensureDir
38
+ } = require('./flow-utils');
39
+
40
+ // ============================================================================
41
+ // Constants
42
+ // ============================================================================
43
+
44
+ const DEFAULT_AGING_THRESHOLD = 3; // Sessions before item is "aging"
45
+ const AUTO_FIXABLE_TYPES = [
46
+ 'console.log',
47
+ 'unused-import',
48
+ 'debugger',
49
+ 'trailing-whitespace',
50
+ 'empty-catch'
51
+ ];
52
+
53
+ // ============================================================================
54
+ // Utility Functions
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Generate unique debt item ID (6 bytes for better collision resistance)
59
+ */
60
+ function generateDebtId() {
61
+ return 'td-' + crypto.randomBytes(6).toString('hex');
62
+ }
63
+
64
+ /**
65
+ * Get current date in YYYY-MM-DD format
66
+ */
67
+ function getCurrentDate() {
68
+ return new Date().toISOString().split('T')[0];
69
+ }
70
+
71
+ /**
72
+ * Safe JSON write with atomic temp file pattern
73
+ */
74
+ function safeWriteJson(filePath, data) {
75
+ const dir = path.dirname(filePath);
76
+ ensureDir(dir);
77
+ writeJson(filePath, data);
78
+ }
79
+
80
+ // ============================================================================
81
+ // Tech Debt Manager Class
82
+ // ============================================================================
83
+
84
+ class TechDebtManager {
85
+ constructor(projectRoot = null) {
86
+ this.projectRoot = projectRoot || getProjectRoot();
87
+ this.debtFilePath = path.join(this.projectRoot, '.workflow', 'state', 'tech-debt.json');
88
+ this.configPath = path.join(this.projectRoot, '.workflow', 'config.json');
89
+ this.readyPath = path.join(this.projectRoot, '.workflow', 'state', 'ready.json');
90
+ this.data = this.load();
91
+ this.config = this.loadConfig();
92
+ }
93
+
94
+ /**
95
+ * Load tech debt data from file
96
+ */
97
+ load() {
98
+ const defaultData = {
99
+ version: '1.0.0',
100
+ lastUpdated: new Date().toISOString(),
101
+ issues: [],
102
+ stats: {
103
+ totalOpen: 0,
104
+ bySeverity: { critical: 0, high: 0, medium: 0, low: 0 },
105
+ autoFixable: 0
106
+ }
107
+ };
108
+
109
+ const data = safeJsonParse(this.debtFilePath, defaultData);
110
+ return data;
111
+ }
112
+
113
+ /**
114
+ * Load config for tech debt settings
115
+ */
116
+ loadConfig() {
117
+ const config = safeJsonParse(this.configPath, {});
118
+ return config.techDebt || {
119
+ enabled: true,
120
+ promptOnSessionEnd: true,
121
+ showInMorningBriefing: true,
122
+ agingThreshold: DEFAULT_AGING_THRESHOLD,
123
+ autoFix: {
124
+ enabled: true,
125
+ types: AUTO_FIXABLE_TYPES
126
+ },
127
+ debtBudget: {
128
+ enabled: false,
129
+ maxOpenItems: 20
130
+ }
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Save tech debt data to file
136
+ */
137
+ save() {
138
+ this.data.lastUpdated = new Date().toISOString();
139
+ this.updateStats();
140
+ safeWriteJson(this.debtFilePath, this.data);
141
+ }
142
+
143
+ /**
144
+ * Update statistics
145
+ */
146
+ updateStats() {
147
+ const openIssues = this.data.issues.filter(i => i.status === 'open');
148
+
149
+ this.data.stats = {
150
+ totalOpen: openIssues.length,
151
+ bySeverity: {
152
+ critical: openIssues.filter(i => i.severity === 'critical').length,
153
+ high: openIssues.filter(i => i.severity === 'high').length,
154
+ medium: openIssues.filter(i => i.severity === 'medium').length,
155
+ low: openIssues.filter(i => i.severity === 'low').length
156
+ },
157
+ autoFixable: openIssues.filter(i => i.autoFixable).length
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Create unique key for deduplication
163
+ */
164
+ createIssueKey(issue) {
165
+ return `${issue.file}:${issue.line}:${issue.description}`.toLowerCase();
166
+ }
167
+
168
+ /**
169
+ * Determine if an issue is auto-fixable
170
+ */
171
+ isAutoFixable(issue) {
172
+ const fixablePatterns = [
173
+ /console\.(log|debug|info|warn)/i,
174
+ /unused\s+(import|variable|parameter)/i,
175
+ /debugger\s+statement/i,
176
+ /trailing\s+whitespace/i,
177
+ /empty\s+catch/i
178
+ ];
179
+
180
+ const desc = issue.description.toLowerCase();
181
+ return fixablePatterns.some(pattern => pattern.test(desc));
182
+ }
183
+
184
+ /**
185
+ * Add issues from session review
186
+ * Deduplicates and updates session count for existing issues
187
+ */
188
+ addIssues(issues) {
189
+ const today = getCurrentDate();
190
+ const existingKeys = new Map(
191
+ this.data.issues.map(i => [this.createIssueKey(i), i])
192
+ );
193
+
194
+ let added = 0;
195
+ let updated = 0;
196
+
197
+ for (const issue of issues) {
198
+ const key = this.createIssueKey(issue);
199
+ const existing = existingKeys.get(key);
200
+
201
+ if (existing) {
202
+ // Update existing issue
203
+ existing.sessionsSeen = (existing.sessionsSeen || 1) + 1;
204
+ existing.lastSeen = today;
205
+ updated++;
206
+ } else {
207
+ // Add new issue
208
+ const newIssue = {
209
+ id: generateDebtId(),
210
+ file: issue.file,
211
+ line: issue.line || 0,
212
+ category: issue.category || 'code',
213
+ severity: issue.severity || 'low',
214
+ description: issue.description,
215
+ fix: issue.fix || '',
216
+ firstSeen: today,
217
+ lastSeen: today,
218
+ sessionsSeen: 1,
219
+ status: 'open',
220
+ autoFixable: this.isAutoFixable(issue)
221
+ };
222
+
223
+ this.data.issues.push(newIssue);
224
+ existingKeys.set(key, newIssue);
225
+ added++;
226
+ }
227
+ }
228
+
229
+ this.save();
230
+ return { added, updated };
231
+ }
232
+
233
+ /**
234
+ * Get all open issues
235
+ */
236
+ getOpenIssues() {
237
+ return this.data.issues.filter(i => i.status === 'open');
238
+ }
239
+
240
+ /**
241
+ * Get aging issues (seen >= threshold sessions)
242
+ */
243
+ getAgingIssues() {
244
+ const threshold = this.config.agingThreshold || DEFAULT_AGING_THRESHOLD;
245
+ return this.getOpenIssues().filter(i => i.sessionsSeen >= threshold);
246
+ }
247
+
248
+ /**
249
+ * Get auto-fixable issues
250
+ */
251
+ getAutoFixable() {
252
+ return this.getOpenIssues().filter(i => i.autoFixable);
253
+ }
254
+
255
+ /**
256
+ * Get issues by severity
257
+ */
258
+ getBySeverity(severity) {
259
+ return this.getOpenIssues().filter(i => i.severity === severity);
260
+ }
261
+
262
+ /**
263
+ * Mark issue as fixed
264
+ */
265
+ markFixed(id) {
266
+ const issue = this.data.issues.find(i => i.id === id);
267
+ if (issue) {
268
+ issue.status = 'fixed';
269
+ issue.fixedAt = new Date().toISOString();
270
+ this.save();
271
+ return true;
272
+ }
273
+ return false;
274
+ }
275
+
276
+ /**
277
+ * Mark issue as dismissed (won't fix)
278
+ */
279
+ dismiss(id, reason = '') {
280
+ const issue = this.data.issues.find(i => i.id === id);
281
+ if (issue) {
282
+ issue.status = 'dismissed';
283
+ issue.dismissedAt = new Date().toISOString();
284
+ issue.dismissReason = reason;
285
+ this.save();
286
+ return true;
287
+ }
288
+ return false;
289
+ }
290
+
291
+ /**
292
+ * Promote debt item to task in ready.json
293
+ */
294
+ promoteToTask(id) {
295
+ const issue = this.data.issues.find(i => i.id === id && i.status === 'open');
296
+ if (!issue) {
297
+ return { success: false, error: 'Issue not found or not open' };
298
+ }
299
+
300
+ const ready = safeJsonParse(this.readyPath, { ready: [], inProgress: [] });
301
+
302
+ // Check if already promoted
303
+ const existingTask = ready.ready.find(t => t.debtItemId === id);
304
+ if (existingTask) {
305
+ return { success: false, error: 'Already promoted to task' };
306
+ }
307
+
308
+ // Create task
309
+ const task = {
310
+ id: `wf-debt-${crypto.randomBytes(4).toString('hex')}`,
311
+ title: `Fix tech debt: ${issue.description.slice(0, 50)}`,
312
+ type: 'refactor',
313
+ priority: issue.severity === 'critical' ? 'high' : (issue.severity === 'high' ? 'medium' : 'low'),
314
+ source: 'tech-debt',
315
+ debtItemId: issue.id,
316
+ file: issue.file,
317
+ line: issue.line,
318
+ created: new Date().toISOString()
319
+ };
320
+
321
+ ready.ready.push(task);
322
+ safeWriteJson(this.readyPath, ready);
323
+
324
+ issue.promotedToTask = task.id;
325
+ this.save();
326
+
327
+ return { success: true, taskId: task.id };
328
+ }
329
+
330
+ /**
331
+ * Get statistics
332
+ */
333
+ getStats() {
334
+ this.updateStats();
335
+ return {
336
+ ...this.data.stats,
337
+ agingCount: this.getAgingIssues().length
338
+ };
339
+ }
340
+
341
+ /**
342
+ * Run auto-fixes for all auto-fixable issues
343
+ * Returns list of files modified
344
+ */
345
+ runAutoFix() {
346
+ const fixableIssues = this.getAutoFixable();
347
+ const fixedFiles = new Map();
348
+ const fixed = [];
349
+ const failed = [];
350
+
351
+ // Group by file
352
+ const byFile = new Map();
353
+ for (const issue of fixableIssues) {
354
+ if (!byFile.has(issue.file)) {
355
+ byFile.set(issue.file, []);
356
+ }
357
+ byFile.get(issue.file).push(issue);
358
+ }
359
+
360
+ // Process each file
361
+ for (const [filePath, issues] of byFile) {
362
+ const fullPath = path.join(this.projectRoot, filePath);
363
+
364
+ if (!fs.existsSync(fullPath)) {
365
+ for (const issue of issues) {
366
+ failed.push({ issue, reason: 'File not found' });
367
+ }
368
+ continue;
369
+ }
370
+
371
+ try {
372
+ let content = fs.readFileSync(fullPath, 'utf-8');
373
+ let lines = content.split('\n');
374
+ const linesToRemove = new Set();
375
+
376
+ // Collect lines to fix
377
+ for (const issue of issues) {
378
+ const desc = issue.description.toLowerCase();
379
+
380
+ // console.log - remove line
381
+ if (/console\.(log|debug|info|warn)/i.test(desc)) {
382
+ if (issue.line > 0 && issue.line <= lines.length) {
383
+ const line = lines[issue.line - 1];
384
+ if (/console\.(log|debug|info|warn)\s*\(/.test(line)) {
385
+ linesToRemove.add(issue.line - 1);
386
+ fixed.push(issue);
387
+ continue;
388
+ }
389
+ }
390
+ }
391
+
392
+ // debugger - remove line
393
+ if (/debugger\s+statement/i.test(desc)) {
394
+ if (issue.line > 0 && issue.line <= lines.length) {
395
+ const line = lines[issue.line - 1];
396
+ if (/^\s*debugger\s*;?\s*$/.test(line)) {
397
+ linesToRemove.add(issue.line - 1);
398
+ fixed.push(issue);
399
+ continue;
400
+ }
401
+ }
402
+ }
403
+
404
+ // empty catch - add comment
405
+ if (/empty\s+catch/i.test(desc)) {
406
+ if (issue.line > 0 && issue.line <= lines.length) {
407
+ const line = lines[issue.line - 1];
408
+ if (/catch\s*(\([^)]*\))?\s*\{\s*\}/.test(line)) {
409
+ lines[issue.line - 1] = line.replace(
410
+ /catch\s*(\([^)]*\))?\s*\{\s*\}/,
411
+ (match, param) => `catch ${param || '(err)'} { /* intentionally empty */ }`
412
+ );
413
+ fixed.push(issue);
414
+ continue;
415
+ }
416
+ }
417
+ }
418
+
419
+ // If we couldn't fix it
420
+ failed.push({ issue, reason: 'Could not apply fix' });
421
+ }
422
+
423
+ // Remove lines (in reverse order to preserve line numbers)
424
+ const sortedLinesToRemove = Array.from(linesToRemove).sort((a, b) => b - a);
425
+ for (const lineIndex of sortedLinesToRemove) {
426
+ lines.splice(lineIndex, 1);
427
+ }
428
+
429
+ // Write back
430
+ if (linesToRemove.size > 0 || fixed.some(i => i.file === filePath)) {
431
+ fs.writeFileSync(fullPath, lines.join('\n'), 'utf-8');
432
+ fixedFiles.set(filePath, issues.filter(i => fixed.includes(i)).length);
433
+ }
434
+
435
+ } catch (err) {
436
+ for (const issue of issues) {
437
+ failed.push({ issue, reason: err.message });
438
+ }
439
+ }
440
+ }
441
+
442
+ // Mark fixed issues
443
+ for (const issue of fixed) {
444
+ this.markFixed(issue.id);
445
+ }
446
+
447
+ return {
448
+ fixed: fixed.length,
449
+ failed: failed.length,
450
+ files: Array.from(fixedFiles.keys()),
451
+ details: { fixed, failed }
452
+ };
453
+ }
454
+
455
+ /**
456
+ * Create tasks for all aging issues
457
+ */
458
+ promoteAgingToTasks() {
459
+ const aging = this.getAgingIssues().filter(i => !i.promotedToTask);
460
+ const promoted = [];
461
+
462
+ for (const issue of aging) {
463
+ const result = this.promoteToTask(issue.id);
464
+ if (result.success) {
465
+ promoted.push({ issue, taskId: result.taskId });
466
+ }
467
+ }
468
+
469
+ return promoted;
470
+ }
471
+ }
472
+
473
+ // ============================================================================
474
+ // CLI Interface
475
+ // ============================================================================
476
+
477
+ function printHeader(text) {
478
+ const width = 60;
479
+ console.log(`${c.cyan}╔${'═'.repeat(width - 2)}╗${c.reset}`);
480
+ console.log(`${c.cyan}║${c.reset} ${c.bold}${text.padEnd(width - 4)}${c.reset}${c.cyan}║${c.reset}`);
481
+ console.log(`${c.cyan}╚${'═'.repeat(width - 2)}╝${c.reset}`);
482
+ }
483
+
484
+ function printSection(text) {
485
+ console.log(`\n${c.yellow}━━━ ${text} ━━━${c.reset}`);
486
+ }
487
+
488
+ function formatIssue(issue, showDetails = true) {
489
+ const severityColors = {
490
+ critical: c.red,
491
+ high: c.yellow,
492
+ medium: c.blue,
493
+ low: c.dim
494
+ };
495
+ const color = severityColors[issue.severity] || c.dim;
496
+
497
+ let line = ` ${c.dim}[${issue.id}]${c.reset} ${issue.file}`;
498
+ if (issue.line) line += `:${issue.line}`;
499
+ line += ` ${color}(${issue.severity})${c.reset}`;
500
+
501
+ if (issue.sessionsSeen > 1) {
502
+ line += ` ${c.yellow}⚠ ${issue.sessionsSeen} sessions${c.reset}`;
503
+ }
504
+
505
+ console.log(line);
506
+
507
+ if (showDetails) {
508
+ console.log(` ${c.dim}${issue.description}${c.reset}`);
509
+ if (issue.fix) {
510
+ console.log(` ${c.green}Fix: ${issue.fix}${c.reset}`);
511
+ }
512
+ }
513
+ }
514
+
515
+ function showSummary(manager) {
516
+ const stats = manager.getStats();
517
+
518
+ printHeader('Technical Debt Dashboard');
519
+ console.log('');
520
+
521
+ console.log(`${c.bold}Summary:${c.reset} ${stats.totalOpen} open items`);
522
+ console.log(` ${c.red}Critical: ${stats.bySeverity.critical}${c.reset} ` +
523
+ `${c.yellow}High: ${stats.bySeverity.high}${c.reset} ` +
524
+ `${c.blue}Medium: ${stats.bySeverity.medium}${c.reset} ` +
525
+ `${c.dim}Low: ${stats.bySeverity.low}${c.reset}`);
526
+
527
+ if (stats.agingCount > 0) {
528
+ console.log(`\n${c.yellow}⚠ ${stats.agingCount} items aging (3+ sessions)${c.reset}`);
529
+ }
530
+
531
+ if (stats.autoFixable > 0) {
532
+ console.log(`${c.green}✓ ${stats.autoFixable} auto-fixable items available${c.reset}`);
533
+ }
534
+
535
+ // Show quick commands
536
+ console.log(`\n${c.dim}Commands:${c.reset}`);
537
+ console.log(` ${c.cyan}flow tech-debt list${c.reset} List all items`);
538
+ console.log(` ${c.cyan}flow tech-debt list --aging${c.reset} Show aging items`);
539
+ console.log(` ${c.cyan}flow tech-debt fix${c.reset} Run auto-fixes`);
540
+ }
541
+
542
+ function showList(manager, options = {}) {
543
+ let issues;
544
+ let title;
545
+
546
+ if (options.aging) {
547
+ issues = manager.getAgingIssues();
548
+ title = 'Aging Issues (3+ sessions)';
549
+ } else if (options.fixable) {
550
+ issues = manager.getAutoFixable();
551
+ title = 'Auto-Fixable Issues';
552
+ } else if (options.severity) {
553
+ issues = manager.getBySeverity(options.severity);
554
+ title = `${options.severity.charAt(0).toUpperCase() + options.severity.slice(1)} Issues`;
555
+ } else {
556
+ issues = manager.getOpenIssues();
557
+ title = 'All Open Issues';
558
+ }
559
+
560
+ printSection(title);
561
+
562
+ if (issues.length === 0) {
563
+ console.log(` ${c.dim}No issues found${c.reset}`);
564
+ return;
565
+ }
566
+
567
+ for (const issue of issues) {
568
+ formatIssue(issue, options.verbose);
569
+ }
570
+
571
+ console.log(`\n${c.dim}Total: ${issues.length} items${c.reset}`);
572
+ }
573
+
574
+ function runFix(manager) {
575
+ const fixable = manager.getAutoFixable();
576
+
577
+ if (fixable.length === 0) {
578
+ console.log(`${c.yellow}No auto-fixable issues found.${c.reset}`);
579
+ return;
580
+ }
581
+
582
+ console.log(`${c.cyan}Running auto-fix on ${fixable.length} items...${c.reset}\n`);
583
+
584
+ const result = manager.runAutoFix();
585
+
586
+ if (result.fixed > 0) {
587
+ console.log(`${c.green}✓ Fixed ${result.fixed} issues${c.reset}`);
588
+ for (const file of result.files) {
589
+ console.log(` ${c.dim}${file}${c.reset}`);
590
+ }
591
+ }
592
+
593
+ if (result.failed > 0) {
594
+ console.log(`\n${c.yellow}⚠ Could not fix ${result.failed} issues${c.reset}`);
595
+ for (const { issue, reason } of result.details.failed) {
596
+ console.log(` ${c.dim}[${issue.id}] ${reason}${c.reset}`);
597
+ }
598
+ }
599
+ }
600
+
601
+ function dismissIssue(manager, id, reason) {
602
+ if (manager.dismiss(id, reason)) {
603
+ console.log(`${c.green}✓ Dismissed: ${id}${c.reset}`);
604
+ } else {
605
+ console.log(`${c.red}✗ Issue not found: ${id}${c.reset}`);
606
+ }
607
+ }
608
+
609
+ function promoteIssue(manager, id) {
610
+ const result = manager.promoteToTask(id);
611
+ if (result.success) {
612
+ console.log(`${c.green}✓ Created task: ${result.taskId}${c.reset}`);
613
+ } else {
614
+ console.log(`${c.red}✗ ${result.error}${c.reset}`);
615
+ }
616
+ }
617
+
618
+ function showHelp() {
619
+ console.log(`
620
+ ${c.bold}Technical Debt Manager${c.reset}
621
+
622
+ ${c.cyan}Usage:${c.reset}
623
+ flow tech-debt Show summary dashboard
624
+ flow tech-debt list List all open items
625
+ flow tech-debt list --aging Show items seen 3+ sessions
626
+ flow tech-debt list --fixable Show auto-fixable items
627
+ flow tech-debt list --severity X Filter by severity (critical/high/medium/low)
628
+ flow tech-debt fix Run auto-fixes (batch)
629
+ flow tech-debt dismiss <id> Mark as won't-fix
630
+ flow tech-debt promote <id> Create task from debt item
631
+ flow tech-debt promote-aging Create tasks for all aging items
632
+
633
+ ${c.cyan}Options:${c.reset}
634
+ -v, --verbose Show detailed descriptions
635
+ --help Show this help
636
+
637
+ ${c.cyan}Examples:${c.reset}
638
+ flow tech-debt list --aging -v Show aging items with details
639
+ flow tech-debt fix Auto-fix all safe items
640
+ flow tech-debt dismiss td-abc123 Dismiss a specific item
641
+ `);
642
+ }
643
+
644
+ function main() {
645
+ const args = process.argv.slice(2);
646
+ const command = args[0] || 'summary';
647
+
648
+ const manager = new TechDebtManager();
649
+
650
+ // Parse flags
651
+ const flags = {
652
+ aging: args.includes('--aging'),
653
+ fixable: args.includes('--fixable'),
654
+ verbose: args.includes('-v') || args.includes('--verbose'),
655
+ severity: null
656
+ };
657
+
658
+ const severityIndex = args.indexOf('--severity');
659
+ if (severityIndex !== -1 && args[severityIndex + 1]) {
660
+ flags.severity = args[severityIndex + 1];
661
+ }
662
+
663
+ switch (command) {
664
+ case 'summary':
665
+ case 'status':
666
+ showSummary(manager);
667
+ break;
668
+
669
+ case 'list':
670
+ showList(manager, flags);
671
+ break;
672
+
673
+ case 'fix':
674
+ runFix(manager);
675
+ break;
676
+
677
+ case 'dismiss':
678
+ const dismissId = args[1];
679
+ const reason = args.slice(2).join(' ');
680
+ if (!dismissId) {
681
+ console.log(`${c.red}Usage: flow tech-debt dismiss <id> [reason]${c.reset}`);
682
+ process.exit(1);
683
+ }
684
+ dismissIssue(manager, dismissId, reason);
685
+ break;
686
+
687
+ case 'promote':
688
+ const promoteId = args[1];
689
+ if (!promoteId) {
690
+ console.log(`${c.red}Usage: flow tech-debt promote <id>${c.reset}`);
691
+ process.exit(1);
692
+ }
693
+ promoteIssue(manager, promoteId);
694
+ break;
695
+
696
+ case 'promote-aging':
697
+ const promoted = manager.promoteAgingToTasks();
698
+ if (promoted.length > 0) {
699
+ console.log(`${c.green}✓ Created ${promoted.length} tasks from aging items:${c.reset}`);
700
+ for (const { issue, taskId } of promoted) {
701
+ console.log(` ${taskId}: ${issue.description.slice(0, 50)}`);
702
+ }
703
+ } else {
704
+ console.log(`${c.dim}No aging items to promote${c.reset}`);
705
+ }
706
+ break;
707
+
708
+ case '--help':
709
+ case 'help':
710
+ showHelp();
711
+ break;
712
+
713
+ default:
714
+ console.log(`${c.red}Unknown command: ${command}${c.reset}`);
715
+ showHelp();
716
+ process.exit(1);
717
+ }
718
+ }
719
+
720
+ // ============================================================================
721
+ // Exports
722
+ // ============================================================================
723
+
724
+ module.exports = {
725
+ TechDebtManager,
726
+ generateDebtId,
727
+ AUTO_FIXABLE_TYPES,
728
+ DEFAULT_AGING_THRESHOLD
729
+ };
730
+
731
+ // Run CLI if executed directly
732
+ if (require.main === module) {
733
+ main();
734
+ }