kspec 1.0.12 → 1.0.14

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +207 -45
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kspec",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Spec-driven development workflow for Kiro CLI",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -17,7 +17,12 @@ const defaultConfig = {
17
17
 
18
18
  function loadConfig() {
19
19
  if (fs.existsSync(CONFIG_FILE)) {
20
- return { ...defaultConfig, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) };
20
+ try {
21
+ return { ...defaultConfig, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) };
22
+ } catch {
23
+ console.error('Warning: Invalid config.json, using defaults');
24
+ return defaultConfig;
25
+ }
21
26
  }
22
27
  return defaultConfig;
23
28
  }
@@ -65,8 +70,9 @@ function slugify(text) {
65
70
  }
66
71
 
67
72
  function detectCli() {
68
- try { execSync('kiro-cli --version', { stdio: 'ignore' }); return 'kiro-cli'; } catch {}
69
- try { execSync('q --version', { stdio: 'ignore' }); return 'q'; } catch {}
73
+ const opts = { stdio: 'ignore', timeout: 5000 };
74
+ try { execSync('kiro-cli --version', opts); return 'kiro-cli'; } catch {}
75
+ try { execSync('q --version', opts); return 'q'; } catch {}
70
76
  return null;
71
77
  }
72
78
 
@@ -100,10 +106,19 @@ async function confirm(question) {
100
106
  }
101
107
 
102
108
  function chat(message, agent) {
109
+ // Refresh context before each chat to ensure LLM has latest state
110
+ refreshContext();
111
+
103
112
  const cli = requireCli();
104
113
  const args = agent ? ['chat', '--agent', agent, message] : ['chat', message];
105
114
  const child = spawn(cli, args, { stdio: 'inherit' });
106
- return new Promise(resolve => child.on('close', resolve));
115
+ return new Promise((resolve, reject) => {
116
+ child.on('error', err => {
117
+ console.error(`Failed to start ${cli}: ${err.message}`);
118
+ reject(err);
119
+ });
120
+ child.on('close', resolve);
121
+ });
107
122
  }
108
123
 
109
124
  // Spec management
@@ -155,13 +170,109 @@ function getOrSelectSpec(name) {
155
170
  function getTaskStats(folder) {
156
171
  const tasksFile = path.join(folder, 'tasks.md');
157
172
  if (!fs.existsSync(tasksFile)) return null;
158
-
173
+
159
174
  const content = fs.readFileSync(tasksFile, 'utf8');
160
- const total = (content.match(/^-\s*\[[ x]\]/gm) || []).length;
161
- const done = (content.match(/^-\s*\[x\]/gim) || []).length;
175
+ // Match both [x] and [X] consistently for total and done counts
176
+ const total = (content.match(/^-\s*\[[ xX]\]/gm) || []).length;
177
+ const done = (content.match(/^-\s*\[[xX]\]/gm) || []).length;
162
178
  return { total, done, remaining: total - done };
163
179
  }
164
180
 
181
+ function getCurrentTask(folder) {
182
+ const tasksFile = path.join(folder, 'tasks.md');
183
+ if (!fs.existsSync(tasksFile)) return null;
184
+
185
+ const content = fs.readFileSync(tasksFile, 'utf8');
186
+ const lines = content.split('\n');
187
+ for (const line of lines) {
188
+ if (/^-\s*\[ \]/.test(line)) {
189
+ return line.replace(/^-\s*\[ \]\s*/, '').trim();
190
+ }
191
+ }
192
+ return null;
193
+ }
194
+
195
+ function refreshContext() {
196
+ const contextFile = path.join(KSPEC_DIR, 'CONTEXT.md');
197
+ const current = getCurrentSpec();
198
+
199
+ if (!current) {
200
+ // No current spec - create minimal context
201
+ const content = `# kspec Context
202
+
203
+ No active spec. Run: \`kspec spec "Feature Name"\`
204
+ `;
205
+ ensureDir(KSPEC_DIR);
206
+ fs.writeFileSync(contextFile, content);
207
+ return content;
208
+ }
209
+
210
+ const specName = path.basename(current);
211
+ const stats = getTaskStats(current);
212
+ const currentTask = getCurrentTask(current);
213
+
214
+ // Read spec-lite if exists
215
+ const specLiteFile = path.join(current, 'spec-lite.md');
216
+ let specLite = '';
217
+ if (fs.existsSync(specLiteFile)) {
218
+ specLite = fs.readFileSync(specLiteFile, 'utf8');
219
+ }
220
+
221
+ // Read memory if exists
222
+ const memoryFile = path.join(current, 'memory.md');
223
+ let memory = '';
224
+ if (fs.existsSync(memoryFile)) {
225
+ memory = fs.readFileSync(memoryFile, 'utf8');
226
+ }
227
+
228
+ // Build context
229
+ let content = `# kspec Context
230
+ > Auto-generated. Always read this first after context compression.
231
+
232
+ ## Current Spec
233
+ **${specName}**
234
+ Path: \`${current}\`
235
+
236
+ `;
237
+
238
+ if (stats) {
239
+ content += `## Progress
240
+ - Tasks: ${stats.done}/${stats.total} completed
241
+ - Remaining: ${stats.remaining}
242
+ `;
243
+ if (currentTask) {
244
+ content += `- **Current Task**: ${currentTask}
245
+ `;
246
+ }
247
+ content += '\n';
248
+ }
249
+
250
+ if (specLite) {
251
+ content += `## Requirements Summary
252
+ ${specLite}
253
+
254
+ `;
255
+ }
256
+
257
+ if (memory) {
258
+ content += `## Decisions & Learnings
259
+ ${memory}
260
+
261
+ `;
262
+ }
263
+
264
+ content += `## Quick Commands
265
+ - \`kspec build\` - Continue building tasks
266
+ - \`kspec verify\` - Verify implementation
267
+ - \`kspec status\` - Show current status
268
+ - \`kspec context\` - Refresh this file
269
+ `;
270
+
271
+ ensureDir(KSPEC_DIR);
272
+ fs.writeFileSync(contextFile, content);
273
+ return content;
274
+ }
275
+
165
276
  // Templates
166
277
  const steeringTemplates = {
167
278
  'product.md': `# Product Overview
@@ -208,10 +319,15 @@ const agentTemplates = {
208
319
  tools: ['read', 'write'],
209
320
  allowedTools: ['read', 'write'],
210
321
  resources: [
322
+ 'file://.kspec/CONTEXT.md',
211
323
  'file://.kiro/steering/**/*.md',
212
324
  'file://.kspec/**/*.md'
213
325
  ],
214
- prompt: `You are the kspec analyser. Your job:
326
+ prompt: `You are the kspec analyser.
327
+
328
+ FIRST: Read .kspec/CONTEXT.md for current state and spec summary.
329
+
330
+ Your job:
215
331
  1. Analyse the codebase structure, tech stack, patterns
216
332
  2. Review .kiro/steering/ docs
217
333
  3. Suggest updates to steering based on actual codebase
@@ -229,25 +345,31 @@ Output a clear analysis report. Propose specific steering doc updates.`,
229
345
  tools: ['read', 'write'],
230
346
  allowedTools: ['read', 'write'],
231
347
  resources: [
348
+ 'file://.kspec/CONTEXT.md',
232
349
  'file://.kiro/steering/**/*.md',
233
350
  'file://.kspec/**/*.md'
234
351
  ],
235
- prompt: `You are the kspec specification writer. Your job:
352
+ prompt: `You are the kspec specification writer.
353
+
354
+ WORKFLOW (do this autonomously):
236
355
  1. Read .kiro/steering/ for project context
237
- 2. Create a comprehensive spec.md with:
356
+ 2. Create spec folder: .kspec/specs/YYYY-MM-DD-{feature-slug}/
357
+ - Use today's date and a short slug (2-4 words from feature name)
358
+ 3. Create spec.md in that folder with:
238
359
  - Problem/Context
239
360
  - Requirements (functional + non-functional)
240
361
  - Constraints
241
362
  - High-level design
242
363
  - Acceptance criteria
243
- 3. IMMEDIATELY after spec.md, create spec-lite.md:
244
- - Concise version (under 500 words)
364
+ 4. Create spec-lite.md (CRITICAL - under 500 words):
365
+ - Concise version for context retention after compression
245
366
  - Key requirements only
246
- - Used for context after compression
367
+ 5. Update .kspec/.current with the spec folder path
368
+ 6. Update .kspec/CONTEXT.md with current state
247
369
 
248
- Always create both files. spec-lite.md is critical for context retention.`,
370
+ After completion, suggest: "Switch to kspec-tasks agent to generate implementation tasks"`,
249
371
  keyboardShortcut: 'ctrl+s',
250
- welcomeMessage: 'Ready to create specification.'
372
+ welcomeMessage: 'Ready to create specification. Describe your feature.'
251
373
  },
252
374
 
253
375
  'kspec-tasks.json': {
@@ -257,21 +379,29 @@ Always create both files. spec-lite.md is critical for context retention.`,
257
379
  tools: ['read', 'write'],
258
380
  allowedTools: ['read', 'write'],
259
381
  resources: [
382
+ 'file://.kspec/CONTEXT.md',
260
383
  'file://.kiro/steering/**/*.md',
261
384
  'file://.kspec/**/*.md'
262
385
  ],
263
- prompt: `You are the kspec task generator. Your job:
264
- 1. Read spec.md and spec-lite.md from the spec folder
265
- 2. Generate tasks.md with:
386
+ prompt: `You are the kspec task generator.
387
+
388
+ WORKFLOW:
389
+ 1. Read .kspec/CONTEXT.md for current spec location
390
+ 2. Read .kspec/.current to get spec folder path
391
+ 3. Read spec.md and spec-lite.md from that folder
392
+ 4. Generate tasks.md in the spec folder with:
266
393
  - Checkbox format: "- [ ] Task description"
267
394
  - TDD approach: test first, then implement
268
395
  - Logical ordering (models → services → API → UI)
269
396
  - Dependencies noted
270
397
  - File paths where changes occur
398
+ 5. Update .kspec/CONTEXT.md with task count
399
+
400
+ Tasks must be atomic and independently verifiable.
271
401
 
272
- Tasks must be atomic and independently verifiable.`,
402
+ After completion, suggest: "Switch to kspec-build agent to start implementing tasks"`,
273
403
  keyboardShortcut: 'ctrl+t',
274
- welcomeMessage: 'Generating tasks from spec...'
404
+ welcomeMessage: 'Reading current spec and generating tasks...'
275
405
  },
276
406
 
277
407
  'kspec-build.json': {
@@ -281,24 +411,34 @@ Tasks must be atomic and independently verifiable.`,
281
411
  tools: ['read', 'write', 'shell'],
282
412
  allowedTools: ['read', 'write', 'shell'],
283
413
  resources: [
414
+ 'file://.kspec/CONTEXT.md',
284
415
  'file://.kiro/steering/**/*.md',
285
416
  'file://.kspec/**/*.md'
286
417
  ],
287
- prompt: `You are the kspec builder. Your job:
288
- 1. Read tasks.md, find first uncompleted task (- [ ])
289
- 2. For each task:
418
+ prompt: `You are the kspec builder.
419
+
420
+ WORKFLOW:
421
+ 1. Read .kspec/CONTEXT.md for current spec and task progress
422
+ 2. Read .kspec/.current to get spec folder path
423
+ 3. Read tasks.md from spec folder, find first uncompleted task (- [ ])
424
+ 4. For each task:
290
425
  a) Write test first (TDD)
291
426
  b) Implement minimal code to pass
292
427
  c) Run tests
293
428
  d) Mark task complete: change "- [ ]" to "- [x]"
294
429
  e) Update tasks.md file
295
- 3. Commit after each task
430
+ f) Update .kspec/CONTEXT.md with new progress
431
+ 5. Commit after each task with descriptive message
432
+
433
+ CRITICAL:
434
+ - Always update tasks.md after completing each task
435
+ - Update .kspec/CONTEXT.md with current task and progress
436
+ - NEVER delete .kiro or .kspec folders
437
+ - Use non-interactive flags for commands (--yes, -y)
296
438
 
297
- CRITICAL: Always update tasks.md after completing each task.
298
- NEVER delete .kiro or .kspec folders.
299
- Use non-interactive flags for commands (--yes, -y).`,
439
+ When all tasks complete, suggest: "Switch to kspec-verify agent to verify implementation"`,
300
440
  keyboardShortcut: 'ctrl+b',
301
- welcomeMessage: 'Building from tasks...'
441
+ welcomeMessage: 'Reading current task and building...'
302
442
  },
303
443
 
304
444
  'kspec-verify.json': {
@@ -308,10 +448,15 @@ Use non-interactive flags for commands (--yes, -y).`,
308
448
  tools: ['read', 'shell'],
309
449
  allowedTools: ['read', 'shell'],
310
450
  resources: [
451
+ 'file://.kspec/CONTEXT.md',
311
452
  'file://.kiro/steering/**/*.md',
312
453
  'file://.kspec/**/*.md'
313
454
  ],
314
- prompt: `You are the kspec verifier. Based on what you're asked to verify:
455
+ prompt: `You are the kspec verifier.
456
+
457
+ FIRST: Read .kspec/CONTEXT.md for current spec and progress.
458
+
459
+ Based on what you're asked to verify:
315
460
 
316
461
  VERIFY-SPEC:
317
462
  - Check spec covers all requirements
@@ -343,10 +488,15 @@ Output a clear verification report with pass/fail status.`,
343
488
  tools: ['read', 'shell'],
344
489
  allowedTools: ['read', 'shell'],
345
490
  resources: [
491
+ 'file://.kspec/CONTEXT.md',
346
492
  'file://.kiro/steering/**/*.md',
347
493
  'file://.kspec/**/*.md'
348
494
  ],
349
- prompt: `You are the kspec code reviewer. Your job:
495
+ prompt: `You are the kspec code reviewer.
496
+
497
+ FIRST: Read .kspec/CONTEXT.md for current spec context.
498
+
499
+ Your job:
350
500
  1. Review code changes (git diff or specified files)
351
501
  2. Check compliance with .kiro/steering/
352
502
  3. Evaluate:
@@ -402,11 +552,13 @@ const commands = {
402
552
  }
403
553
  }
404
554
 
405
- // Create agents
555
+ // Create agents (skip if already exist to preserve customizations)
406
556
  for (const [file, content] of Object.entries(agentTemplates)) {
407
557
  const p = path.join(AGENTS_DIR, file);
408
- fs.writeFileSync(p, JSON.stringify(content, null, 2));
409
- log(`Created ${p}`);
558
+ if (!fs.existsSync(p)) {
559
+ fs.writeFileSync(p, JSON.stringify(content, null, 2));
560
+ log(`Created ${p}`);
561
+ }
410
562
  }
411
563
 
412
564
  console.log('\n✅ kspec initialized!\n');
@@ -617,13 +769,13 @@ Output: APPROVE or REQUEST_CHANGES with specifics.`, 'kspec-review');
617
769
 
618
770
  status() {
619
771
  const current = getCurrentSpec();
620
-
772
+
621
773
  console.log('\nkspec Status\n');
622
774
  console.log(`CLI: ${detectCli() || '(not installed)'}`);
623
775
  console.log(`Initialized: ${config.initialized ? 'yes' : 'no'}`);
624
776
  console.log(`Date format: ${config.dateFormat || 'YYYY-MM-DD'}`);
625
777
  console.log(`Auto-execute: ${config.autoExecute || 'ask'}`);
626
-
778
+
627
779
  if (current) {
628
780
  console.log(`\nCurrent spec: ${path.basename(current)}`);
629
781
  const stats = getTaskStats(current);
@@ -643,6 +795,12 @@ Output: APPROVE or REQUEST_CHANGES with specifics.`, 'kspec-review');
643
795
  console.log('');
644
796
  },
645
797
 
798
+ context() {
799
+ const content = refreshContext();
800
+ console.log(content);
801
+ console.log(`Context saved to: .kspec/CONTEXT.md\n`);
802
+ },
803
+
646
804
  agents() {
647
805
  console.log(`
648
806
  kspec Agents
@@ -664,7 +822,7 @@ Switch: /agent swap or use keyboard shortcuts
664
822
  console.log(`
665
823
  kspec - Spec-driven development for Kiro CLI
666
824
 
667
- Workflow:
825
+ CLI Workflow (outside kiro-cli):
668
826
  kspec init Interactive setup
669
827
  kspec analyse Analyse codebase, update steering
670
828
  kspec spec "Feature" Create specification
@@ -675,22 +833,26 @@ Workflow:
675
833
  kspec verify Verify implementation
676
834
  kspec done Complete spec, harvest memory
677
835
 
836
+ Inside kiro-cli (recommended):
837
+ /agent swap kspec-spec → Describe feature → creates spec
838
+ /agent swap kspec-tasks → Generates tasks from spec
839
+ /agent swap kspec-build → Builds tasks with TDD
840
+ /agent swap kspec-verify → Verifies implementation
841
+
842
+ Agents read .kspec/CONTEXT.md automatically for state.
843
+
678
844
  Other:
845
+ kspec context Refresh/view context file
679
846
  kspec review [target] Code review
680
847
  kspec list List all specs
681
848
  kspec status Current status
682
849
  kspec agents List agents
683
850
  kspec help Show this help
684
- kspec --help, -h Show this help
685
- kspec --version, -v Show version
686
851
 
687
852
  Examples:
688
- kspec init
689
- kspec spec "User Authentication"
690
- kspec tasks
691
- kspec build
692
- kspec verify
693
- kspec done
853
+ kspec init # First time setup
854
+ kspec spec "User Auth" # CLI mode
855
+ kiro-cli --agent kspec-spec # Direct agent mode
694
856
  `);
695
857
  }
696
858
  };
@@ -717,4 +879,4 @@ async function run(args) {
717
879
  }
718
880
  }
719
881
 
720
- module.exports = { run, commands, loadConfig, detectCli, requireCli, agentTemplates };
882
+ module.exports = { run, commands, loadConfig, detectCli, requireCli, agentTemplates, getTaskStats, refreshContext, getCurrentTask };