kiro-spec-engine 1.4.4 → 1.5.5

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.
@@ -0,0 +1,717 @@
1
+ /**
2
+ * Document Governance Command Handler
3
+ *
4
+ * Provides CLI interface for document governance operations
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const ConfigManager = require('../governance/config-manager');
9
+ const DiagnosticEngine = require('../governance/diagnostic-engine');
10
+ const CleanupTool = require('../governance/cleanup-tool');
11
+ const ValidationEngine = require('../governance/validation-engine');
12
+ const ArchiveTool = require('../governance/archive-tool');
13
+ const HooksManager = require('../governance/hooks-manager');
14
+ const Reporter = require('../governance/reporter');
15
+ const ExecutionLogger = require('../governance/execution-logger');
16
+
17
+ /**
18
+ * Execute document governance command
19
+ *
20
+ * @param {string} subcommand - The subcommand (cleanup, validate, archive, etc.)
21
+ * @param {Object} options - Command options
22
+ * @returns {Promise<number>} - Exit code (0 for success, non-zero for error)
23
+ */
24
+ async function docsCommand(subcommand, options = {}) {
25
+ const projectPath = process.cwd();
26
+ const reporter = new Reporter();
27
+
28
+ try {
29
+ // Load configuration
30
+ const configManager = new ConfigManager(projectPath);
31
+ const config = await configManager.load();
32
+
33
+ // Route to appropriate handler
34
+ switch (subcommand) {
35
+ case 'diagnose':
36
+ case 'diagnostic':
37
+ return await handleDiagnostic(projectPath, config, reporter);
38
+
39
+ case 'cleanup':
40
+ return await handleCleanup(projectPath, config, reporter, options);
41
+
42
+ case 'validate':
43
+ return await handleValidate(projectPath, config, reporter, options);
44
+
45
+ case 'archive':
46
+ return await handleArchive(projectPath, config, reporter, options);
47
+
48
+ case 'hooks':
49
+ return await handleHooks(projectPath, reporter, options);
50
+
51
+ case 'config':
52
+ return await handleConfig(projectPath, configManager, reporter, options);
53
+
54
+ case 'stats':
55
+ return await handleStats(projectPath, reporter, options);
56
+
57
+ case 'report':
58
+ return await handleReport(projectPath, reporter, options);
59
+
60
+ case 'help':
61
+ default:
62
+ showHelp();
63
+ return 0;
64
+ }
65
+ } catch (error) {
66
+ reporter.displayError(error.message);
67
+ if (options.verbose) {
68
+ console.error(error.stack);
69
+ }
70
+ return 2; // Fatal error
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Handle diagnostic command
76
+ */
77
+ async function handleDiagnostic(projectPath, config, reporter) {
78
+ const engine = new DiagnosticEngine(projectPath, config);
79
+ const report = await engine.scan();
80
+
81
+ reporter.displayDiagnostic(report);
82
+
83
+ // Log execution
84
+ const logger = new ExecutionLogger(projectPath);
85
+ await logger.logExecution('diagnostic', 'scan', report);
86
+
87
+ return report.compliant ? 0 : 1;
88
+ }
89
+
90
+ /**
91
+ * Handle cleanup command
92
+ */
93
+ async function handleCleanup(projectPath, config, reporter, options) {
94
+ const tool = new CleanupTool(projectPath, config);
95
+
96
+ const cleanupOptions = {
97
+ dryRun: options.dryRun || options.dry || false,
98
+ interactive: options.interactive || options.i || false,
99
+ spec: options.spec || null
100
+ };
101
+
102
+ const report = await tool.cleanup(cleanupOptions);
103
+
104
+ reporter.displayCleanup(report, cleanupOptions.dryRun);
105
+
106
+ // Log execution (only if not dry run)
107
+ if (!cleanupOptions.dryRun) {
108
+ const logger = new ExecutionLogger(projectPath);
109
+ await logger.logExecution('cleanup', 'delete', report);
110
+ }
111
+
112
+ return report.success ? 0 : 1;
113
+ }
114
+
115
+ /**
116
+ * Handle validate command
117
+ */
118
+ async function handleValidate(projectPath, config, reporter, options) {
119
+ const engine = new ValidationEngine(projectPath, config);
120
+
121
+ const validateOptions = {
122
+ spec: options.spec || null,
123
+ all: options.all || false
124
+ };
125
+
126
+ const report = await engine.validate(validateOptions);
127
+
128
+ reporter.displayValidation(report);
129
+
130
+ // Log execution
131
+ const logger = new ExecutionLogger(projectPath);
132
+ await logger.logExecution('validation', 'validate', report);
133
+
134
+ return report.valid ? 0 : 1;
135
+ }
136
+
137
+ /**
138
+ * Handle archive command
139
+ */
140
+ async function handleArchive(projectPath, config, reporter, options) {
141
+ if (!options.spec) {
142
+ reporter.displayError('--spec option is required for archive command');
143
+ console.log('Usage: kse docs archive --spec <spec-name> [--dry-run]');
144
+ return 2;
145
+ }
146
+
147
+ const tool = new ArchiveTool(projectPath, config);
148
+
149
+ const archiveOptions = {
150
+ dryRun: options.dryRun || options.dry || false
151
+ };
152
+
153
+ const report = await tool.archive(options.spec, archiveOptions);
154
+
155
+ reporter.displayArchive(report, archiveOptions.dryRun);
156
+
157
+ // Log execution (only if not dry run)
158
+ if (!archiveOptions.dryRun) {
159
+ const logger = new ExecutionLogger(projectPath);
160
+ await logger.logExecution('archive', 'move', report);
161
+ }
162
+
163
+ return report.success ? 0 : 1;
164
+ }
165
+
166
+ /**
167
+ * Handle hooks command
168
+ */
169
+ async function handleHooks(projectPath, reporter, options) {
170
+ const manager = new HooksManager(projectPath);
171
+
172
+ // Determine subcommand (install, uninstall, status)
173
+ const hookSubcommand = options._?.[0] || 'status';
174
+
175
+ switch (hookSubcommand) {
176
+ case 'install':
177
+ return await handleHooksInstall(manager, reporter);
178
+
179
+ case 'uninstall':
180
+ return await handleHooksUninstall(manager, reporter);
181
+
182
+ case 'status':
183
+ default:
184
+ return await handleHooksStatus(manager, reporter);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Handle hooks install
190
+ */
191
+ async function handleHooksInstall(manager, reporter) {
192
+ console.log(chalk.cyan('šŸ”§ Installing document governance hooks...\n'));
193
+
194
+ const result = await manager.installHooks();
195
+
196
+ if (result.success) {
197
+ console.log(chalk.green('āœ… ' + result.message));
198
+
199
+ if (result.backupCreated) {
200
+ console.log(chalk.gray(' Backup created at: .git/hooks/pre-commit.backup'));
201
+ }
202
+
203
+ console.log(chalk.cyan('\nThe pre-commit hook will now validate documents before each commit.'));
204
+ console.log(chalk.gray('To bypass validation, use: git commit --no-verify\n'));
205
+
206
+ return 0;
207
+ } else {
208
+ console.log(chalk.red('āŒ ' + result.message));
209
+
210
+ if (result.reason === 'not_git_repo') {
211
+ console.log(chalk.yellow('\nThis is not a Git repository.'));
212
+ console.log(chalk.gray('Initialize Git first: git init\n'));
213
+ }
214
+
215
+ return 1;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Handle hooks uninstall
221
+ */
222
+ async function handleHooksUninstall(manager, reporter) {
223
+ console.log(chalk.cyan('šŸ”§ Uninstalling document governance hooks...\n'));
224
+
225
+ const result = await manager.uninstallHooks();
226
+
227
+ if (result.success) {
228
+ console.log(chalk.green('āœ… ' + result.message + '\n'));
229
+ return 0;
230
+ } else {
231
+ console.log(chalk.red('āŒ ' + result.message));
232
+
233
+ if (result.reason === 'not_our_hook') {
234
+ console.log(chalk.yellow('\nThe pre-commit hook was not installed by kiro-spec-engine.'));
235
+ console.log(chalk.gray('You may need to manually edit: .git/hooks/pre-commit\n'));
236
+ }
237
+
238
+ return 1;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Handle hooks status
244
+ */
245
+ async function handleHooksStatus(manager, reporter) {
246
+ console.log(chalk.cyan('šŸ” Checking Git hooks status...\n'));
247
+
248
+ const status = await manager.checkHooksInstalled();
249
+
250
+ if (status.installed) {
251
+ console.log(chalk.green('āœ… Document governance hooks are installed'));
252
+ console.log(chalk.gray(' Pre-commit validation is active\n'));
253
+ return 0;
254
+ } else {
255
+ console.log(chalk.yellow('āš ļø Document governance hooks are not installed'));
256
+ console.log(chalk.gray(' Reason: ' + status.message));
257
+ console.log(chalk.cyan('\nTo install hooks: kse docs hooks install\n'));
258
+ return 1;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Handle config command
264
+ */
265
+ async function handleConfig(projectPath, configManager, reporter, options) {
266
+ // Check if --set flag is provided
267
+ if (options.set) {
268
+ return await handleConfigSet(configManager, reporter, options);
269
+ }
270
+
271
+ // Check if --reset flag is provided
272
+ if (options.reset) {
273
+ return await handleConfigReset(configManager, reporter);
274
+ }
275
+
276
+ // Default: display current configuration
277
+ return await handleConfigDisplay(configManager, reporter);
278
+ }
279
+
280
+ /**
281
+ * Handle config display
282
+ */
283
+ async function handleConfigDisplay(configManager, reporter) {
284
+ console.log(chalk.bold.cyan('\nāš™ļø Document Governance Configuration\n'));
285
+
286
+ const config = configManager.getAll();
287
+
288
+ console.log(chalk.bold('Root Allowed Files:'));
289
+ config.rootAllowedFiles.forEach(file => {
290
+ console.log(` • ${file}`);
291
+ });
292
+ console.log();
293
+
294
+ console.log(chalk.bold('Spec Subdirectories:'));
295
+ config.specSubdirs.forEach(dir => {
296
+ console.log(` • ${dir}`);
297
+ });
298
+ console.log();
299
+
300
+ console.log(chalk.bold('Temporary Patterns:'));
301
+ config.temporaryPatterns.forEach(pattern => {
302
+ console.log(` • ${pattern}`);
303
+ });
304
+ console.log();
305
+
306
+ console.log(chalk.gray('Configuration file: .kiro/config/docs.json'));
307
+ console.log(chalk.gray('To modify: kse docs config --set <key> <value>'));
308
+ console.log(chalk.gray('To reset: kse docs config --reset\n'));
309
+
310
+ return 0;
311
+ }
312
+
313
+ /**
314
+ * Handle config set
315
+ */
316
+ async function handleConfigSet(configManager, reporter, options) {
317
+ // Parse the key and value from options
318
+ // Expected format: --set key value or --set key "value1,value2"
319
+ const args = options._ || [];
320
+
321
+ // Find the index of the key after 'config'
322
+ const configIndex = args.indexOf('config');
323
+ const keyIndex = configIndex + 1;
324
+
325
+ if (keyIndex >= args.length) {
326
+ reporter.displayError('Missing configuration key');
327
+ console.log(chalk.gray('Usage: kse docs config --set <key> <value>'));
328
+ console.log(chalk.gray('Example: kse docs config --set root-allowed-files "README.md,CUSTOM.md"\n'));
329
+ return 2;
330
+ }
331
+
332
+ const key = args[keyIndex];
333
+ const valueIndex = keyIndex + 1;
334
+
335
+ if (valueIndex >= args.length) {
336
+ reporter.displayError('Missing configuration value');
337
+ console.log(chalk.gray('Usage: kse docs config --set <key> <value>'));
338
+ console.log(chalk.gray('Example: kse docs config --set root-allowed-files "README.md,CUSTOM.md"\n'));
339
+ return 2;
340
+ }
341
+
342
+ const valueStr = args[valueIndex];
343
+
344
+ // Convert kebab-case to camelCase
345
+ const camelKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
346
+
347
+ // Validate key
348
+ const validKeys = ['rootAllowedFiles', 'specSubdirs', 'temporaryPatterns'];
349
+ if (!validKeys.includes(camelKey)) {
350
+ reporter.displayError(`Invalid configuration key: ${key}`);
351
+ console.log(chalk.gray('Valid keys: root-allowed-files, spec-subdirs, temporary-patterns\n'));
352
+ return 2;
353
+ }
354
+
355
+ // Parse value (comma-separated list)
356
+ const value = valueStr.split(',').map(v => v.trim()).filter(v => v.length > 0);
357
+
358
+ if (value.length === 0) {
359
+ reporter.displayError('Value cannot be empty');
360
+ return 2;
361
+ }
362
+
363
+ // Set the value
364
+ try {
365
+ await configManager.set(camelKey, value);
366
+
367
+ console.log(chalk.green(`āœ… Configuration updated: ${key}`));
368
+ console.log(chalk.gray(` New value: ${value.join(', ')}\n`));
369
+
370
+ return 0;
371
+ } catch (error) {
372
+ reporter.displayError(`Failed to update configuration: ${error.message}`);
373
+ return 1;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Handle config reset
379
+ */
380
+ async function handleConfigReset(configManager, reporter) {
381
+ console.log(chalk.yellow('āš ļø Resetting configuration to defaults...\n'));
382
+
383
+ try {
384
+ await configManager.reset();
385
+
386
+ console.log(chalk.green('āœ… Configuration reset to defaults'));
387
+ console.log(chalk.gray(' Run "kse docs config" to view current configuration\n'));
388
+
389
+ return 0;
390
+ } catch (error) {
391
+ reporter.displayError(`Failed to reset configuration: ${error.message}`);
392
+ return 1;
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Handle stats command
398
+ */
399
+ async function handleStats(projectPath, reporter, options) {
400
+ const logger = new ExecutionLogger(projectPath);
401
+
402
+ // Get execution history
403
+ const history = await logger.getHistory();
404
+
405
+ if (history.length === 0) {
406
+ console.log(chalk.yellow('āš ļø No execution history found'));
407
+ console.log(chalk.gray(' Run document governance commands to generate statistics\n'));
408
+ return 0;
409
+ }
410
+
411
+ // Calculate statistics
412
+ const stats = calculateStatistics(history);
413
+
414
+ // Display statistics
415
+ reporter.displayStats(stats);
416
+
417
+ return 0;
418
+ }
419
+
420
+ /**
421
+ * Handle report command
422
+ */
423
+ async function handleReport(projectPath, reporter, options) {
424
+ const logger = new ExecutionLogger(projectPath);
425
+ const fs = require('fs-extra');
426
+ const path = require('path');
427
+
428
+ // Get execution history
429
+ const history = await logger.getHistory();
430
+
431
+ if (history.length === 0) {
432
+ console.log(chalk.yellow('āš ļø No execution history found'));
433
+ console.log(chalk.gray(' Run document governance commands to generate a report\n'));
434
+ return 0;
435
+ }
436
+
437
+ // Calculate statistics
438
+ const stats = calculateStatistics(history);
439
+
440
+ // Generate markdown report
441
+ const report = generateMarkdownReport(stats, history);
442
+
443
+ // Ensure reports directory exists
444
+ const reportsDir = path.join(projectPath, '.kiro', 'reports');
445
+ await fs.ensureDir(reportsDir);
446
+
447
+ // Generate filename with timestamp
448
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
449
+ const filename = `document-compliance-${timestamp}.md`;
450
+ const reportPath = path.join(reportsDir, filename);
451
+
452
+ // Save report
453
+ await fs.writeFile(reportPath, report, 'utf8');
454
+
455
+ console.log(chalk.green('āœ… Compliance report generated'));
456
+ console.log(chalk.gray(` Saved to: ${reportPath}\n`));
457
+
458
+ return 0;
459
+ }
460
+
461
+ /**
462
+ * Calculate statistics from execution history
463
+ *
464
+ * @param {Array} history - Execution history entries
465
+ * @returns {Object} - Statistics object
466
+ */
467
+ function calculateStatistics(history) {
468
+ const stats = {
469
+ totalExecutions: history.length,
470
+ executionsByTool: {},
471
+ totalViolations: 0,
472
+ violationsByType: {},
473
+ totalCleanupActions: 0,
474
+ totalArchiveActions: 0,
475
+ totalErrors: 0,
476
+ firstExecution: null,
477
+ lastExecution: null,
478
+ violationsOverTime: [],
479
+ cleanupActionsOverTime: []
480
+ };
481
+
482
+ // Process each history entry
483
+ history.forEach(entry => {
484
+ // Track executions by tool
485
+ if (!stats.executionsByTool[entry.tool]) {
486
+ stats.executionsByTool[entry.tool] = 0;
487
+ }
488
+ stats.executionsByTool[entry.tool]++;
489
+
490
+ // Track timestamps
491
+ if (!stats.firstExecution || entry.timestamp < stats.firstExecution) {
492
+ stats.firstExecution = entry.timestamp;
493
+ }
494
+ if (!stats.lastExecution || entry.timestamp > stats.lastExecution) {
495
+ stats.lastExecution = entry.timestamp;
496
+ }
497
+
498
+ // Process diagnostic results
499
+ if (entry.tool === 'diagnostic' && entry.results) {
500
+ if (entry.results.violations) {
501
+ const violationCount = entry.results.violations.length;
502
+ stats.totalViolations += violationCount;
503
+
504
+ // Track violations over time
505
+ stats.violationsOverTime.push({
506
+ timestamp: entry.timestamp,
507
+ count: violationCount
508
+ });
509
+
510
+ // Track violations by type
511
+ entry.results.violations.forEach(violation => {
512
+ if (!stats.violationsByType[violation.type]) {
513
+ stats.violationsByType[violation.type] = 0;
514
+ }
515
+ stats.violationsByType[violation.type]++;
516
+ });
517
+ }
518
+ }
519
+
520
+ // Process cleanup results
521
+ if (entry.tool === 'cleanup' && entry.results) {
522
+ if (entry.results.deletedFiles) {
523
+ const cleanupCount = entry.results.deletedFiles.length;
524
+ stats.totalCleanupActions += cleanupCount;
525
+
526
+ // Track cleanup actions over time
527
+ stats.cleanupActionsOverTime.push({
528
+ timestamp: entry.timestamp,
529
+ count: cleanupCount
530
+ });
531
+ }
532
+
533
+ if (entry.results.errors) {
534
+ stats.totalErrors += entry.results.errors.length;
535
+ }
536
+ }
537
+
538
+ // Process archive results
539
+ if (entry.tool === 'archive' && entry.results) {
540
+ if (entry.results.movedFiles) {
541
+ stats.totalArchiveActions += entry.results.movedFiles.length;
542
+ }
543
+
544
+ if (entry.results.errors) {
545
+ stats.totalErrors += entry.results.errors.length;
546
+ }
547
+ }
548
+
549
+ // Process validation results
550
+ if (entry.tool === 'validation' && entry.results) {
551
+ if (entry.results.errors) {
552
+ stats.totalErrors += entry.results.errors.length;
553
+ }
554
+ }
555
+ });
556
+
557
+ return stats;
558
+ }
559
+
560
+ /**
561
+ * Generate markdown compliance report
562
+ *
563
+ * @param {Object} stats - Statistics object
564
+ * @param {Array} history - Execution history
565
+ * @returns {string} - Markdown report
566
+ */
567
+ function generateMarkdownReport(stats, history) {
568
+ const lines = [];
569
+
570
+ // Header
571
+ lines.push('# Document Compliance Report');
572
+ lines.push('');
573
+ lines.push(`**Generated:** ${new Date().toISOString()}`);
574
+ lines.push('');
575
+
576
+ // Summary
577
+ lines.push('## Summary');
578
+ lines.push('');
579
+ lines.push(`- **Total Executions:** ${stats.totalExecutions}`);
580
+ lines.push(`- **Total Violations Found:** ${stats.totalViolations}`);
581
+ lines.push(`- **Total Cleanup Actions:** ${stats.totalCleanupActions}`);
582
+ lines.push(`- **Total Archive Actions:** ${stats.totalArchiveActions}`);
583
+ lines.push(`- **Total Errors:** ${stats.totalErrors}`);
584
+ lines.push(`- **First Execution:** ${stats.firstExecution || 'N/A'}`);
585
+ lines.push(`- **Last Execution:** ${stats.lastExecution || 'N/A'}`);
586
+ lines.push('');
587
+
588
+ // Executions by Tool
589
+ lines.push('## Executions by Tool');
590
+ lines.push('');
591
+ lines.push('| Tool | Count |');
592
+ lines.push('|------|-------|');
593
+ Object.entries(stats.executionsByTool).forEach(([tool, count]) => {
594
+ lines.push(`| ${tool} | ${count} |`);
595
+ });
596
+ lines.push('');
597
+
598
+ // Violations by Type
599
+ if (Object.keys(stats.violationsByType).length > 0) {
600
+ lines.push('## Violations by Type');
601
+ lines.push('');
602
+ lines.push('| Type | Count |');
603
+ lines.push('|------|-------|');
604
+ Object.entries(stats.violationsByType)
605
+ .sort((a, b) => b[1] - a[1])
606
+ .forEach(([type, count]) => {
607
+ lines.push(`| ${type} | ${count} |`);
608
+ });
609
+ lines.push('');
610
+ }
611
+
612
+ // Violations Over Time
613
+ if (stats.violationsOverTime.length > 0) {
614
+ lines.push('## Violations Over Time');
615
+ lines.push('');
616
+ lines.push('| Timestamp | Count |');
617
+ lines.push('|-----------|-------|');
618
+ stats.violationsOverTime.forEach(entry => {
619
+ lines.push(`| ${entry.timestamp} | ${entry.count} |`);
620
+ });
621
+ lines.push('');
622
+ }
623
+
624
+ // Cleanup Actions Over Time
625
+ if (stats.cleanupActionsOverTime.length > 0) {
626
+ lines.push('## Cleanup Actions Over Time');
627
+ lines.push('');
628
+ lines.push('| Timestamp | Files Deleted |');
629
+ lines.push('|-----------|---------------|');
630
+ stats.cleanupActionsOverTime.forEach(entry => {
631
+ lines.push(`| ${entry.timestamp} | ${entry.count} |`);
632
+ });
633
+ lines.push('');
634
+ }
635
+
636
+ // Recent Executions
637
+ lines.push('## Recent Executions');
638
+ lines.push('');
639
+ const recentHistory = history.slice(-10).reverse();
640
+ recentHistory.forEach(entry => {
641
+ lines.push(`### ${entry.tool} - ${entry.operation}`);
642
+ lines.push('');
643
+ lines.push(`**Timestamp:** ${entry.timestamp}`);
644
+ lines.push('');
645
+
646
+ if (entry.results) {
647
+ if (entry.results.violations) {
648
+ lines.push(`**Violations Found:** ${entry.results.violations.length}`);
649
+ }
650
+ if (entry.results.deletedFiles) {
651
+ lines.push(`**Files Deleted:** ${entry.results.deletedFiles.length}`);
652
+ }
653
+ if (entry.results.movedFiles) {
654
+ lines.push(`**Files Moved:** ${entry.results.movedFiles.length}`);
655
+ }
656
+ if (entry.results.errors && entry.results.errors.length > 0) {
657
+ lines.push(`**Errors:** ${entry.results.errors.length}`);
658
+ }
659
+ }
660
+
661
+ lines.push('');
662
+ });
663
+
664
+ // Footer
665
+ lines.push('---');
666
+ lines.push('');
667
+ lines.push('*Generated by kiro-spec-engine document governance system*');
668
+ lines.push('');
669
+
670
+ return lines.join('\n');
671
+ }
672
+
673
+ /**
674
+ * Display help information
675
+ */
676
+ function showHelp() {
677
+ console.log(chalk.bold.cyan('\nšŸ“‹ Document Governance Commands\n'));
678
+
679
+ console.log(chalk.bold('Usage:'));
680
+ console.log(' kse docs <subcommand> [options]\n');
681
+
682
+ console.log(chalk.bold('Subcommands:'));
683
+ console.log(' diagnose Scan project for document violations');
684
+ console.log(' cleanup Remove temporary documents');
685
+ console.log(' validate Validate document structure');
686
+ console.log(' archive Organize Spec artifacts into subdirectories');
687
+ console.log(' hooks <action> Manage Git hooks (install, uninstall, status)');
688
+ console.log(' config Display or modify configuration');
689
+ console.log(' stats Display compliance statistics');
690
+ console.log(' report Generate compliance report');
691
+ console.log(' help Show this help message\n');
692
+
693
+ console.log(chalk.bold('Options:'));
694
+ console.log(' --dry-run, --dry Preview changes without applying them');
695
+ console.log(' --interactive, -i Prompt for confirmation (cleanup only)');
696
+ console.log(' --spec <name> Target specific Spec directory');
697
+ console.log(' --all Validate all Specs (validate only)');
698
+ console.log(' --set <key> <value> Set configuration value (config only)');
699
+ console.log(' --reset Reset configuration to defaults (config only)');
700
+ console.log(' --verbose Show detailed error information\n');
701
+
702
+ console.log(chalk.bold('Examples:'));
703
+ console.log(' kse docs diagnose');
704
+ console.log(' kse docs cleanup --dry-run');
705
+ console.log(' kse docs cleanup --spec my-spec');
706
+ console.log(' kse docs validate --all');
707
+ console.log(' kse docs archive --spec my-spec --dry-run');
708
+ console.log(' kse docs hooks install');
709
+ console.log(' kse docs hooks status');
710
+ console.log(' kse docs config');
711
+ console.log(' kse docs config --set root-allowed-files "README.md,CUSTOM.md"');
712
+ console.log(' kse docs config --reset');
713
+ console.log(' kse docs stats');
714
+ console.log(' kse docs report\n');
715
+ }
716
+
717
+ module.exports = docsCommand;