kiro-spec-engine 1.2.2 → 1.3.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 (49) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +172 -0
  3. package/bin/kiro-spec-engine.js +62 -0
  4. package/docs/adoption-guide.md +506 -0
  5. package/docs/agent-hooks-analysis.md +815 -0
  6. package/docs/architecture.md +706 -0
  7. package/docs/cross-tool-guide.md +554 -0
  8. package/docs/developer-guide.md +615 -0
  9. package/docs/manual-workflows-guide.md +417 -0
  10. package/docs/steering-strategy-guide.md +196 -0
  11. package/docs/upgrade-guide.md +632 -0
  12. package/lib/adoption/detection-engine.js +14 -4
  13. package/lib/commands/adopt.js +117 -3
  14. package/lib/commands/context.js +99 -0
  15. package/lib/commands/prompt.js +105 -0
  16. package/lib/commands/status.js +225 -0
  17. package/lib/commands/task.js +199 -0
  18. package/lib/commands/watch.js +569 -0
  19. package/lib/commands/workflows.js +240 -0
  20. package/lib/commands/workspace.js +189 -0
  21. package/lib/context/context-exporter.js +378 -0
  22. package/lib/context/prompt-generator.js +482 -0
  23. package/lib/steering/adoption-config.js +164 -0
  24. package/lib/steering/steering-manager.js +289 -0
  25. package/lib/task/task-claimer.js +430 -0
  26. package/lib/utils/tool-detector.js +383 -0
  27. package/lib/watch/action-executor.js +458 -0
  28. package/lib/watch/event-debouncer.js +323 -0
  29. package/lib/watch/execution-logger.js +550 -0
  30. package/lib/watch/file-watcher.js +499 -0
  31. package/lib/watch/presets.js +266 -0
  32. package/lib/watch/watch-manager.js +533 -0
  33. package/lib/workspace/workspace-manager.js +370 -0
  34. package/lib/workspace/workspace-sync.js +356 -0
  35. package/package.json +4 -1
  36. package/template/.kiro/tools/backup_manager.py +295 -0
  37. package/template/.kiro/tools/configuration_manager.py +218 -0
  38. package/template/.kiro/tools/document_evaluator.py +550 -0
  39. package/template/.kiro/tools/enhancement_logger.py +168 -0
  40. package/template/.kiro/tools/error_handler.py +335 -0
  41. package/template/.kiro/tools/improvement_identifier.py +444 -0
  42. package/template/.kiro/tools/modification_applicator.py +737 -0
  43. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  44. package/template/.kiro/tools/quality_scorer.py +305 -0
  45. package/template/.kiro/tools/report_generator.py +154 -0
  46. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  47. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  48. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  49. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
@@ -12,6 +12,9 @@ const DetectionEngine = require('../adoption/detection-engine');
12
12
  const { getAdoptionStrategy } = require('../adoption/adoption-strategy');
13
13
  const BackupSystem = require('../backup/backup-system');
14
14
  const VersionManager = require('../version/version-manager');
15
+ const SteeringManager = require('../steering/steering-manager');
16
+ const AdoptionConfig = require('../steering/adoption-config');
17
+ const { detectTool, generateAutoConfig } = require('../utils/tool-detector');
15
18
 
16
19
  /**
17
20
  * Executes the adopt command
@@ -134,7 +137,54 @@ async function adoptCommand(options = {}) {
134
137
 
135
138
  console.log();
136
139
 
137
- // 7. Create backup if needed
140
+ // 7. Handle steering strategy if conflicts detected
141
+ let steeringStrategy = null;
142
+ let steeringBackupId = null;
143
+
144
+ if (detection.steeringDetection && detection.steeringDetection.hasExistingSteering) {
145
+ console.log(chalk.blue('🎯 Handling steering files...'));
146
+ const steeringManager = new SteeringManager();
147
+
148
+ // Prompt for strategy
149
+ steeringStrategy = await steeringManager.promptStrategy(detection.steeringDetection);
150
+
151
+ if (steeringStrategy === 'use-kse') {
152
+ // Backup existing steering files
153
+ console.log(chalk.blue('📦 Backing up existing steering files...'));
154
+ const backupResult = await steeringManager.backupSteering(projectPath);
155
+
156
+ if (backupResult.success) {
157
+ steeringBackupId = backupResult.backupId;
158
+ console.log(chalk.green(`✅ Steering backup created: ${steeringBackupId}`));
159
+
160
+ // Install kse steering files
161
+ console.log(chalk.blue('📝 Installing kse steering files...'));
162
+ const installResult = await steeringManager.installKseSteering(projectPath);
163
+
164
+ if (installResult.success) {
165
+ console.log(chalk.green(`✅ Installed ${installResult.filesInstalled} kse steering file(s)`));
166
+ } else {
167
+ console.log(chalk.red(`❌ Failed to install kse steering: ${installResult.error}`));
168
+ console.log(chalk.yellow('Aborting adoption'));
169
+ return;
170
+ }
171
+ } else {
172
+ console.log(chalk.red(`❌ Failed to backup steering: ${backupResult.error}`));
173
+ console.log(chalk.yellow('Aborting adoption for safety'));
174
+ return;
175
+ }
176
+ } else if (steeringStrategy === 'use-project') {
177
+ console.log(chalk.blue('✅ Keeping existing steering files'));
178
+ }
179
+
180
+ // Save steering strategy to adoption config
181
+ const adoptionConfig = new AdoptionConfig(projectPath);
182
+ await adoptionConfig.updateSteeringStrategy(steeringStrategy, steeringBackupId);
183
+
184
+ console.log();
185
+ }
186
+
187
+ // 8. Create backup if needed
138
188
  let backupId = null;
139
189
  if (detection.hasKiroDir && (strategy === 'partial' || strategy === 'full')) {
140
190
  console.log(chalk.blue('📦 Creating backup...'));
@@ -153,7 +203,7 @@ async function adoptCommand(options = {}) {
153
203
 
154
204
  console.log();
155
205
 
156
- // 8. Execute adoption
206
+ // 9. Execute adoption
157
207
  console.log(chalk.blue('🚀 Executing adoption...'));
158
208
  const adoptionStrategy = getAdoptionStrategy(strategy);
159
209
  const packageJson = require('../../package.json');
@@ -166,11 +216,19 @@ async function adoptCommand(options = {}) {
166
216
 
167
217
  console.log();
168
218
 
169
- // 9. Report results
219
+ // 10. Report results
170
220
  if (result.success) {
171
221
  console.log(chalk.green('✅ Adoption completed successfully!'));
172
222
  console.log();
173
223
 
224
+ if (steeringStrategy) {
225
+ console.log(chalk.blue('Steering Strategy:'), steeringStrategy);
226
+ if (steeringBackupId) {
227
+ console.log(chalk.gray(' Backup:'), steeringBackupId);
228
+ }
229
+ console.log();
230
+ }
231
+
174
232
  if (result.filesCreated.length > 0) {
175
233
  console.log(chalk.blue('Files created:'));
176
234
  result.filesCreated.forEach(file => console.log(` + ${file}`));
@@ -198,6 +256,62 @@ async function adoptCommand(options = {}) {
198
256
  console.log(chalk.gray(' Run'), chalk.cyan('kse rollback'), chalk.gray('if you need to undo changes'));
199
257
  }
200
258
 
259
+ console.log();
260
+
261
+ // 11. Detect tool and offer automation setup
262
+ console.log(chalk.blue('🔍 Detecting your development environment...'));
263
+ try {
264
+ const toolDetection = await detectTool(projectPath);
265
+ const autoConfig = await generateAutoConfig(toolDetection, projectPath);
266
+
267
+ console.log();
268
+ console.log(chalk.blue('Tool Detected:'), chalk.cyan(toolDetection.primaryTool));
269
+ console.log(chalk.blue('Confidence:'), autoConfig.confidence);
270
+
271
+ if (autoConfig.notes.length > 0) {
272
+ console.log();
273
+ autoConfig.notes.forEach(note => console.log(chalk.gray(` ℹ️ ${note}`)));
274
+ }
275
+
276
+ // Offer automation setup (unless --auto)
277
+ if (!auto && autoConfig.suggestedPresets.length > 0) {
278
+ console.log();
279
+ const { setupAutomation } = await inquirer.prompt([
280
+ {
281
+ type: 'confirm',
282
+ name: 'setupAutomation',
283
+ message: 'Would you like to set up automation for this tool?',
284
+ default: true
285
+ }
286
+ ]);
287
+
288
+ if (setupAutomation) {
289
+ console.log();
290
+ console.log(chalk.blue('📋 Recommended automation setup:'));
291
+ console.log();
292
+ console.log(chalk.gray('Suggested presets:'));
293
+ autoConfig.suggestedPresets.forEach(preset => {
294
+ console.log(` - ${preset}`);
295
+ });
296
+ console.log();
297
+ console.log(chalk.gray('Run these commands to set up:'));
298
+ autoConfig.suggestedCommands.forEach(cmd => {
299
+ console.log(chalk.cyan(` ${cmd}`));
300
+ });
301
+ }
302
+ } else if (autoConfig.suggestedCommands.length > 0) {
303
+ console.log();
304
+ console.log(chalk.blue('💡 Automation setup:'));
305
+ autoConfig.suggestedCommands.forEach(cmd => {
306
+ console.log(chalk.gray(` ${cmd}`));
307
+ });
308
+ }
309
+ } catch (toolError) {
310
+ // Tool detection is optional, don't fail adoption if it errors
311
+ console.log(chalk.yellow('⚠️ Could not detect development tool'));
312
+ console.log(chalk.gray(' You can manually set up automation later'));
313
+ }
314
+
201
315
  console.log();
202
316
  console.log(chalk.blue('💡 Next steps:'));
203
317
  console.log(' 1. Review the .kiro/ directory structure');
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Context Command
3
+ *
4
+ * Exports spec context for cross-tool usage
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const ContextExporter = require('../context/context-exporter');
9
+
10
+ /**
11
+ * Export spec context
12
+ *
13
+ * @param {string} specName - Spec name
14
+ * @param {Object} options - Command options
15
+ * @param {boolean} options.requirements - Include requirements (default: true)
16
+ * @param {boolean} options.design - Include design (default: true)
17
+ * @param {boolean} options.tasks - Include tasks (default: true)
18
+ * @param {boolean} options.steering - Include steering rules
19
+ * @param {string} options.steeringFiles - Comma-separated list of steering files
20
+ * @returns {Promise<void>}
21
+ */
22
+ async function exportContext(specName, options = {}) {
23
+ const projectPath = process.cwd();
24
+ const exporter = new ContextExporter();
25
+
26
+ console.log(chalk.red('🔥') + ' Exporting Context');
27
+ console.log();
28
+
29
+ try {
30
+ console.log(`Spec: ${chalk.cyan(specName)}`);
31
+ console.log();
32
+
33
+ // Parse options
34
+ const exportOptions = {
35
+ includeRequirements: options.requirements !== false,
36
+ includeDesign: options.design !== false,
37
+ includeTasks: options.tasks !== false,
38
+ includeSteering: options.steering === true,
39
+ steeringFiles: options.steeringFiles
40
+ ? options.steeringFiles.split(',').map(f => f.trim())
41
+ : []
42
+ };
43
+
44
+ console.log('Export options:');
45
+ console.log(` Requirements: ${exportOptions.includeRequirements ? chalk.green('✓') : chalk.gray('✗')}`);
46
+ console.log(` Design: ${exportOptions.includeDesign ? chalk.green('✓') : chalk.gray('✗')}`);
47
+ console.log(` Tasks: ${exportOptions.includeTasks ? chalk.green('✓') : chalk.gray('✗')}`);
48
+ console.log(` Steering: ${exportOptions.includeSteering ? chalk.green('✓') : chalk.gray('✗')}`);
49
+
50
+ if (exportOptions.includeSteering && exportOptions.steeringFiles.length > 0) {
51
+ console.log(` Steering files: ${chalk.gray(exportOptions.steeringFiles.join(', '))}`);
52
+ }
53
+
54
+ console.log();
55
+ console.log('Exporting...');
56
+ console.log();
57
+
58
+ const result = await exporter.exportContext(
59
+ projectPath,
60
+ specName,
61
+ exportOptions
62
+ );
63
+
64
+ if (result.success) {
65
+ console.log(chalk.green('✅ Context exported successfully'));
66
+ console.log();
67
+ console.log(`Export file: ${chalk.cyan(result.exportPath)}`);
68
+ console.log(`Sections: ${chalk.gray(result.sections)}`);
69
+ console.log(`Size: ${chalk.gray(formatBytes(result.size))}`);
70
+ console.log();
71
+ console.log('Usage:');
72
+ console.log(' 1. Copy the exported file to your AI coding assistant');
73
+ console.log(' 2. Reference specific sections when working on tasks');
74
+ console.log(' 3. Update task status in the original tasks.md after completion');
75
+ } else {
76
+ console.log(chalk.red('❌ Export failed'));
77
+ console.log();
78
+ console.log(`Error: ${result.error}`);
79
+ }
80
+ } catch (error) {
81
+ console.log(chalk.red('❌ Error:'), error.message);
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Format bytes to human-readable string
87
+ *
88
+ * @param {number} bytes - Bytes
89
+ * @returns {string} Formatted string
90
+ */
91
+ function formatBytes(bytes) {
92
+ if (bytes < 1024) return `${bytes} B`;
93
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
94
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
95
+ }
96
+
97
+ module.exports = {
98
+ exportContext
99
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Prompt Command
3
+ *
4
+ * Generates task-specific prompts for AI coding assistants
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const PromptGenerator = require('../context/prompt-generator');
9
+
10
+ /**
11
+ * Generate task prompt
12
+ *
13
+ * @param {string} specName - Spec name
14
+ * @param {string} taskId - Task ID
15
+ * @param {Object} options - Command options
16
+ * @param {string} options.tool - Target tool (generic, claude-code, cursor, codex, kiro)
17
+ * @param {number} options.maxLength - Maximum context length
18
+ * @returns {Promise<void>}
19
+ */
20
+ async function generatePrompt(specName, taskId, options = {}) {
21
+ const projectPath = process.cwd();
22
+ const generator = new PromptGenerator();
23
+
24
+ console.log(chalk.red('🔥') + ' Generating Task Prompt');
25
+ console.log();
26
+
27
+ try {
28
+ console.log(`Spec: ${chalk.cyan(specName)}`);
29
+ console.log(`Task: ${chalk.cyan(taskId)}`);
30
+
31
+ const targetTool = options.tool || 'generic';
32
+ console.log(`Target Tool: ${chalk.cyan(targetTool)}`);
33
+ console.log();
34
+
35
+ const generateOptions = {
36
+ targetTool,
37
+ maxContextLength: options.maxLength || 10000
38
+ };
39
+
40
+ console.log('Generating prompt...');
41
+ console.log();
42
+
43
+ const result = await generator.generatePrompt(
44
+ projectPath,
45
+ specName,
46
+ taskId,
47
+ generateOptions
48
+ );
49
+
50
+ if (result.success) {
51
+ console.log(chalk.green('✅ Prompt generated successfully'));
52
+ console.log();
53
+ console.log(`Prompt file: ${chalk.cyan(result.promptPath)}`);
54
+ console.log(`Size: ${chalk.gray(formatBytes(result.size))}`);
55
+ console.log();
56
+ console.log('Usage:');
57
+ console.log(' 1. Copy the prompt file content');
58
+ console.log(' 2. Paste it into your AI coding assistant');
59
+ console.log(' 3. Follow the implementation guidelines');
60
+ console.log(' 4. Update task status after completion');
61
+ console.log();
62
+
63
+ // Tool-specific tips
64
+ if (targetTool === 'claude-code') {
65
+ console.log(chalk.blue('💡 Claude Code Tips:'));
66
+ console.log(' • Copy the entire prompt into the chat');
67
+ console.log(' • Reference specific sections as needed');
68
+ } else if (targetTool === 'cursor') {
69
+ console.log(chalk.blue('💡 Cursor Tips:'));
70
+ console.log(' • Paste the prompt into the composer');
71
+ console.log(' • Use Cmd+K to apply changes');
72
+ } else if (targetTool === 'codex') {
73
+ console.log(chalk.blue('💡 GitHub Copilot Tips:'));
74
+ console.log(' • Include the prompt in code comments');
75
+ console.log(' • Let Copilot suggest implementations');
76
+ } else if (targetTool === 'kiro') {
77
+ console.log(chalk.blue('💡 Kiro IDE Tips:'));
78
+ console.log(' • Steering rules are loaded automatically');
79
+ console.log(' • Use #File to reference specific files');
80
+ }
81
+ } else {
82
+ console.log(chalk.red('❌ Prompt generation failed'));
83
+ console.log();
84
+ console.log(`Error: ${result.error}`);
85
+ }
86
+ } catch (error) {
87
+ console.log(chalk.red('❌ Error:'), error.message);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Format bytes to human-readable string
93
+ *
94
+ * @param {number} bytes - Bytes
95
+ * @returns {string} Formatted string
96
+ */
97
+ function formatBytes(bytes) {
98
+ if (bytes < 1024) return `${bytes} B`;
99
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
100
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
101
+ }
102
+
103
+ module.exports = {
104
+ generatePrompt
105
+ };
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Status Command
3
+ *
4
+ * Displays project status including specs, tasks, and team activity
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('path');
9
+ const fs = require('fs-extra');
10
+ const TaskClaimer = require('../task/task-claimer');
11
+ const WorkspaceManager = require('../workspace/workspace-manager');
12
+
13
+ /**
14
+ * Executes the status command
15
+ *
16
+ * @param {Object} options - Command options
17
+ * @param {boolean} options.verbose - Show detailed information
18
+ * @param {boolean} options.team - Show team activity
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function statusCommand(options = {}) {
22
+ const { verbose = false, team = false } = options;
23
+ const projectPath = process.cwd();
24
+
25
+ console.log(chalk.red('🔥') + ' Kiro Spec Engine - Project Status');
26
+ console.log();
27
+
28
+ try {
29
+ // 1. Check if .kiro/ exists
30
+ const kiroPath = path.join(projectPath, '.kiro');
31
+ const kiroExists = await fs.pathExists(kiroPath);
32
+
33
+ if (!kiroExists) {
34
+ console.log(chalk.yellow('⚠️ No .kiro/ directory found'));
35
+ console.log();
36
+ console.log('This project has not been adopted yet.');
37
+ console.log('Run ' + chalk.cyan('kse adopt') + ' to get started.');
38
+ return;
39
+ }
40
+
41
+ // 2. Check multi-user mode
42
+ const workspaceManager = new WorkspaceManager();
43
+ const isMultiUser = await workspaceManager.isMultiUserMode(projectPath);
44
+
45
+ console.log(chalk.blue('📊 Project Information'));
46
+ console.log(` Mode: ${isMultiUser ? chalk.cyan('Multi-User') : chalk.gray('Single-User')}`);
47
+
48
+ if (isMultiUser) {
49
+ const workspaces = await workspaceManager.listWorkspaces(projectPath);
50
+ console.log(` Active Users: ${chalk.cyan(workspaces.length)}`);
51
+ if (verbose) {
52
+ workspaces.forEach(username => {
53
+ console.log(` • ${username}`);
54
+ });
55
+ }
56
+ }
57
+
58
+ console.log();
59
+
60
+ // 3. List specs
61
+ const specsPath = path.join(projectPath, '.kiro/specs');
62
+ const specsExist = await fs.pathExists(specsPath);
63
+
64
+ if (!specsExist) {
65
+ console.log(chalk.yellow('📁 No specs found'));
66
+ console.log();
67
+ console.log('Create your first spec: ' + chalk.cyan('kse create-spec my-feature'));
68
+ return;
69
+ }
70
+
71
+ const entries = await fs.readdir(specsPath, { withFileTypes: true });
72
+ const specDirs = entries.filter(entry =>
73
+ entry.isDirectory() && !entry.name.startsWith('.')
74
+ );
75
+
76
+ if (specDirs.length === 0) {
77
+ console.log(chalk.yellow('📁 No specs found'));
78
+ console.log();
79
+ console.log('Create your first spec: ' + chalk.cyan('kse create-spec my-feature'));
80
+ return;
81
+ }
82
+
83
+ console.log(chalk.blue(`📁 Specs (${specDirs.length})`));
84
+ console.log();
85
+
86
+ // 4. Analyze each spec
87
+ const taskClaimer = new TaskClaimer();
88
+ const allClaimedTasks = [];
89
+
90
+ for (const specDir of specDirs) {
91
+ const specName = specDir.name;
92
+ const specPath = path.join(specsPath, specName);
93
+
94
+ // Check for tasks.md
95
+ const tasksPath = path.join(specPath, 'tasks.md');
96
+ const tasksExist = await fs.pathExists(tasksPath);
97
+
98
+ if (!tasksExist) {
99
+ console.log(chalk.gray(` ${specName}`));
100
+ console.log(chalk.gray(' No tasks.md found'));
101
+ console.log();
102
+ continue;
103
+ }
104
+
105
+ // Parse tasks
106
+ const tasks = await taskClaimer.parseTasks(tasksPath);
107
+ const totalTasks = tasks.length;
108
+ const completedTasks = tasks.filter(t => t.status === 'completed').length;
109
+ const inProgressTasks = tasks.filter(t => t.status === 'in-progress').length;
110
+ const claimedTasks = tasks.filter(t => t.claimedBy);
111
+
112
+ // Calculate completion percentage
113
+ const completionPercent = totalTasks > 0
114
+ ? Math.round((completedTasks / totalTasks) * 100)
115
+ : 0;
116
+
117
+ // Display spec info
118
+ console.log(chalk.cyan(` ${specName}`));
119
+ console.log(` Tasks: ${chalk.green(completedTasks)}/${totalTasks} completed (${completionPercent}%)`);
120
+
121
+ if (inProgressTasks > 0) {
122
+ console.log(` In Progress: ${chalk.yellow(inProgressTasks)}`);
123
+ }
124
+
125
+ if (claimedTasks.length > 0) {
126
+ console.log(` Claimed: ${chalk.blue(claimedTasks.length)}`);
127
+
128
+ if (verbose || team) {
129
+ claimedTasks.forEach(task => {
130
+ const staleMarker = task.isStale ? chalk.red(' [STALE]') : '';
131
+ console.log(` • ${task.taskId} ${task.title}`);
132
+ console.log(` ${chalk.gray(`@${task.claimedBy}, ${new Date(task.claimedAt).toLocaleDateString()}`)}${staleMarker}`);
133
+ });
134
+ }
135
+
136
+ // Collect for team view
137
+ claimedTasks.forEach(task => {
138
+ allClaimedTasks.push({
139
+ ...task,
140
+ specName
141
+ });
142
+ });
143
+ }
144
+
145
+ console.log();
146
+ }
147
+
148
+ // 5. Team activity view
149
+ if (team && allClaimedTasks.length > 0) {
150
+ console.log(chalk.blue('👥 Team Activity'));
151
+ console.log();
152
+
153
+ // Group by user
154
+ const tasksByUser = {};
155
+ allClaimedTasks.forEach(task => {
156
+ if (!tasksByUser[task.claimedBy]) {
157
+ tasksByUser[task.claimedBy] = [];
158
+ }
159
+ tasksByUser[task.claimedBy].push(task);
160
+ });
161
+
162
+ // Display by user
163
+ Object.keys(tasksByUser).sort().forEach(username => {
164
+ const userTasks = tasksByUser[username];
165
+ const staleTasks = userTasks.filter(t => t.isStale);
166
+
167
+ console.log(chalk.cyan(` ${username}`));
168
+ console.log(` Active Tasks: ${userTasks.length}`);
169
+
170
+ if (staleTasks.length > 0) {
171
+ console.log(` ${chalk.red(`Stale Claims: ${staleTasks.length}`)}`);
172
+ }
173
+
174
+ if (verbose) {
175
+ userTasks.forEach(task => {
176
+ const staleMarker = task.isStale ? chalk.red(' [STALE]') : '';
177
+ const statusColor = task.status === 'completed' ? chalk.green :
178
+ task.status === 'in-progress' ? chalk.yellow :
179
+ chalk.gray;
180
+ console.log(` • [${task.specName}] ${task.taskId} ${task.title}`);
181
+ console.log(` ${statusColor(task.status)} • ${chalk.gray(new Date(task.claimedAt).toLocaleDateString())}${staleMarker}`);
182
+ });
183
+ }
184
+
185
+ console.log();
186
+ });
187
+ }
188
+
189
+ // 6. Summary
190
+ if (allClaimedTasks.length > 0) {
191
+ const staleClaims = allClaimedTasks.filter(t => t.isStale);
192
+
193
+ if (staleClaims.length > 0) {
194
+ console.log(chalk.yellow('⚠️ Warning'));
195
+ console.log(` ${staleClaims.length} task(s) have stale claims (>7 days old)`);
196
+ console.log();
197
+
198
+ if (!verbose && !team) {
199
+ console.log(chalk.gray(' Run with --team or --verbose to see details'));
200
+ console.log();
201
+ }
202
+ }
203
+ }
204
+
205
+ // 7. Next steps
206
+ console.log(chalk.blue('💡 Commands'));
207
+ console.log(' View team activity: ' + chalk.cyan('kse status --team'));
208
+ console.log(' Detailed view: ' + chalk.cyan('kse status --verbose'));
209
+
210
+ if (isMultiUser) {
211
+ console.log(' Claim a task: ' + chalk.cyan('kse task claim <spec-name> <task-id>'));
212
+ console.log(' Sync workspace: ' + chalk.cyan('kse workspace sync'));
213
+ }
214
+
215
+ } catch (error) {
216
+ console.log();
217
+ console.log(chalk.red('❌ Error:'), error.message);
218
+ console.log();
219
+ console.log(chalk.gray('If you need help, please report this issue:'));
220
+ console.log(chalk.cyan('https://github.com/heguangyong/kiro-spec-engine/issues'));
221
+ process.exit(1);
222
+ }
223
+ }
224
+
225
+ module.exports = statusCommand;