km-spec-driven-development 0.1.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/bin/km-spec ADDED
@@ -0,0 +1,995 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Resolve symlinks to find the real script location (works on Linux and macOS)
5
+ _SOURCE="${BASH_SOURCE[0]}"
6
+ while [[ -L "$_SOURCE" ]]; do
7
+ _DIR="$(cd -P "$(dirname "$_SOURCE")" && pwd)"
8
+ _SOURCE="$(readlink "$_SOURCE")"
9
+ [[ "$_SOURCE" != /* ]] && _SOURCE="$_DIR/$_SOURCE"
10
+ done
11
+ SCRIPT_DIR="$(cd -P "$(dirname "$_SOURCE")" && pwd)"
12
+ TEMPLATES_DIR="${SCRIPT_DIR}/../templates"
13
+
14
+ VERSION="0.1.0"
15
+
16
+ # ─── Logo & Help ──────────────────────────────────────────────────────────────
17
+
18
+ print_logo() {
19
+ cat <<'EOF'
20
+ _ __ __ __ ____
21
+ | |/ /| \/ | / ___| _ __ ___ ___
22
+ | ' / | |\/| |____\___ \| '_ \ / _ \/ __|
23
+ | . \ | | | |_____|__) | |_) | __/ (__
24
+ |_|\_\|_| |_| |____/| .__/ \___|\___|
25
+ |_|
26
+
27
+ KM Spec Driven Development
28
+ Lightweight Spec Driven Development for AI-assisted projects
29
+ EOF
30
+ }
31
+
32
+ print_help() {
33
+ print_logo
34
+ cat <<'EOF'
35
+
36
+ Usage:
37
+ km-spec <command> [options]
38
+
39
+ Commands:
40
+ init Initialize SDD structure
41
+ project Configure .specs/project/PROJECT.md
42
+ roadmap Configure .specs/project/ROADMAP.md
43
+ state Update .specs/project/STATE.md
44
+ stack Update Technical Context in CONVENTIONS.md
45
+ architecture Update Architecture in CONVENTIONS.md
46
+ structure Update Project Structure in CONVENTIONS.md
47
+ testing Update Testing in CONVENTIONS.md
48
+ integrations Update Integrations in CONVENTIONS.md
49
+ concerns Update Concerns in CONVENTIONS.md
50
+ feature <name> Create a feature spec
51
+ quick <name> Create a quick task
52
+ status Show SDD status
53
+ doctor Validate SDD structure
54
+ help Show this help
55
+ EOF
56
+ }
57
+
58
+ # ─── Helpers ──────────────────────────────────────────────────────────────────
59
+
60
+ slugify() {
61
+ local s
62
+ s="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
63
+ s="$(printf '%s' "$s" | sed \
64
+ -e 's/[áàãâä]/a/g' \
65
+ -e 's/[éèêë]/e/g' \
66
+ -e 's/[íìîï]/i/g' \
67
+ -e 's/[óòõôö]/o/g' \
68
+ -e 's/[úùûü]/u/g' \
69
+ -e 's/[ç]/c/g' \
70
+ -e 's/[ñ]/n/g')"
71
+ s="$(printf '%s' "$s" | sed 's/[^a-z0-9]/-/g; s/-\{2,\}/-/g; s/^-//; s/-$//')"
72
+ printf '%s' "$s"
73
+ }
74
+
75
+ require_templates() {
76
+ if [[ ! -d "$TEMPLATES_DIR" ]]; then
77
+ printf 'Error: templates directory not found at: %s\n' "$TEMPLATES_DIR" >&2
78
+ exit 1
79
+ fi
80
+ }
81
+
82
+ require_init() {
83
+ if [[ ! -d ".specs" ]]; then
84
+ printf 'Error: .specs/ not found in current directory.\nRun: km-spec init\n' >&2
85
+ exit 1
86
+ fi
87
+ }
88
+
89
+ ok() { printf ' \033[32mOK \033[0m %s\n' "$1"; }
90
+ warn() { printf ' \033[33mWARN \033[0m %s\n' "$1"; }
91
+ err() { printf ' \033[31mERROR\033[0m %s\n' "$1"; }
92
+ info() { printf ' %s\n' "$1"; }
93
+
94
+ confirm() {
95
+ local reply
96
+ read -r -p "$1 [y/N] " reply || true
97
+ [[ "$reply" =~ ^[Yy]$ ]]
98
+ }
99
+
100
+ # update_section FILE HEADING NEW_CONTENT
101
+ # Replaces the body of a ## section with NEW_CONTENT.
102
+ # Appends the section if the heading is not found.
103
+ # Preserves all other sections.
104
+ update_section() {
105
+ local file="$1"
106
+ local heading="$2"
107
+ local content="$3"
108
+ local tmp_content tmp_result
109
+ tmp_content="$(mktemp)"
110
+ tmp_result="$(mktemp)"
111
+
112
+ printf '%s\n' "$content" > "$tmp_content"
113
+
114
+ awk -v heading="$heading" -v cf="$tmp_content" '
115
+ BEGIN {
116
+ in_section=0; inserted=0; n=0
117
+ while ((getline line < cf) > 0) lines[++n] = line
118
+ close(cf)
119
+ }
120
+ $0 == heading {
121
+ in_section=1; inserted=1
122
+ print $0
123
+ for (i=1; i<=n; i++) print lines[i]
124
+ next
125
+ }
126
+ in_section && /^## / { in_section=0 }
127
+ !in_section { print }
128
+ END {
129
+ if (!inserted) {
130
+ print ""
131
+ print heading
132
+ for (i=1; i<=n; i++) print lines[i]
133
+ }
134
+ }
135
+ ' "$file" > "$tmp_result"
136
+
137
+ cp "$tmp_result" "$file"
138
+ rm -f "$tmp_result" "$tmp_content"
139
+ }
140
+
141
+ # ─── cmd_help ─────────────────────────────────────────────────────────────────
142
+
143
+ cmd_help() {
144
+ print_help
145
+ }
146
+
147
+ # ─── cmd_init ─────────────────────────────────────────────────────────────────
148
+
149
+ cmd_init() {
150
+ require_templates
151
+
152
+ local force=false
153
+ [[ "${1:-}" == "--force" ]] && force=true
154
+
155
+ local created=0 skipped=0
156
+
157
+ local files=(
158
+ "AGENTS.md"
159
+ "SDD.md"
160
+ ".specs/project/PROJECT.md"
161
+ ".specs/project/ROADMAP.md"
162
+ ".specs/project/STATE.md"
163
+ ".specs/codebase/CONVENTIONS.md"
164
+ ".specs/features/.gitkeep"
165
+ ".specs/quick/.gitkeep"
166
+ )
167
+
168
+ printf '[KM Spec] Initializing SDD structure...\n\n'
169
+
170
+ for f in "${files[@]}"; do
171
+ local src="$TEMPLATES_DIR/$f"
172
+ local dst="./$f"
173
+ local dir
174
+ dir="$(dirname "$dst")"
175
+
176
+ if [[ ! -f "$src" ]]; then
177
+ warn "Template not found: $src"
178
+ continue
179
+ fi
180
+
181
+ mkdir -p "$dir"
182
+
183
+ if [[ -f "$dst" ]] && [[ "$force" != true ]]; then
184
+ printf '[KM Spec] Skipped: %s already exists\n' "$f"
185
+ skipped=$((skipped + 1))
186
+ else
187
+ cp "$src" "$dst"
188
+ printf '[KM Spec] Created: %s\n' "$f"
189
+ created=$((created + 1))
190
+ fi
191
+ done
192
+
193
+ printf '\n[KM Spec] Done. Created: %d Skipped: %d\n' "$created" "$skipped"
194
+ printf '\nNext steps:\n'
195
+ printf ' km-spec project — fill in project goals and constraints\n'
196
+ printf ' km-spec stack — document the technical stack\n'
197
+ }
198
+
199
+ # ─── cmd_feature ──────────────────────────────────────────────────────────────
200
+
201
+ cmd_feature() {
202
+ require_init
203
+
204
+ local name="${1:-}"
205
+ local force=false
206
+
207
+ if [[ -z "$name" ]]; then
208
+ printf 'Usage: km-spec feature "Feature Name"\n' >&2
209
+ exit 1
210
+ fi
211
+
212
+ shift || true
213
+ [[ "${1:-}" == "--force" ]] && force=true
214
+
215
+ local slug
216
+ slug="$(slugify "$name")"
217
+ local dir=".specs/features/$slug"
218
+
219
+ if [[ -d "$dir" ]] && [[ "$force" != true ]]; then
220
+ printf 'Feature already exists: %s\nUse --force to overwrite.\n' "$dir"
221
+ exit 1
222
+ fi
223
+
224
+ mkdir -p "$dir"
225
+
226
+ printf '\nCreating feature: %s\n\n' "$name"
227
+
228
+ local goal req1 req2 req3 ac1 ac2 ac3 oos
229
+ goal="" req1="" req2="" req3="" ac1="" ac2="" ac3="" oos=""
230
+ read -r -p "Feature goal: " goal || true
231
+ read -r -p "Requirement 1: " req1 || true
232
+ read -r -p "Requirement 2: " req2 || true
233
+ read -r -p "Requirement 3: " req3 || true
234
+ read -r -p "Acceptance criterion 1: " ac1 || true
235
+ read -r -p "Acceptance criterion 2: " ac2 || true
236
+ read -r -p "Acceptance criterion 3: " ac3 || true
237
+ read -r -p "Out of scope: " oos || true
238
+
239
+ cat > "$dir/spec.md" <<SPEC
240
+ # Feature: $name
241
+
242
+ ## Goal
243
+
244
+ $goal
245
+
246
+ ## Requirements
247
+
248
+ - REQ-001: $req1
249
+ - REQ-002: $req2
250
+ - REQ-003: $req3
251
+
252
+ ## Acceptance Criteria
253
+
254
+ - [ ] AC-001: $ac1
255
+ - [ ] AC-002: $ac2
256
+ - [ ] AC-003: $ac3
257
+
258
+ ## Out of Scope
259
+
260
+ - $oos
261
+ SPEC
262
+
263
+ cat > "$dir/tasks.md" <<TASKS
264
+ # Tasks: $name
265
+
266
+ ## TASK-001 — Define implementation plan
267
+
268
+ Status: TODO
269
+
270
+ Related requirements:
271
+
272
+ - REQ-001
273
+ - REQ-002
274
+ - REQ-003
275
+
276
+ Objective:
277
+
278
+ Create an implementation plan for this feature based on \`spec.md\`.
279
+
280
+ Implementation steps:
281
+
282
+ 1. Read \`.specs/project/STATE.md\`.
283
+ 2. Read \`.specs/project/PROJECT.md\`.
284
+ 3. Read \`.specs/codebase/CONVENTIONS.md\`.
285
+ 4. Inspect the existing code before changing anything.
286
+ 5. Break the feature into atomic implementation tasks.
287
+
288
+ Acceptance criteria:
289
+
290
+ - [ ] Implementation tasks are clear and atomic.
291
+ - [ ] Each task references related requirements.
292
+ - [ ] Validation requirements are defined.
293
+
294
+ Validation:
295
+
296
+ - [ ] Build passes, if applicable.
297
+ - [ ] Lint passes, if available.
298
+ - [ ] Typecheck passes, if available.
299
+ - [ ] Tests pass, if available.
300
+ - [ ] Acceptance criteria verified.
301
+
302
+ Notes:
303
+ TASKS
304
+
305
+ cat > "$dir/validation.md" <<VALIDATION
306
+ # Validation: $name
307
+
308
+ ## Validation Summary
309
+
310
+ Status: Pending
311
+
312
+ ## Evidence
313
+
314
+ | Gate | Result | Command / Notes |
315
+ |------|--------|-----------------|
316
+ | Build | Pending | |
317
+ | Lint | Pending | |
318
+ | Typecheck | Pending | |
319
+ | Tests | Pending | |
320
+ | Manual Review | Pending | |
321
+
322
+ ## Issues Found
323
+
324
+ None.
325
+
326
+ ## Final Result
327
+
328
+ Pending.
329
+ VALIDATION
330
+
331
+ printf '\n'
332
+ printf '[KM Spec] Created: %s/spec.md\n' "$dir"
333
+ printf '[KM Spec] Created: %s/tasks.md\n' "$dir"
334
+ printf '[KM Spec] Created: %s/validation.md\n' "$dir"
335
+ printf '\nNext step: open %s/spec.md and review the spec.\n' "$dir"
336
+ printf 'Then ask your AI agent to implement TASK-001.\n'
337
+ }
338
+
339
+ # ─── cmd_quick ────────────────────────────────────────────────────────────────
340
+
341
+ cmd_quick() {
342
+ require_init
343
+
344
+ local name="${1:-}"
345
+ local force=false
346
+
347
+ if [[ -z "$name" ]]; then
348
+ printf 'Usage: km-spec quick "Task Name"\n' >&2
349
+ exit 1
350
+ fi
351
+
352
+ shift || true
353
+ [[ "${1:-}" == "--force" ]] && force=true
354
+
355
+ local slug
356
+ slug="$(slugify "$name")"
357
+ local file=".specs/quick/${slug}.md"
358
+
359
+ if [[ -f "$file" ]] && [[ "$force" != true ]]; then
360
+ printf 'Quick task already exists: %s\nUse --force to overwrite.\n' "$file"
361
+ exit 1
362
+ fi
363
+
364
+ mkdir -p ".specs/quick"
365
+
366
+ printf '\nCreating quick task: %s\n\n' "$name"
367
+
368
+ local objective files_affected ac validation_steps
369
+ objective="" files_affected="" ac="" validation_steps=""
370
+ read -r -p "Objective: " objective || true
371
+ read -r -p "Likely affected files: " files_affected || true
372
+ read -r -p "Acceptance criteria: " ac || true
373
+ read -r -p "Validation steps: " validation_steps || true
374
+
375
+ cat > "$file" <<QUICK
376
+ # Quick Task: $name
377
+
378
+ ## Objective
379
+
380
+ $objective
381
+
382
+ ## Scope
383
+
384
+ Files likely affected:
385
+
386
+ - $files_affected
387
+
388
+ ## Acceptance Criteria
389
+
390
+ - [ ] $ac
391
+
392
+ ## Validation
393
+
394
+ - [ ] Build passes, if applicable.
395
+ - [ ] Lint passes, if available.
396
+ - [ ] Typecheck passes, if available.
397
+ - [ ] Tests pass, if available.
398
+ - [ ] Manual check completed, if needed.
399
+
400
+ Notes: $validation_steps
401
+
402
+ ## Summary
403
+
404
+ [Describe what was changed after implementation.]
405
+ QUICK
406
+
407
+ printf '\n[KM Spec] Created: %s\n' "$file"
408
+ printf '\nNext step: ask your AI agent to complete this task, then update the Summary section.\n'
409
+ }
410
+
411
+ # ─── cmd_project ──────────────────────────────────────────────────────────────
412
+
413
+ cmd_project() {
414
+ require_init
415
+
416
+ local file=".specs/project/PROJECT.md"
417
+ mkdir -p ".specs/project"
418
+
419
+ if [[ -f "$file" ]] && [[ -s "$file" ]]; then
420
+ if ! confirm "PROJECT.md already exists. Overwrite?"; then
421
+ printf 'Aborted.\n'
422
+ exit 0
423
+ fi
424
+ fi
425
+
426
+ printf '\nConfiguring PROJECT.md\n\n'
427
+
428
+ local name desc problem users goals nonneg oos terms
429
+ name="" desc="" problem="" users="" goals="" nonneg="" oos="" terms=""
430
+ read -r -p "Project name: " name || true
431
+ read -r -p "One-line description: " desc || true
432
+ read -r -p "Problem being solved: " problem || true
433
+ read -r -p "Target users (comma-separated): " users || true
434
+ read -r -p "Main goals (comma-separated): " goals || true
435
+ read -r -p "Non-negotiables (comma-separated): " nonneg || true
436
+ read -r -p "Out of scope: " oos || true
437
+ read -r -p "Key terms (term: definition): " terms || true
438
+
439
+ local users_md goals_md nonneg_md
440
+ users_md="$(printf '%s' "$users" | tr ',' '\n' | sed 's/^[[:space:]]*/- /')"
441
+ goals_md="$(printf '%s' "$goals" | tr ',' '\n' | sed 's/^[[:space:]]*/- /')"
442
+ nonneg_md="$(printf '%s' "$nonneg" | tr ',' '\n' | sed 's/^[[:space:]]*/- /')"
443
+
444
+ cat > "$file" <<PROJECT
445
+ # Project
446
+
447
+ ## Name
448
+
449
+ $name
450
+
451
+ ## One-Line Description
452
+
453
+ $desc
454
+
455
+ ## Problem
456
+
457
+ $problem
458
+
459
+ ## Target Users
460
+
461
+ $users_md
462
+
463
+ ## Goals
464
+
465
+ $goals_md
466
+
467
+ ## Non-Negotiables
468
+
469
+ $nonneg_md
470
+
471
+ ## Out of Scope
472
+
473
+ - $oos
474
+
475
+ ## Key Terms
476
+
477
+ - $terms
478
+ PROJECT
479
+
480
+ printf '\n[KM Spec] Updated: %s\n' "$file"
481
+ printf '\nNext step: km-spec stack\n'
482
+ }
483
+
484
+ # ─── cmd_roadmap ──────────────────────────────────────────────────────────────
485
+
486
+ cmd_roadmap() {
487
+ require_init
488
+
489
+ local file=".specs/project/ROADMAP.md"
490
+ mkdir -p ".specs/project"
491
+
492
+ if [[ -f "$file" ]] && [[ -s "$file" ]]; then
493
+ if ! confirm "ROADMAP.md already exists. Overwrite?"; then
494
+ printf 'Aborted.\n'
495
+ exit 0
496
+ fi
497
+ fi
498
+
499
+ printf '\nConfiguring ROADMAP.md\n\n'
500
+
501
+ local phase ms_name ms_goal ms_status features backlog deferred
502
+ phase="" ms_name="" ms_goal="" ms_status="" features="" backlog="" deferred=""
503
+ read -r -p "Current phase (Discovery/Setup/MVP/Active Development/Stabilization/Maintenance): " phase || true
504
+ read -r -p "Milestone name: " ms_name || true
505
+ read -r -p "Milestone goal: " ms_goal || true
506
+ read -r -p "Milestone status (Planned/In Progress/Done): " ms_status || true
507
+ read -r -p "Features in this milestone (comma-separated): " features || true
508
+ read -r -p "Backlog items (comma-separated): " backlog || true
509
+ read -r -p "Deferred items (comma-separated): " deferred || true
510
+
511
+ local features_md backlog_md deferred_md
512
+ features_md="$(printf '%s' "$features" | tr ',' '\n' | sed 's/^[[:space:]]*/- [ ] /')"
513
+ backlog_md="$(printf '%s' "$backlog" | tr ',' '\n' | sed 's/^[[:space:]]*/- [ ] /')"
514
+ deferred_md="$(printf '%s' "$deferred" | tr ',' '\n' | sed 's/^[[:space:]]*/- [ ] /')"
515
+
516
+ cat > "$file" <<ROADMAP
517
+ # Roadmap
518
+
519
+ ## Current Phase
520
+
521
+ $phase
522
+
523
+ ## Milestones
524
+
525
+ ### Milestone 1 — $ms_name
526
+
527
+ **Status:** $ms_status
528
+ **Goal:** $ms_goal
529
+
530
+ #### Features
531
+
532
+ $features_md
533
+
534
+ ## Backlog
535
+
536
+ $backlog_md
537
+
538
+ ## Deferred
539
+
540
+ $deferred_md
541
+ ROADMAP
542
+
543
+ printf '\n[KM Spec] Updated: %s\n' "$file"
544
+ printf '\nNext step: km-spec feature "Your first feature"\n'
545
+ }
546
+
547
+ # ─── cmd_state ────────────────────────────────────────────────────────────────
548
+
549
+ cmd_state() {
550
+ require_init
551
+
552
+ local file=".specs/project/STATE.md"
553
+ if [[ ! -f "$file" ]]; then
554
+ printf 'Error: %s not found. Run: km-spec init\n' "$file" >&2
555
+ exit 1
556
+ fi
557
+
558
+ printf '\nUpdating STATE.md\n\n'
559
+
560
+ local focus last_task current_task next_steps decision blocker notes
561
+ focus="" last_task="" current_task="" next_steps="" decision="" blocker="" notes=""
562
+ read -r -p "Current focus: " focus || true
563
+ read -r -p "Last completed task: " last_task || true
564
+ read -r -p "Current task: " current_task || true
565
+ read -r -p "Suggested next steps (comma-separated): " next_steps || true
566
+ read -r -p "Decision to record (Enter to skip): " decision || true
567
+ read -r -p "Blocker to record (Enter to skip): " blocker || true
568
+ read -r -p "Notes for future agents: " notes || true
569
+
570
+ local today
571
+ today="$(date '+%Y-%m-%d %H:%M')"
572
+
573
+ local next_md
574
+ next_md="$(printf '%s' "$next_steps" | tr ',' '\n' | awk 'NR>0{print NR". "$0}' | sed 's/^[0-9]*\.[[:space:]]*/& /')"
575
+
576
+ local decisions_text blockers_text
577
+ decisions_text="$([ -n "$decision" ] && printf '- %s' "$decision" || printf '- None.')"
578
+ blockers_text="$([ -n "$blocker" ] && printf '- %s' "$blocker" || printf '- None.')"
579
+
580
+ cat >> "$file" <<SESSION
581
+
582
+ ---
583
+
584
+ ## Session — $today
585
+
586
+ **Current Focus:** $focus
587
+
588
+ **Last Completed Task:** $last_task
589
+
590
+ **Current Task:** $current_task
591
+
592
+ **Suggested Next Steps:**
593
+
594
+ $next_md
595
+
596
+ **Decisions:**
597
+
598
+ $decisions_text
599
+
600
+ **Blockers:**
601
+
602
+ $blockers_text
603
+
604
+ **Notes:**
605
+
606
+ $notes
607
+ SESSION
608
+
609
+ printf '\n[KM Spec] Updated: %s\n' "$file"
610
+ printf '\n[KM Spec] Done.\n'
611
+ }
612
+
613
+ # ─── CONVENTIONS.md updaters ──────────────────────────────────────────────────
614
+
615
+ conventions_file=".specs/codebase/CONVENTIONS.md"
616
+
617
+ _require_conventions() {
618
+ require_init
619
+ if [[ ! -f "$conventions_file" ]]; then
620
+ printf 'Error: %s not found. Run: km-spec init\n' "$conventions_file" >&2
621
+ exit 1
622
+ fi
623
+ }
624
+
625
+ cmd_stack() {
626
+ _require_conventions
627
+ printf '\nUpdating Technical Context in CONVENTIONS.md\n\n'
628
+
629
+ local lang framework runtime pkgmgr database libs deploy cicd
630
+ lang="" framework="" runtime="" pkgmgr="" database="" libs="" deploy="" cicd=""
631
+ read -r -p "Language: " lang || true
632
+ read -r -p "Framework: " framework || true
633
+ read -r -p "Runtime: " runtime || true
634
+ read -r -p "Package manager: " pkgmgr || true
635
+ read -r -p "Database: " database || true
636
+ read -r -p "Main libraries: " libs || true
637
+ read -r -p "Deployment: " deploy || true
638
+ read -r -p "CI/CD: " cicd || true
639
+
640
+ local content
641
+ content="
642
+ - Language: $lang
643
+ - Framework: $framework
644
+ - Runtime: $runtime
645
+ - Package manager: $pkgmgr
646
+ - Database: $database
647
+ - Main libraries: $libs
648
+ - Deployment: $deploy
649
+ - CI/CD: $cicd"
650
+
651
+ update_section "$conventions_file" "## 1. Technical Context" "$content"
652
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
653
+ printf '\nNext step: km-spec architecture\n'
654
+ }
655
+
656
+ cmd_architecture() {
657
+ _require_conventions
658
+ printf '\nUpdating Architecture in CONVENTIONS.md\n\n'
659
+
660
+ local modules dataflow patterns decisions
661
+ modules="" dataflow="" patterns="" decisions=""
662
+ read -r -p "Main modules: " modules || true
663
+ read -r -p "Data flow: " dataflow || true
664
+ read -r -p "Architectural patterns: " patterns || true
665
+ read -r -p "Important technical decisions: " decisions || true
666
+
667
+ local content
668
+ content="
669
+ - Main modules: $modules
670
+ - Data flow: $dataflow
671
+ - Architectural patterns: $patterns
672
+ - Important decisions: $decisions"
673
+
674
+ update_section "$conventions_file" "## 2. Architecture" "$content"
675
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
676
+ printf '\nNext step: km-spec structure\n'
677
+ }
678
+
679
+ cmd_structure() {
680
+ _require_conventions
681
+ printf '\nUpdating Project Structure in CONVENTIONS.md\n\n'
682
+
683
+ local srcdir api_dir comp_dir svc_dir test_dir generated
684
+ srcdir="" api_dir="" comp_dir="" svc_dir="" test_dir="" generated=""
685
+ read -r -p "Main source directory: " srcdir || true
686
+ read -r -p "Where API files go: " api_dir || true
687
+ read -r -p "Where components go: " comp_dir || true
688
+ read -r -p "Where services go: " svc_dir || true
689
+ read -r -p "Where tests go: " test_dir || true
690
+ read -r -p "Generated files or folders: " generated || true
691
+
692
+ local content
693
+ content="
694
+ Main source directory: \`$srcdir\`
695
+
696
+ File placement rules:
697
+
698
+ - API files: $api_dir
699
+ - Components: $comp_dir
700
+ - Services: $svc_dir
701
+ - Tests: $test_dir
702
+ - Generated: $generated"
703
+
704
+ update_section "$conventions_file" "## 3. Project Structure" "$content"
705
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
706
+ printf '\nNext step: km-spec testing\n'
707
+ }
708
+
709
+ cmd_testing() {
710
+ _require_conventions
711
+ printf '\nUpdating Testing in CONVENTIONS.md\n\n'
712
+
713
+ local framework pattern unit_rules int_rules e2e_rules commands
714
+ framework="" pattern="" unit_rules="" int_rules="" e2e_rules="" commands=""
715
+ read -r -p "Test framework: " framework || true
716
+ read -r -p "Test file pattern: " pattern || true
717
+ read -r -p "Unit test rules: " unit_rules || true
718
+ read -r -p "Integration test rules: " int_rules || true
719
+ read -r -p "E2E test rules: " e2e_rules || true
720
+ read -r -p "Commands to run tests: " commands || true
721
+
722
+ local content
723
+ content="
724
+ - Test framework: $framework
725
+ - Test file pattern: $pattern
726
+ - Unit test rules: $unit_rules
727
+ - Integration test rules: $int_rules
728
+ - E2E test rules: $e2e_rules
729
+ - Required commands: \`$commands\`
730
+
731
+ Validation gates:
732
+
733
+ - [ ] Build
734
+ - [ ] Lint
735
+ - [ ] Typecheck
736
+ - [ ] Tests
737
+ - [ ] Manual check when needed"
738
+
739
+ update_section "$conventions_file" "## 5. Testing" "$content"
740
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
741
+ printf '\nNext step: km-spec integrations\n'
742
+ }
743
+
744
+ cmd_integrations() {
745
+ _require_conventions
746
+ printf '\nUpdating Integrations in CONVENTIONS.md\n\n'
747
+
748
+ local svc_name purpose auth env_vars limits webhooks
749
+ svc_name="" purpose="" auth="" env_vars="" limits="" webhooks=""
750
+ read -r -p "Service name: " svc_name || true
751
+ read -r -p "Purpose: " purpose || true
752
+ read -r -p "Authentication method: " auth || true
753
+ read -r -p "Environment variables: " env_vars || true
754
+ read -r -p "Rate limits or risks: " limits || true
755
+ read -r -p "Webhook details (Enter to skip): " webhooks || true
756
+
757
+ local content
758
+ content="
759
+ | Field | Value |
760
+ |-------|-------|
761
+ | Service | $svc_name |
762
+ | Purpose | $purpose |
763
+ | Auth | $auth |
764
+ | Env vars | $env_vars |
765
+ | Risks / limits | $limits |
766
+ | Webhooks | ${webhooks:-None} |"
767
+
768
+ update_section "$conventions_file" "## 6. Integrations" "$content"
769
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
770
+ printf '\nNext step: km-spec concerns\n'
771
+ }
772
+
773
+ cmd_concerns() {
774
+ _require_conventions
775
+ printf '\nUpdating Concerns in CONVENTIONS.md\n\n'
776
+
777
+ local risks debt fragile low_cov deprecated
778
+ risks="" debt="" fragile="" low_cov="" deprecated=""
779
+ read -r -p "Known risks: " risks || true
780
+ read -r -p "Technical debt: " debt || true
781
+ read -r -p "Fragile areas: " fragile || true
782
+ read -r -p "Low test coverage areas: " low_cov || true
783
+ read -r -p "Deprecated patterns: " deprecated || true
784
+
785
+ local content
786
+ content="
787
+ - Known risks: $risks
788
+ - Technical debt: $debt
789
+ - Fragile areas: $fragile
790
+ - Low test coverage areas: $low_cov
791
+ - Deprecated patterns: $deprecated"
792
+
793
+ update_section "$conventions_file" "## 7. Concerns" "$content"
794
+ printf '\n[KM Spec] Updated: %s\n' "$conventions_file"
795
+ printf '\nRun km-spec status to review the full setup.\n'
796
+ }
797
+
798
+ # ─── cmd_status ───────────────────────────────────────────────────────────────
799
+
800
+ cmd_status() {
801
+ printf 'KM Spec Status\n\n'
802
+
803
+ printf 'Base files:\n'
804
+ local base_files=("AGENTS.md" "SDD.md" ".specs/project/PROJECT.md"
805
+ ".specs/project/ROADMAP.md" ".specs/project/STATE.md"
806
+ ".specs/codebase/CONVENTIONS.md")
807
+
808
+ for f in "${base_files[@]}"; do
809
+ if [[ -f "$f" ]]; then
810
+ printf ' Found %s\n' "$f"
811
+ else
812
+ printf ' Missing %s\n' "$f"
813
+ fi
814
+ done
815
+
816
+ printf '\nFeatures:\n'
817
+ local feat_found=false
818
+ if [[ -d ".specs/features" ]]; then
819
+ for d in .specs/features/*/; do
820
+ [[ -d "$d" ]] || continue
821
+ printf ' %s\n' "$(basename "$d")"
822
+ feat_found=true
823
+ done
824
+ fi
825
+ [[ "$feat_found" == true ]] || printf ' (none)\n'
826
+
827
+ printf '\nQuick tasks:\n'
828
+ local quick_found=false
829
+ if [[ -d ".specs/quick" ]]; then
830
+ for f in .specs/quick/*.md; do
831
+ [[ -f "$f" ]] || continue
832
+ printf ' %s\n' "$(basename "$f" .md)"
833
+ quick_found=true
834
+ done
835
+ fi
836
+ [[ "$quick_found" == true ]] || printf ' (none)\n'
837
+
838
+ printf '\nCONVENTIONS.md sections:\n'
839
+ if [[ -f ".specs/codebase/CONVENTIONS.md" ]]; then
840
+ local sections=(
841
+ "## 1. Technical Context"
842
+ "## 2. Architecture"
843
+ "## 3. Project Structure"
844
+ "## 4. Coding Standards"
845
+ "## 5. Testing"
846
+ "## 6. Integrations"
847
+ "## 7. Concerns"
848
+ "## 8. AI Rules"
849
+ "## 9. Optional Expansion"
850
+ )
851
+ for s in "${sections[@]}"; do
852
+ if grep -qF "$s" ".specs/codebase/CONVENTIONS.md" 2>/dev/null; then
853
+ printf ' Found %s\n' "$s"
854
+ else
855
+ printf ' Missing %s\n' "$s"
856
+ fi
857
+ done
858
+ else
859
+ printf ' CONVENTIONS.md not found\n'
860
+ fi
861
+ }
862
+
863
+ # ─── cmd_doctor ───────────────────────────────────────────────────────────────
864
+
865
+ cmd_doctor() {
866
+ printf 'KM Spec Doctor\n\n'
867
+
868
+ local errors=0 warnings=0
869
+
870
+ printf 'Required files:\n'
871
+ local required_files=("AGENTS.md" "SDD.md" ".specs/project/PROJECT.md"
872
+ ".specs/project/ROADMAP.md" ".specs/project/STATE.md"
873
+ ".specs/codebase/CONVENTIONS.md")
874
+ for f in "${required_files[@]}"; do
875
+ if [[ -f "$f" ]]; then ok "$f"
876
+ else err "$f — missing"; errors=$((errors + 1)); fi
877
+ done
878
+
879
+ printf '\nRequired directories:\n'
880
+ local required_dirs=(".specs/project" ".specs/codebase" ".specs/features" ".specs/quick")
881
+ for d in "${required_dirs[@]}"; do
882
+ if [[ -d "$d" ]]; then ok "$d"
883
+ else err "$d — missing"; errors=$((errors + 1)); fi
884
+ done
885
+
886
+ printf '\nForbidden standalone files (content must be inside CONVENTIONS.md):\n'
887
+ local forbidden=(".specs/codebase/STACK.md" ".specs/codebase/ARCHITECTURE.md"
888
+ ".specs/codebase/STRUCTURE.md" ".specs/codebase/TESTING.md"
889
+ ".specs/codebase/INTEGRATIONS.md" ".specs/codebase/CONCERNS.md")
890
+ for f in "${forbidden[@]}"; do
891
+ if [[ -f "$f" ]]; then
892
+ warn "$f — consolidate into CONVENTIONS.md"
893
+ warnings=$((warnings + 1))
894
+ else
895
+ ok "$(basename "$f") — not present (correct)"
896
+ fi
897
+ done
898
+
899
+ printf '\nCONVENTIONS.md sections:\n'
900
+ if [[ -f ".specs/codebase/CONVENTIONS.md" ]]; then
901
+ local sections=(
902
+ "## 1. Technical Context" "## 2. Architecture" "## 3. Project Structure"
903
+ "## 4. Coding Standards" "## 5. Testing" "## 6. Integrations"
904
+ "## 7. Concerns" "## 8. AI Rules"
905
+ )
906
+ for s in "${sections[@]}"; do
907
+ if grep -qF "$s" ".specs/codebase/CONVENTIONS.md" 2>/dev/null; then ok "$s"
908
+ else warn "$s — section missing"; warnings=$((warnings + 1)); fi
909
+ done
910
+ fi
911
+
912
+ printf '\nSTATE.md:\n'
913
+ if [[ -f ".specs/project/STATE.md" ]]; then
914
+ if [[ -s ".specs/project/STATE.md" ]]; then ok "STATE.md — not empty"
915
+ else warn "STATE.md — empty"; warnings=$((warnings + 1)); fi
916
+ fi
917
+
918
+ printf '\nAGENTS.md references:\n'
919
+ if [[ -f "AGENTS.md" ]]; then
920
+ local agents_ok=true
921
+ grep -q "STATE.md" "AGENTS.md" 2>/dev/null || agents_ok=false
922
+ grep -q "PROJECT.md" "AGENTS.md" 2>/dev/null || agents_ok=false
923
+ grep -q "CONVENTIONS.md" "AGENTS.md" 2>/dev/null || agents_ok=false
924
+ if [[ "$agents_ok" == true ]]; then
925
+ ok "AGENTS.md — references STATE.md, PROJECT.md, CONVENTIONS.md"
926
+ else
927
+ warn "AGENTS.md — missing one or more required file references"
928
+ warnings=$((warnings + 1))
929
+ fi
930
+ fi
931
+
932
+ printf '\nFeature integrity:\n'
933
+ local feat_checked=false
934
+ if [[ -d ".specs/features" ]]; then
935
+ for d in .specs/features/*/; do
936
+ [[ -d "$d" ]] || continue
937
+ feat_checked=true
938
+ local feat
939
+ feat="$(basename "$d")"
940
+ for req_file in spec.md tasks.md validation.md; do
941
+ if [[ -f "$d$req_file" ]]; then ok "$feat/$req_file"
942
+ else warn "$feat/$req_file — missing"; warnings=$((warnings + 1)); fi
943
+ done
944
+ done
945
+ fi
946
+ [[ "$feat_checked" == true ]] || info "No features found."
947
+
948
+ printf '\nQuick tasks:\n'
949
+ local quick_checked=false
950
+ if [[ -d ".specs/quick" ]]; then
951
+ for f in .specs/quick/*.md; do
952
+ [[ -f "$f" ]] || continue
953
+ quick_checked=true
954
+ ok "$(basename "$f")"
955
+ done
956
+ fi
957
+ [[ "$quick_checked" == true ]] || info "No quick tasks found."
958
+
959
+ printf '\nSummary:\n'
960
+ if [[ $errors -eq 0 ]] && [[ $warnings -eq 0 ]]; then
961
+ printf ' \033[32mAll checks passed.\033[0m\n'
962
+ else
963
+ [[ $errors -gt 0 ]] && printf ' \033[31m%d error(s) found.\033[0m\n' "$errors"
964
+ [[ $warnings -gt 0 ]] && printf ' \033[33m%d warning(s) found.\033[0m\n' "$warnings"
965
+ fi
966
+ }
967
+
968
+ # ─── Main ─────────────────────────────────────────────────────────────────────
969
+
970
+ main() {
971
+ local cmd="${1:-help}"
972
+ shift || true
973
+
974
+ case "$cmd" in
975
+ init) cmd_init "$@" ;;
976
+ feature) cmd_feature "$@" ;;
977
+ quick) cmd_quick "$@" ;;
978
+ project) cmd_project "$@" ;;
979
+ roadmap) cmd_roadmap "$@" ;;
980
+ state) cmd_state "$@" ;;
981
+ stack) cmd_stack "$@" ;;
982
+ architecture) cmd_architecture "$@" ;;
983
+ structure) cmd_structure "$@" ;;
984
+ testing) cmd_testing "$@" ;;
985
+ integrations) cmd_integrations "$@" ;;
986
+ concerns) cmd_concerns "$@" ;;
987
+ status) cmd_status "$@" ;;
988
+ doctor) cmd_doctor "$@" ;;
989
+ help|--help|-h) cmd_help ;;
990
+ version|--version|-v) printf 'km-spec v%s\n' "$VERSION" ;;
991
+ *) printf 'Unknown command: %s\n\n' "$cmd" >&2; cmd_help; exit 1 ;;
992
+ esac
993
+ }
994
+
995
+ main "$@"