kspec 1.0.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.
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # kspec — Spec-Driven Development for Kiro CLI
2
+
3
+ [![npm version](https://img.shields.io/npm/v/kspec-cli.svg)](https://www.npmjs.com/package/kspec-cli)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Spec-driven development workflow for Kiro CLI with verification at every step.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install -g kspec-cli
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ kspec init # Interactive setup
18
+ kspec analyse # Analyse codebase
19
+ kspec spec "User Auth API" # Create specification
20
+ kspec verify-spec # Verify spec is complete
21
+ kspec tasks # Generate tasks
22
+ kspec verify-tasks # Verify tasks cover spec
23
+ kspec build # Execute with TDD
24
+ kspec verify # Verify implementation
25
+ kspec done # Complete & harvest memory
26
+ ```
27
+
28
+ ## Workflow
29
+
30
+ ```
31
+ init → analyse → spec → verify-spec → tasks → verify-tasks → build → verify → done
32
+ ```
33
+
34
+ ## Commands
35
+
36
+ | Command | Description |
37
+ |---------|-------------|
38
+ | `kspec init` | Interactive setup (date format, execution mode) |
39
+ | `kspec analyse` | Analyse codebase, update steering docs |
40
+ | `kspec spec "Name"` | Create spec.md + spec-lite.md |
41
+ | `kspec verify-spec` | Verify spec covers requirements |
42
+ | `kspec tasks` | Generate tasks.md from spec |
43
+ | `kspec verify-tasks` | Verify tasks cover spec |
44
+ | `kspec build` | Execute tasks with TDD |
45
+ | `kspec verify` | Verify implementation matches spec |
46
+ | `kspec done` | Complete spec, harvest memory |
47
+ | `kspec review` | Code review |
48
+ | `kspec list` | List all specs |
49
+ | `kspec status` | Current status |
50
+
51
+ ## Agents & Shortcuts
52
+
53
+ | Agent | Shortcut | Purpose |
54
+ |-------|----------|---------|
55
+ | kspec-analyse | Ctrl+A | Analyse codebase |
56
+ | kspec-spec | Ctrl+S | Create specifications |
57
+ | kspec-tasks | Ctrl+T | Generate tasks |
58
+ | kspec-build | Ctrl+B | Execute with TDD |
59
+ | kspec-verify | Ctrl+V | Verify spec/tasks/impl |
60
+ | kspec-review | Ctrl+R | Code review |
61
+
62
+ ## Structure
63
+
64
+ ```
65
+ .kspec/
66
+ ├── config.json # User preferences
67
+ ├── memory.md # Project learnings
68
+ └── specs/
69
+ └── 2026-01-22-feature/
70
+ ├── spec.md # Full specification
71
+ ├── spec-lite.md # Concise (for context compression)
72
+ ├── tasks.md # Implementation tasks
73
+ └── memory.md # Feature learnings
74
+
75
+ .kiro/
76
+ └── steering/ # Project rules (Kiro native)
77
+ ```
78
+
79
+ ## Configuration
80
+
81
+ Set during `kspec init`:
82
+
83
+ - **Date format**: YYYY-MM-DD, DD-MM-YYYY, or MM-DD-YYYY
84
+ - **Auto-execute**: ask (default), auto, or dry-run
85
+
86
+ ## Requirements
87
+
88
+ - Node.js >= 18
89
+ - Kiro CLI or Amazon Q CLI
90
+
91
+ ## License
92
+
93
+ MIT
package/bin/kspec.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ const { run } = require('../src/index.js');
3
+ run(process.argv.slice(2)).catch(e => { console.error(e.message); process.exit(1); });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "kspec",
3
+ "version": "1.0.0",
4
+ "description": "Spec-driven development workflow for Kiro CLI",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "kspec": "bin/kspec.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node --test",
11
+ "lint": "eslint src bin"
12
+ },
13
+ "keywords": [
14
+ "kiro",
15
+ "cli",
16
+ "spec-driven",
17
+ "tdd",
18
+ "development",
19
+ "workflow",
20
+ "ai"
21
+ ],
22
+ "author": "Sanjeev Kumar Badrinath",
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/sanjeevkumarraob/kspec.git"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "files": [
32
+ "bin",
33
+ "src",
34
+ "templates"
35
+ ]
36
+ }
package/src/index.js ADDED
@@ -0,0 +1,677 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execSync, spawn } = require('child_process');
4
+ const readline = require('readline');
5
+
6
+ const KSPEC_DIR = '.kspec';
7
+ const STEERING_DIR = '.kiro/steering';
8
+ const AGENTS_DIR = '.kiro/agents';
9
+ const CONFIG_FILE = path.join(KSPEC_DIR, 'config.json');
10
+
11
+ // Default config
12
+ const defaultConfig = {
13
+ dateFormat: 'YYYY-MM-DD',
14
+ autoExecute: 'ask',
15
+ initialized: false
16
+ };
17
+
18
+ function loadConfig() {
19
+ if (fs.existsSync(CONFIG_FILE)) {
20
+ return { ...defaultConfig, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) };
21
+ }
22
+ return defaultConfig;
23
+ }
24
+
25
+ function saveConfig(cfg) {
26
+ ensureDir(KSPEC_DIR);
27
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2));
28
+ }
29
+
30
+ const config = loadConfig();
31
+
32
+ // Helpers
33
+ function log(msg) { console.log(`[kspec] ${msg}`); }
34
+ function die(msg) { console.error(`Error: ${msg}`); process.exit(1); }
35
+ function ensureDir(dir) { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); }
36
+
37
+ function formatDate(format) {
38
+ const d = new Date();
39
+ const pad = n => n.toString().padStart(2, '0');
40
+ const parts = { YYYY: d.getFullYear(), MM: pad(d.getMonth() + 1), DD: pad(d.getDate()) };
41
+ return format.replace(/YYYY|MM|DD/g, m => parts[m]);
42
+ }
43
+
44
+ function slugify(text) {
45
+ return text.slice(0, 50).toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '').replace(/^-+|-+$/g, '');
46
+ }
47
+
48
+ function detectCli() {
49
+ try { execSync('kiro-cli --version', { stdio: 'ignore' }); return 'kiro-cli'; } catch {}
50
+ try { execSync('q --version', { stdio: 'ignore' }); return 'q'; } catch {}
51
+ return null;
52
+ }
53
+
54
+ function requireCli() {
55
+ const cli = requireCli();
56
+ if (!cli) die("Neither 'kiro-cli' nor 'q' found. Install Kiro CLI first.");
57
+ return cli;
58
+ }
59
+
60
+ async function prompt(question, choices) {
61
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
62
+
63
+ return new Promise(resolve => {
64
+ if (choices) {
65
+ console.log(`\n${question}`);
66
+ choices.forEach((c, i) => console.log(` ${i + 1}) ${c.label}`));
67
+ rl.question('\nChoice: ', answer => {
68
+ rl.close();
69
+ const idx = parseInt(answer) - 1;
70
+ resolve(choices[idx]?.value || choices[0].value);
71
+ });
72
+ } else {
73
+ rl.question(question, answer => { rl.close(); resolve(answer); });
74
+ }
75
+ });
76
+ }
77
+
78
+ async function confirm(question) {
79
+ const answer = await prompt(`${question} (Y/n): `);
80
+ return !answer || answer.toLowerCase() === 'y';
81
+ }
82
+
83
+ function chat(message, agent) {
84
+ const cli = requireCli();
85
+ const args = agent ? ['chat', '--agent', agent, message] : ['chat', message];
86
+ const child = spawn(cli, args, { stdio: 'inherit', shell: true });
87
+ return new Promise(resolve => child.on('close', resolve));
88
+ }
89
+
90
+ // Spec management
91
+ function getSpecsDir() { return path.join(KSPEC_DIR, 'specs'); }
92
+
93
+ function getCurrentSpec() {
94
+ const file = path.join(KSPEC_DIR, '.current');
95
+ if (fs.existsSync(file)) {
96
+ const spec = fs.readFileSync(file, 'utf8').trim();
97
+ if (fs.existsSync(spec)) return spec;
98
+ }
99
+ return null;
100
+ }
101
+
102
+ function setCurrentSpec(folder) {
103
+ ensureDir(KSPEC_DIR);
104
+ fs.writeFileSync(path.join(KSPEC_DIR, '.current'), folder);
105
+ }
106
+
107
+ function findSpec(name) {
108
+ const specsDir = getSpecsDir();
109
+ if (!fs.existsSync(specsDir)) return null;
110
+
111
+ const slug = slugify(name);
112
+ const matches = fs.readdirSync(specsDir)
113
+ .filter(d => d.includes(slug) && fs.statSync(path.join(specsDir, d)).isDirectory())
114
+ .sort().reverse();
115
+
116
+ if (matches.length === 1) return path.join(specsDir, matches[0]);
117
+ if (matches.length > 1) {
118
+ console.log('Multiple matches:');
119
+ matches.forEach((m, i) => console.log(` ${i + 1}) ${m}`));
120
+ die('Be more specific.');
121
+ }
122
+ return null;
123
+ }
124
+
125
+ function getOrSelectSpec(name) {
126
+ if (name) {
127
+ const found = findSpec(name);
128
+ if (found) return found;
129
+ die(`Spec "${name}" not found.`);
130
+ }
131
+ const current = getCurrentSpec();
132
+ if (current) return current;
133
+ die('No current spec. Run: kspec spec "Feature Name"');
134
+ }
135
+
136
+ function getTaskStats(folder) {
137
+ const tasksFile = path.join(folder, 'tasks.md');
138
+ if (!fs.existsSync(tasksFile)) return null;
139
+
140
+ const content = fs.readFileSync(tasksFile, 'utf8');
141
+ const total = (content.match(/^-\s*\[[ x]\]/gm) || []).length;
142
+ const done = (content.match(/^-\s*\[x\]/gim) || []).length;
143
+ return { total, done, remaining: total - done };
144
+ }
145
+
146
+ // Templates
147
+ const steeringTemplates = {
148
+ 'product.md': `# Product Overview
149
+
150
+ ## Purpose
151
+ [Define your product's purpose and target users]
152
+
153
+ ## Key Features
154
+ [List main features and capabilities]
155
+
156
+ ## Success Metrics
157
+ [How success is measured]`,
158
+
159
+ 'tech.md': `# Technology Stack
160
+
161
+ ## Languages & Runtime
162
+ [Primary language and version]
163
+
164
+ ## Frameworks
165
+ [Web framework, testing framework, key libraries]
166
+
167
+ ## Tools
168
+ [Build tools, package managers, linters]`,
169
+
170
+ 'testing.md': `# Testing Standards
171
+
172
+ ## Approach
173
+ TDD: Red → Green → Refactor
174
+
175
+ ## Test Types
176
+ - Unit: Individual functions
177
+ - Integration: Component interactions
178
+ - E2E: User flows
179
+
180
+ ## Coverage
181
+ [Minimum thresholds]`
182
+ };
183
+
184
+ const agentTemplates = {
185
+ 'kspec-analyse.json': {
186
+ name: 'kspec-analyse',
187
+ description: 'Analyse codebase and update steering docs',
188
+ prompt: `You are the kspec analyser. Your job:
189
+ 1. Analyse the codebase structure, tech stack, patterns
190
+ 2. Review .kiro/steering/ docs
191
+ 3. Suggest updates to steering based on actual codebase
192
+ 4. Identify risks, tech debt, improvement areas
193
+
194
+ Output a clear analysis report. Propose specific steering doc updates.`,
195
+ allowedTools: ['read', 'write'],
196
+ keyboardShortcut: 'ctrl+a',
197
+ welcomeMessage: 'Analysing codebase...',
198
+ toolsSettings: {
199
+ read: { allowedPaths: ['./**'] },
200
+ write: { allowedPaths: ['.kiro/steering/**'] }
201
+ }
202
+ },
203
+
204
+ 'kspec-spec.json': {
205
+ name: 'kspec-spec',
206
+ description: 'Create feature specifications',
207
+ prompt: `You are the kspec specification writer. Your job:
208
+ 1. Read .kiro/steering/ for project context
209
+ 2. Create a comprehensive spec.md with:
210
+ - Problem/Context
211
+ - Requirements (functional + non-functional)
212
+ - Constraints
213
+ - High-level design
214
+ - Acceptance criteria
215
+ 3. IMMEDIATELY after spec.md, create spec-lite.md:
216
+ - Concise version (under 500 words)
217
+ - Key requirements only
218
+ - Used for context after compression
219
+
220
+ Always create both files. spec-lite.md is critical for context retention.`,
221
+ allowedTools: ['read', 'write'],
222
+ keyboardShortcut: 'ctrl+s',
223
+ welcomeMessage: 'Ready to create specification.',
224
+ toolsSettings: {
225
+ read: { allowedPaths: ['./**'] },
226
+ write: { allowedPaths: ['.kspec/specs/**'] }
227
+ }
228
+ },
229
+
230
+ 'kspec-tasks.json': {
231
+ name: 'kspec-tasks',
232
+ description: 'Generate implementation tasks from spec',
233
+ prompt: `You are the kspec task generator. Your job:
234
+ 1. Read spec.md and spec-lite.md from the spec folder
235
+ 2. Generate tasks.md with:
236
+ - Checkbox format: "- [ ] Task description"
237
+ - TDD approach: test first, then implement
238
+ - Logical ordering (models → services → API → UI)
239
+ - Dependencies noted
240
+ - File paths where changes occur
241
+
242
+ Tasks must be atomic and independently verifiable.`,
243
+ allowedTools: ['read', 'write'],
244
+ keyboardShortcut: 'ctrl+t',
245
+ welcomeMessage: 'Generating tasks from spec...',
246
+ toolsSettings: {
247
+ read: { allowedPaths: ['./**'] },
248
+ write: { allowedPaths: ['.kspec/specs/**'] }
249
+ }
250
+ },
251
+
252
+ 'kspec-build.json': {
253
+ name: 'kspec-build',
254
+ description: 'Execute tasks with TDD',
255
+ prompt: `You are the kspec builder. Your job:
256
+ 1. Read tasks.md, find first uncompleted task (- [ ])
257
+ 2. For each task:
258
+ a) Write test first (TDD)
259
+ b) Implement minimal code to pass
260
+ c) Run tests
261
+ d) Mark task complete: change "- [ ]" to "- [x]"
262
+ e) Update tasks.md file
263
+ 3. Commit after each task
264
+
265
+ CRITICAL: Always update tasks.md after completing each task.
266
+ NEVER delete .kiro or .kspec folders.
267
+ Use non-interactive flags for commands (--yes, -y).`,
268
+ allowedTools: ['read', 'write', 'shell'],
269
+ keyboardShortcut: 'ctrl+b',
270
+ welcomeMessage: 'Building from tasks...',
271
+ toolsSettings: {
272
+ read: { allowedPaths: ['./**'] },
273
+ write: { allowedPaths: ['./**'] },
274
+ shell: { autoAllowReadonly: true }
275
+ }
276
+ },
277
+
278
+ 'kspec-verify.json': {
279
+ name: 'kspec-verify',
280
+ description: 'Verify spec, tasks, or implementation',
281
+ prompt: `You are the kspec verifier. Based on what you're asked to verify:
282
+
283
+ VERIFY-SPEC:
284
+ - Check spec covers all requirements
285
+ - Identify gaps or ambiguities
286
+ - Suggest splitting large requirements
287
+ - Confirm implementability with current codebase
288
+
289
+ VERIFY-TASKS:
290
+ - Check tasks cover all spec requirements
291
+ - Verify task completion status
292
+ - Check test coverage for completed tasks
293
+ - Report: X/Y tasks done, coverage %
294
+
295
+ VERIFY-IMPLEMENTATION:
296
+ - Check implementation matches spec requirements
297
+ - Check all tasks marked complete
298
+ - Run tests, report results
299
+ - List any gaps between spec and implementation
300
+
301
+ Output a clear verification report with pass/fail status.`,
302
+ allowedTools: ['read', 'shell'],
303
+ keyboardShortcut: 'ctrl+v',
304
+ welcomeMessage: 'What should I verify?',
305
+ toolsSettings: {
306
+ read: { allowedPaths: ['./**'] },
307
+ shell: { autoAllowReadonly: true }
308
+ }
309
+ },
310
+
311
+ 'kspec-review.json': {
312
+ name: 'kspec-review',
313
+ description: 'Code review',
314
+ prompt: `You are the kspec code reviewer. Your job:
315
+ 1. Review code changes (git diff or specified files)
316
+ 2. Check compliance with .kiro/steering/
317
+ 3. Evaluate:
318
+ - Code quality and readability
319
+ - Test coverage
320
+ - Security concerns
321
+ - Performance implications
322
+ 4. Provide actionable feedback
323
+
324
+ Output: APPROVE / REQUEST_CHANGES with specific issues.`,
325
+ allowedTools: ['read', 'shell'],
326
+ keyboardShortcut: 'ctrl+r',
327
+ welcomeMessage: 'Ready to review. What should I look at?',
328
+ toolsSettings: {
329
+ read: { allowedPaths: ['./**'] },
330
+ shell: { allowedCommands: ['git diff*', 'git log*', 'git status*', 'git show*'], autoAllowReadonly: true }
331
+ }
332
+ }
333
+ };
334
+
335
+ // Commands
336
+ const commands = {
337
+ async init() {
338
+ console.log('\n🚀 Welcome to kspec!\n');
339
+
340
+ const dateFormat = await prompt('Date format for spec folders:', [
341
+ { label: 'YYYY-MM-DD (2026-01-22) - sorts chronologically', value: 'YYYY-MM-DD' },
342
+ { label: 'DD-MM-YYYY (22-01-2026)', value: 'DD-MM-YYYY' },
343
+ { label: 'MM-DD-YYYY (01-22-2026)', value: 'MM-DD-YYYY' }
344
+ ]);
345
+
346
+ const autoExecute = await prompt('Command execution during build:', [
347
+ { label: 'Ask for permission (recommended)', value: 'ask' },
348
+ { label: 'Auto-execute (faster)', value: 'auto' },
349
+ { label: 'Dry-run only (show, don\'t run)', value: 'dry' }
350
+ ]);
351
+
352
+ const createSteering = await confirm('Create steering doc templates?');
353
+
354
+ // Save config
355
+ const cfg = { dateFormat, autoExecute, initialized: true };
356
+ saveConfig(cfg);
357
+ Object.assign(config, cfg);
358
+
359
+ // Create directories
360
+ ensureDir(path.join(KSPEC_DIR, 'specs'));
361
+ ensureDir(AGENTS_DIR);
362
+
363
+ // Create steering templates
364
+ if (createSteering) {
365
+ ensureDir(STEERING_DIR);
366
+ for (const [file, content] of Object.entries(steeringTemplates)) {
367
+ const p = path.join(STEERING_DIR, file);
368
+ if (!fs.existsSync(p)) {
369
+ fs.writeFileSync(p, content);
370
+ log(`Created ${p}`);
371
+ }
372
+ }
373
+ }
374
+
375
+ // Create agents
376
+ for (const [file, content] of Object.entries(agentTemplates)) {
377
+ const p = path.join(AGENTS_DIR, file);
378
+ fs.writeFileSync(p, JSON.stringify(content, null, 2));
379
+ log(`Created ${p}`);
380
+ }
381
+
382
+ console.log('\n✅ kspec initialized!\n');
383
+ console.log('Next: kspec analyse');
384
+ },
385
+
386
+ async analyse() {
387
+ log('Analysing codebase...');
388
+ await chat(`Analyse this codebase:
389
+ 1. Identify tech stack, architecture, patterns
390
+ 2. Review existing .kiro/steering/ docs (if any)
391
+ 3. Suggest updates to steering docs
392
+ 4. Identify risks and improvement areas
393
+
394
+ Update steering docs as needed.`, 'kspec-analyse');
395
+ },
396
+
397
+ async spec(args) {
398
+ const feature = args.join(' ');
399
+ if (!feature) die('Usage: kspec spec "Feature Name"');
400
+
401
+ const date = formatDate(config.dateFormat || 'YYYY-MM-DD');
402
+ const folder = path.join(getSpecsDir(), `${date}-${slugify(feature)}`);
403
+ ensureDir(folder);
404
+ setCurrentSpec(folder);
405
+
406
+ log(`Spec folder: ${folder}`);
407
+ await chat(`Create specification for: ${feature}
408
+
409
+ Folder: ${folder}
410
+
411
+ 1. Read .kiro/steering/ for context
412
+ 2. Create ${folder}/spec.md with full specification
413
+ 3. IMMEDIATELY create ${folder}/spec-lite.md (concise version, <500 words)
414
+
415
+ spec-lite.md is critical - it's loaded after context compression.`, 'kspec-spec');
416
+ },
417
+
418
+ async 'verify-spec'(args) {
419
+ const folder = getOrSelectSpec(args.join(' '));
420
+ log(`Verifying spec: ${folder}`);
421
+
422
+ await chat(`Verify the specification in ${folder}/spec.md:
423
+
424
+ 1. Does it cover all requirements clearly?
425
+ 2. Are there gaps or ambiguities?
426
+ 3. Should large requirements be split into smaller chunks?
427
+ 4. Is it implementable with the current codebase?
428
+
429
+ Read the codebase to check implementability.
430
+ Report: PASS/FAIL with specific issues.`, 'kspec-verify');
431
+ },
432
+
433
+ async tasks(args) {
434
+ const folder = getOrSelectSpec(args.join(' '));
435
+ log(`Generating tasks: ${folder}`);
436
+
437
+ await chat(`Generate tasks from specification.
438
+
439
+ Spec folder: ${folder}
440
+ Read: ${folder}/spec.md and ${folder}/spec-lite.md
441
+
442
+ Create ${folder}/tasks.md with:
443
+ - Checkbox format: "- [ ] Task description"
444
+ - TDD approach (test first)
445
+ - Logical order
446
+ - File paths for each task`, 'kspec-tasks');
447
+ },
448
+
449
+ async 'verify-tasks'(args) {
450
+ const folder = getOrSelectSpec(args.join(' '));
451
+ const stats = getTaskStats(folder);
452
+
453
+ log(`Verifying tasks: ${folder}`);
454
+ if (stats) log(`Progress: ${stats.done}/${stats.total} tasks completed`);
455
+
456
+ await chat(`Verify tasks in ${folder}/tasks.md:
457
+
458
+ 1. Do tasks cover ALL requirements from spec.md?
459
+ 2. Check completion status of each task
460
+ 3. For completed tasks, verify test coverage
461
+ 4. Identify any missing tasks
462
+
463
+ Report: X/Y tasks done, gaps found, coverage assessment.`, 'kspec-verify');
464
+ },
465
+
466
+ async build(args) {
467
+ const folder = getOrSelectSpec(args.join(' '));
468
+ const stats = getTaskStats(folder);
469
+
470
+ log(`Building: ${folder}`);
471
+ if (stats) {
472
+ if (stats.remaining === 0) {
473
+ log('All tasks completed! Run: kspec verify');
474
+ return;
475
+ }
476
+ log(`Progress: ${stats.done}/${stats.total} (${stats.remaining} remaining)`);
477
+ }
478
+
479
+ const execMode = config.autoExecute || 'ask';
480
+ const execNote = execMode === 'auto' ? 'Auto-execute enabled.' :
481
+ execMode === 'dry' ? 'Dry-run mode - show commands only.' :
482
+ 'Ask before executing commands.';
483
+
484
+ await chat(`Execute tasks from ${folder}/tasks.md
485
+
486
+ ${execNote}
487
+
488
+ 1. Find first uncompleted task (- [ ])
489
+ 2. Write test first (TDD)
490
+ 3. Implement to pass test
491
+ 4. Run tests
492
+ 5. Mark complete: change "- [ ]" to "- [x]" in tasks.md
493
+ 6. Save tasks.md after each completion
494
+ 7. Continue to next task
495
+
496
+ CRITICAL: Update tasks.md after each task completion.
497
+ NEVER delete .kiro or .kspec folders.`, 'kspec-build');
498
+ },
499
+
500
+ async verify(args) {
501
+ const folder = getOrSelectSpec(args.join(' '));
502
+ const stats = getTaskStats(folder);
503
+
504
+ log(`Verifying implementation: ${folder}`);
505
+ if (stats) log(`Tasks: ${stats.done}/${stats.total}`);
506
+
507
+ await chat(`Verify implementation for ${folder}:
508
+
509
+ 1. Read spec.md - list all requirements
510
+ 2. Read tasks.md - check all marked [x]
511
+ 3. Check codebase - does implementation match spec?
512
+ 4. Run tests - do they pass?
513
+ 5. Check coverage - are requirements tested?
514
+
515
+ Report:
516
+ - Requirements: X/Y implemented
517
+ - Tasks: X/Y completed
518
+ - Tests: PASS/FAIL
519
+ - Gaps: [list any]`, 'kspec-verify');
520
+ },
521
+
522
+ async done(args) {
523
+ const folder = getOrSelectSpec(args.join(' '));
524
+ const stats = getTaskStats(folder);
525
+
526
+ if (stats && stats.remaining > 0) {
527
+ const proceed = await confirm(`${stats.remaining} tasks remaining. Mark done anyway?`);
528
+ if (!proceed) return;
529
+ }
530
+
531
+ log(`Completing: ${folder}`);
532
+
533
+ // Create memory
534
+ await chat(`Harvest learnings from ${folder}:
535
+
536
+ 1. Read spec.md, tasks.md, and implementation
537
+ 2. Create ${folder}/memory.md with:
538
+ - Key decisions made
539
+ - Patterns used
540
+ - Lessons learned
541
+ - Follow-ups needed
542
+
543
+ 3. Update .kspec/memory.md (project-level) with:
544
+ - New glossary terms
545
+ - Reusable patterns
546
+ - Cross-cutting learnings`, 'kspec-analyse');
547
+
548
+ log('Spec completed!');
549
+ },
550
+
551
+ async review(args) {
552
+ const target = args.join(' ') || 'recent changes (git diff HEAD~1)';
553
+ await chat(`Review: ${target}
554
+
555
+ Check compliance with .kiro/steering/
556
+ Evaluate quality, tests, security.
557
+ Output: APPROVE or REQUEST_CHANGES with specifics.`, 'kspec-review');
558
+ },
559
+
560
+ list() {
561
+ const specsDir = getSpecsDir();
562
+ if (!fs.existsSync(specsDir)) {
563
+ console.log('No specs yet. Run: kspec spec "Feature Name"');
564
+ return;
565
+ }
566
+
567
+ const current = getCurrentSpec();
568
+ const specs = fs.readdirSync(specsDir)
569
+ .filter(d => fs.statSync(path.join(specsDir, d)).isDirectory())
570
+ .sort().reverse();
571
+
572
+ if (specs.length === 0) {
573
+ console.log('No specs yet. Run: kspec spec "Feature Name"');
574
+ return;
575
+ }
576
+
577
+ console.log('\nSpecs:\n');
578
+ specs.forEach(s => {
579
+ const folder = path.join(specsDir, s);
580
+ const isCurrent = folder === current;
581
+ const stats = getTaskStats(folder);
582
+ const progress = stats ? `[${stats.done}/${stats.total}]` : '[no tasks]';
583
+ console.log(` ${isCurrent ? '→' : ' '} ${s} ${progress}`);
584
+ });
585
+ console.log('');
586
+ },
587
+
588
+ status() {
589
+ const current = getCurrentSpec();
590
+
591
+ console.log('\nkspec Status\n');
592
+ console.log(`CLI: ${detectCli() || '(not installed)'}`);
593
+ console.log(`Initialized: ${config.initialized ? 'yes' : 'no'}`);
594
+ console.log(`Date format: ${config.dateFormat || 'YYYY-MM-DD'}`);
595
+ console.log(`Auto-execute: ${config.autoExecute || 'ask'}`);
596
+
597
+ if (current) {
598
+ console.log(`\nCurrent spec: ${path.basename(current)}`);
599
+ const stats = getTaskStats(current);
600
+ if (stats) {
601
+ console.log(`Tasks: ${stats.done}/${stats.total} completed`);
602
+ if (stats.remaining > 0) {
603
+ console.log(`\nNext: kspec build`);
604
+ } else {
605
+ console.log(`\nNext: kspec verify`);
606
+ }
607
+ } else {
608
+ console.log(`\nNext: kspec tasks`);
609
+ }
610
+ } else {
611
+ console.log(`\nNo current spec. Run: kspec spec "Feature Name"`);
612
+ }
613
+ console.log('');
614
+ },
615
+
616
+ agents() {
617
+ console.log(`
618
+ kspec Agents
619
+
620
+ Agent Shortcut Purpose
621
+ ─────────────────────────────────────────────
622
+ kspec-analyse Ctrl+A Analyse codebase, update steering
623
+ kspec-spec Ctrl+S Create specifications
624
+ kspec-tasks Ctrl+T Generate tasks from spec
625
+ kspec-build Ctrl+B Execute tasks with TDD
626
+ kspec-verify Ctrl+V Verify spec/tasks/implementation
627
+ kspec-review Ctrl+R Code review
628
+
629
+ Switch: /agent swap or use keyboard shortcuts
630
+ `);
631
+ },
632
+
633
+ help() {
634
+ console.log(`
635
+ kspec - Spec-driven development for Kiro CLI
636
+
637
+ Workflow:
638
+ kspec init Interactive setup
639
+ kspec analyse Analyse codebase, update steering
640
+ kspec spec "Feature" Create specification
641
+ kspec verify-spec Verify spec is complete
642
+ kspec tasks Generate tasks from spec
643
+ kspec verify-tasks Verify tasks cover spec
644
+ kspec build Execute tasks with TDD
645
+ kspec verify Verify implementation
646
+ kspec done Complete spec, harvest memory
647
+
648
+ Other:
649
+ kspec review [target] Code review
650
+ kspec list List all specs
651
+ kspec status Current status
652
+ kspec agents List agents
653
+ kspec help Show this help
654
+
655
+ Examples:
656
+ kspec init
657
+ kspec spec "User Authentication"
658
+ kspec tasks
659
+ kspec build
660
+ kspec verify
661
+ kspec done
662
+ `);
663
+ }
664
+ };
665
+
666
+ async function run(args) {
667
+ const cmd = (args[0] || 'help').replace(/^\//, '');
668
+ const cmdArgs = args.slice(1);
669
+
670
+ if (commands[cmd]) {
671
+ await commands[cmd](cmdArgs);
672
+ } else {
673
+ die(`Unknown command: ${cmd}\nRun 'kspec help' for usage.`);
674
+ }
675
+ }
676
+
677
+ module.exports = { run, commands, loadConfig };