specweave 0.22.0 → 0.22.2

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 (184) hide show
  1. package/CLAUDE.md +211 -0
  2. package/README.md +5 -5
  3. package/bin/specweave.js +5 -8
  4. package/dist/plugins/specweave-github/lib/CodeValidator.d.ts +1 -1
  5. package/dist/plugins/specweave-github/lib/CodeValidator.js +1 -1
  6. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +10 -0
  7. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-client-v2.js +26 -0
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  10. package/dist/plugins/specweave-github/lib/task-sync.d.ts.map +1 -1
  11. package/dist/plugins/specweave-github/lib/task-sync.js +7 -0
  12. package/dist/plugins/specweave-github/lib/task-sync.js.map +1 -1
  13. package/dist/src/cli/commands/migrate-to-profiles.d.ts +1 -0
  14. package/dist/src/cli/commands/migrate-to-profiles.d.ts.map +1 -1
  15. package/dist/src/cli/commands/migrate-to-profiles.js +12 -1
  16. package/dist/src/cli/commands/migrate-to-profiles.js.map +1 -1
  17. package/dist/src/cli/commands/next-command.d.ts +52 -0
  18. package/dist/src/cli/commands/next-command.d.ts.map +1 -0
  19. package/dist/src/cli/commands/next-command.js +204 -0
  20. package/dist/src/cli/commands/next-command.js.map +1 -0
  21. package/dist/src/cli/commands/sync-specs.d.ts +16 -0
  22. package/dist/src/cli/commands/sync-specs.d.ts.map +1 -0
  23. package/dist/src/cli/commands/sync-specs.js +130 -0
  24. package/dist/src/cli/commands/sync-specs.js.map +1 -0
  25. package/dist/src/cli/count-tasks.d.ts +20 -0
  26. package/dist/src/cli/count-tasks.d.ts.map +1 -0
  27. package/dist/src/cli/count-tasks.js +50 -0
  28. package/dist/src/cli/count-tasks.js.map +1 -0
  29. package/dist/src/config/ConfigManager.d.ts.map +1 -1
  30. package/dist/src/config/ConfigManager.js +2 -1
  31. package/dist/src/config/ConfigManager.js.map +1 -1
  32. package/dist/src/config/types.d.ts +50 -50
  33. package/dist/src/core/cicd/state-manager.d.ts +8 -0
  34. package/dist/src/core/cicd/state-manager.d.ts.map +1 -1
  35. package/dist/src/core/cicd/state-manager.js +60 -15
  36. package/dist/src/core/cicd/state-manager.js.map +1 -1
  37. package/dist/src/core/cost-tracker.d.ts.map +1 -1
  38. package/dist/src/core/cost-tracker.js +2 -1
  39. package/dist/src/core/cost-tracker.js.map +1 -1
  40. package/dist/src/core/iac/template-engine.d.ts.map +1 -1
  41. package/dist/src/core/iac/template-engine.js +28 -0
  42. package/dist/src/core/iac/template-engine.js.map +1 -1
  43. package/dist/src/core/iac/template-generator.d.ts +53 -0
  44. package/dist/src/core/iac/template-generator.d.ts.map +1 -0
  45. package/dist/src/core/iac/template-generator.js +125 -0
  46. package/dist/src/core/iac/template-generator.js.map +1 -0
  47. package/dist/src/core/increment/status-auto-transition.js +3 -3
  48. package/dist/src/core/increment/status-auto-transition.js.map +1 -1
  49. package/dist/src/core/living-docs/CodeValidator.js +1 -1
  50. package/dist/src/core/living-docs/CodeValidator.js.map +1 -1
  51. package/dist/src/core/living-docs/content-distributor.d.ts.map +1 -1
  52. package/dist/src/core/living-docs/content-distributor.js +11 -1
  53. package/dist/src/core/living-docs/content-distributor.js.map +1 -1
  54. package/dist/src/core/living-docs/living-docs-sync.d.ts +166 -0
  55. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -0
  56. package/dist/src/core/living-docs/living-docs-sync.js +726 -0
  57. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -0
  58. package/dist/src/core/living-docs/task-project-specific-generator.d.ts +7 -3
  59. package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -1
  60. package/dist/src/core/living-docs/task-project-specific-generator.js +40 -24
  61. package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -1
  62. package/dist/src/core/plugin-loader.d.ts +7 -0
  63. package/dist/src/core/plugin-loader.d.ts.map +1 -1
  64. package/dist/src/core/plugin-loader.js +18 -1
  65. package/dist/src/core/plugin-loader.js.map +1 -1
  66. package/dist/src/core/serverless/platform-data-loader.d.ts +8 -0
  67. package/dist/src/core/serverless/platform-data-loader.d.ts.map +1 -1
  68. package/dist/src/core/serverless/platform-data-loader.js +14 -0
  69. package/dist/src/core/serverless/platform-data-loader.js.map +1 -1
  70. package/dist/src/core/serverless/types.d.ts +1 -1
  71. package/dist/src/core/serverless/types.d.ts.map +1 -1
  72. package/dist/src/core/status-line/status-line-manager.d.ts +6 -2
  73. package/dist/src/core/status-line/status-line-manager.d.ts.map +1 -1
  74. package/dist/src/core/status-line/status-line-manager.js +11 -5
  75. package/dist/src/core/status-line/status-line-manager.js.map +1 -1
  76. package/dist/src/core/status-line/task-counter.d.ts +69 -0
  77. package/dist/src/core/status-line/task-counter.d.ts.map +1 -0
  78. package/dist/src/core/status-line/task-counter.js +107 -0
  79. package/dist/src/core/status-line/task-counter.js.map +1 -0
  80. package/dist/src/core/workflow/autonomous-executor.d.ts +111 -0
  81. package/dist/src/core/workflow/autonomous-executor.d.ts.map +1 -0
  82. package/dist/src/core/workflow/autonomous-executor.js +275 -0
  83. package/dist/src/core/workflow/autonomous-executor.js.map +1 -0
  84. package/dist/src/core/workflow/backlog-scanner.d.ts +94 -0
  85. package/dist/src/core/workflow/backlog-scanner.d.ts.map +1 -0
  86. package/dist/src/core/workflow/backlog-scanner.js +170 -0
  87. package/dist/src/core/workflow/backlog-scanner.js.map +1 -0
  88. package/dist/src/core/workflow/command-invoker.d.ts +86 -0
  89. package/dist/src/core/workflow/command-invoker.d.ts.map +1 -0
  90. package/dist/src/core/workflow/command-invoker.js +131 -0
  91. package/dist/src/core/workflow/command-invoker.js.map +1 -0
  92. package/dist/src/core/workflow/cost-estimator.d.ts +120 -0
  93. package/dist/src/core/workflow/cost-estimator.d.ts.map +1 -0
  94. package/dist/src/core/workflow/cost-estimator.js +222 -0
  95. package/dist/src/core/workflow/cost-estimator.js.map +1 -0
  96. package/dist/src/core/workflow/index.d.ts +20 -0
  97. package/dist/src/core/workflow/index.d.ts.map +1 -0
  98. package/dist/src/core/workflow/index.js +24 -0
  99. package/dist/src/core/workflow/index.js.map +1 -0
  100. package/dist/src/core/workflow/state-manager.d.ts +107 -0
  101. package/dist/src/core/workflow/state-manager.d.ts.map +1 -0
  102. package/dist/src/core/workflow/state-manager.js +126 -0
  103. package/dist/src/core/workflow/state-manager.js.map +1 -0
  104. package/dist/src/core/workflow/workflow-orchestrator.d.ts +93 -0
  105. package/dist/src/core/workflow/workflow-orchestrator.d.ts.map +1 -0
  106. package/dist/src/core/workflow/workflow-orchestrator.js +195 -0
  107. package/dist/src/core/workflow/workflow-orchestrator.js.map +1 -0
  108. package/dist/src/init/architecture/types.d.ts +10 -10
  109. package/dist/src/metrics/dora-calculator.js +2 -2
  110. package/dist/src/metrics/dora-calculator.js.map +1 -1
  111. package/dist/src/utils/pricing-constants.d.ts +5 -2
  112. package/dist/src/utils/pricing-constants.d.ts.map +1 -1
  113. package/dist/src/utils/pricing-constants.js +3 -2
  114. package/dist/src/utils/pricing-constants.js.map +1 -1
  115. package/package.json +4 -4
  116. package/plugins/specweave/agents/infrastructure/AGENT.md +88 -46
  117. package/plugins/specweave/agents/pm/AGENT.md +58 -1
  118. package/plugins/specweave/commands/specweave-archive-features.md +1 -1
  119. package/plugins/specweave/commands/specweave-archive-increments.md +1 -1
  120. package/plugins/specweave/commands/specweave-check-hooks.md +5 -0
  121. package/plugins/specweave/commands/specweave-done.md +12 -0
  122. package/plugins/specweave/commands/specweave-plan.md +1 -1
  123. package/plugins/specweave/commands/specweave-progress.md +108 -379
  124. package/plugins/specweave/commands/specweave-reopen.md +1 -1
  125. package/plugins/specweave/commands/specweave-restore-feature.md +1 -1
  126. package/plugins/specweave/commands/specweave-sync-specs.md +20 -48
  127. package/plugins/specweave/hooks/lib/update-status-line.sh +44 -35
  128. package/plugins/specweave/hooks/lib/validate-spec-status.sh +163 -0
  129. package/plugins/specweave/hooks/user-prompt-submit.sh +17 -35
  130. package/plugins/specweave/lib/hooks/update-tasks-md.js +52 -9
  131. package/plugins/specweave/lib/hooks/update-tasks-md.ts +77 -16
  132. package/plugins/specweave/templates/iac/aws-lambda/defaults.json +24 -0
  133. package/plugins/specweave/templates/iac/aws-lambda/templates/README.md.hbs +260 -0
  134. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/dev.tfvars.hbs +34 -0
  135. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/prod.tfvars.hbs +37 -0
  136. package/plugins/specweave/templates/iac/aws-lambda/templates/environments/staging.tfvars.hbs +35 -0
  137. package/plugins/specweave/templates/iac/aws-lambda/templates/outputs.tf.hbs +77 -0
  138. package/plugins/specweave/templates/iac/aws-lambda/templates/providers.tf.hbs +36 -0
  139. package/plugins/specweave/templates/iac/aws-lambda/templates/variables.tf.hbs +115 -0
  140. package/plugins/specweave/templates/iac/azure-functions/defaults.json +25 -0
  141. package/plugins/specweave/templates/iac/azure-functions/templates/README.md.hbs +268 -0
  142. package/plugins/specweave/templates/iac/azure-functions/templates/environments/dev.tfvars.hbs +34 -0
  143. package/plugins/specweave/templates/iac/azure-functions/templates/environments/prod.tfvars.hbs +46 -0
  144. package/plugins/specweave/templates/iac/azure-functions/templates/environments/staging.tfvars.hbs +34 -0
  145. package/plugins/specweave/templates/iac/azure-functions/templates/main.tf.hbs +225 -0
  146. package/plugins/specweave/templates/iac/azure-functions/templates/outputs.tf.hbs +89 -0
  147. package/plugins/specweave/templates/iac/azure-functions/templates/provider.tf.hbs +27 -0
  148. package/plugins/specweave/templates/iac/azure-functions/templates/providers.tf.hbs +35 -0
  149. package/plugins/specweave/templates/iac/azure-functions/templates/variables.tf.hbs +124 -0
  150. package/plugins/specweave/templates/iac/firebase/defaults.json +29 -0
  151. package/plugins/specweave/templates/iac/firebase/templates/README.md.hbs +35 -0
  152. package/plugins/specweave/templates/iac/firebase/templates/environments/dev.tfvars.hbs +7 -0
  153. package/plugins/specweave/templates/iac/firebase/templates/environments/prod.tfvars.hbs +7 -0
  154. package/plugins/specweave/templates/iac/firebase/templates/environments/staging.tfvars.hbs +7 -0
  155. package/plugins/specweave/templates/iac/firebase/templates/main.tf.hbs +90 -0
  156. package/plugins/specweave/templates/iac/firebase/templates/outputs.tf.hbs +15 -0
  157. package/plugins/specweave/templates/iac/firebase/templates/providers.tf.hbs +23 -0
  158. package/plugins/specweave/templates/iac/firebase/templates/variables.tf.hbs +42 -0
  159. package/plugins/specweave/templates/iac/gcp-cloud-functions/defaults.json +26 -0
  160. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/README.md.hbs +299 -0
  161. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/dev.tfvars.hbs +36 -0
  162. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/prod.tfvars.hbs +48 -0
  163. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/environments/staging.tfvars.hbs +41 -0
  164. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/main.tf.hbs +192 -0
  165. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/outputs.tf.hbs +66 -0
  166. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/providers.tf.hbs +25 -0
  167. package/plugins/specweave/templates/iac/gcp-cloud-functions/templates/variables.tf.hbs +119 -0
  168. package/plugins/specweave/templates/iac/supabase/defaults.json +15 -0
  169. package/plugins/specweave/templates/iac/supabase/templates/README.md.hbs +46 -0
  170. package/plugins/specweave/templates/iac/supabase/templates/main.tf.hbs +50 -0
  171. package/plugins/specweave-github/agents/github-manager/AGENT.md +39 -7
  172. package/plugins/specweave-github/commands/specweave-github-create-issue.md +5 -5
  173. package/plugins/specweave-github/lib/CodeValidator.ts +1 -1
  174. package/plugins/specweave-github/lib/github-client-v2.js +29 -0
  175. package/plugins/specweave-github/lib/github-client-v2.ts +30 -0
  176. package/plugins/specweave-github/lib/task-sync.js +4 -0
  177. package/plugins/specweave-github/lib/task-sync.ts +7 -0
  178. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  179. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +2022 -0
  180. package/src/templates/CLAUDE.md.template +31 -0
  181. package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts +0 -116
  182. package/dist/src/core/living-docs/ThreeLayerSyncManager.d.ts.map +0 -1
  183. package/dist/src/core/living-docs/ThreeLayerSyncManager.js +0 -356
  184. package/dist/src/core/living-docs/ThreeLayerSyncManager.js.map +0 -1
@@ -6,9 +6,9 @@
6
6
  # Shows: [increment-name] ████░░░░ X/Y tasks (Z open)
7
7
  #
8
8
  # Logic:
9
- # 1. Scan all metadata.json for status=active/in-progress/planning
9
+ # 1. Scan all spec.md for status=active/planning (SOURCE OF TRUTH!)
10
10
  # 2. Take first (oldest) as current increment
11
- # 3. Count all active/in-progress/planning as openCount
11
+ # 3. Count all active/planning as openCount
12
12
  # 4. Parse current increment's tasks.md for progress
13
13
  # 5. Write to cache
14
14
  #
@@ -37,19 +37,24 @@ TMP_FILE="$PROJECT_ROOT/.specweave/state/.status-line-tmp.txt"
37
37
  # Ensure state directory exists
38
38
  mkdir -p "$PROJECT_ROOT/.specweave/state"
39
39
 
40
- # Step 1: Find all open increments (active/in-progress/planning)
40
+ # Step 1: Find all open increments (active/planning)
41
+ # Read from spec.md (source of truth), not metadata.json
42
+ # ONLY accepts official IncrementStatus enum values
41
43
  # Write to temp file: "timestamp increment_id"
42
44
  > "$TMP_FILE"
43
45
 
44
46
  if [[ -d "$INCREMENTS_DIR" ]]; then
45
- for metadata in "$INCREMENTS_DIR"/*/metadata.json; do
46
- if [[ -f "$metadata" ]]; then
47
- status=$(jq -r '.status // ""' "$metadata" 2>/dev/null || echo "")
48
-
49
- # Check if increment is open (active, in-progress, or planning)
50
- if [[ "$status" == "active" ]] || [[ "$status" == "in-progress" ]] || [[ "$status" == "planning" ]]; then
51
- increment_id=$(basename "$(dirname "$metadata")")
52
- created=$(jq -r '.created // ""' "$metadata" 2>/dev/null || echo "1970-01-01T00:00:00Z")
47
+ for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
48
+ if [[ -f "$spec_file" ]]; then
49
+ # Parse YAML frontmatter for status (source of truth)
50
+ status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' ' || echo "")
51
+
52
+ # Check if increment is open (active, planning, or in-progress)
53
+ # ONLY accepts official IncrementStatus enum values
54
+ if [[ "$status" == "active" ]] || [[ "$status" == "planning" ]] || [[ "$status" == "in-progress" ]]; then
55
+ increment_id=$(basename "$(dirname "$spec_file")")
56
+ # Parse created date from spec.md YAML frontmatter
57
+ created=$(grep -m1 "^created:" "$spec_file" 2>/dev/null | cut -d: -f2- | tr -d ' ' || echo "1970-01-01")
53
58
 
54
59
  # Write to temp file
55
60
  echo "$created $increment_id" >> "$TMP_FILE"
@@ -85,33 +90,37 @@ COMPLETED_TASKS=0
85
90
  PERCENTAGE=0
86
91
 
87
92
  if [[ -f "$TASKS_FILE" ]]; then
88
- # Count total tasks (## T- or ### T- headings)
89
- TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
90
- TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
91
-
92
- # Count completed tasks (multiple formats)
93
- # Format 1: [x] at line start (legacy)
94
- COMPLETED_STANDARD=$(grep -c '^\[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
95
- COMPLETED_STANDARD=$(echo "$COMPLETED_STANDARD" | tr -d '\n\r ' || echo 0)
96
-
97
- # Format 2: **Status**: [x] inline (legacy)
98
- COMPLETED_INLINE=$(grep -c '\*\*Status\*\*: \[x\]' "$TASKS_FILE" 2>/dev/null || echo 0)
99
- COMPLETED_INLINE=$(echo "$COMPLETED_INLINE" | tr -d '\n\r ' || echo 0)
100
-
101
- # Format 3: **Completed**: <date> (current format)
102
- COMPLETED_DATE=$(grep -c '\*\*Completed\*\*:' "$TASKS_FILE" 2>/dev/null || echo 0)
103
- COMPLETED_DATE=$(echo "$COMPLETED_DATE" | tr -d '\n\r ' || echo 0)
104
-
105
- COMPLETED_TASKS=$((COMPLETED_STANDARD + COMPLETED_INLINE + COMPLETED_DATE))
106
-
107
- # Calculate percentage
108
- if [[ $TOTAL_TASKS -gt 0 ]]; then
109
- PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
93
+ # Use TaskCounter CLI for accurate counting (fixes overcounting bug)
94
+ COUNT_TASKS_CLI="$PROJECT_ROOT/dist/src/cli/count-tasks.js"
95
+
96
+ if [[ -f "$COUNT_TASKS_CLI" ]]; then
97
+ # Call CLI and parse JSON output
98
+ TASK_COUNTS=$(node "$COUNT_TASKS_CLI" "$TASKS_FILE" 2>/dev/null || echo '{"total":0,"completed":0,"percentage":0}')
99
+ TOTAL_TASKS=$(echo "$TASK_COUNTS" | jq -r '.total' 2>/dev/null || echo 0)
100
+ COMPLETED_TASKS=$(echo "$TASK_COUNTS" | jq -r '.completed' 2>/dev/null || echo 0)
101
+ PERCENTAGE=$(echo "$TASK_COUNTS" | jq -r '.percentage' 2>/dev/null || echo 0)
102
+ else
103
+ # Fallback to legacy counting if CLI not available (graceful degradation)
104
+ # Count total tasks (## T- or ### T- headings)
105
+ TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
106
+ TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
107
+
108
+ # Count completed tasks - use most reliable single marker (**Completed**: format)
109
+ COMPLETED_TASKS=$(grep -c '\*\*Completed\*\*:' "$TASKS_FILE" 2>/dev/null || echo 0)
110
+ COMPLETED_TASKS=$(echo "$COMPLETED_TASKS" | tr -d '\n\r ' || echo 0)
111
+
112
+ # Calculate percentage
113
+ if [[ $TOTAL_TASKS -gt 0 ]]; then
114
+ PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
115
+ fi
110
116
  fi
111
117
  fi
112
118
 
113
- # Step 5: Extract increment name (remove 4-digit prefix)
114
- INCREMENT_NAME=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
119
+ # Step 5: Extract increment ID and name
120
+ # Format: XXXX-name where XXXX is 4-digit prefix (brackets added by manager)
121
+ INCREMENT_ID=$(echo "$CURRENT_INCREMENT" | grep -oE '^[0-9]{4}')
122
+ INCREMENT_NAME_ONLY=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
123
+ INCREMENT_NAME="$INCREMENT_ID-$INCREMENT_NAME_ONLY"
115
124
 
116
125
  # Step 6: Write cache
117
126
  jq -n \
@@ -0,0 +1,163 @@
1
+ #!/bin/bash
2
+ #
3
+ # validate-spec-status.sh
4
+ #
5
+ # Validates that spec.md status values match IncrementStatus enum.
6
+ # Prevents vocabulary drift and ensures status line hook compatibility.
7
+ #
8
+ # Usage:
9
+ # bash validate-spec-status.sh <increment-id>
10
+ # bash validate-spec-status.sh --all # Validate all increments
11
+ #
12
+ # Exit codes:
13
+ # 0 = Valid
14
+ # 1 = Invalid status found
15
+ # 2 = Error (file not found, etc.)
16
+ #
17
+ # Requires: bash 4.0+ (for associative arrays)
18
+
19
+ set -euo pipefail
20
+
21
+ # Find project root
22
+ find_project_root() {
23
+ local dir="$PWD"
24
+ while [[ "$dir" != "/" ]]; do
25
+ if [[ -d "$dir/.specweave" ]]; then
26
+ echo "$dir"
27
+ return 0
28
+ fi
29
+ dir=$(dirname "$dir")
30
+ done
31
+ echo "$PWD"
32
+ }
33
+
34
+ PROJECT_ROOT=$(find_project_root)
35
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
36
+
37
+ # Valid IncrementStatus enum values (from src/core/types/increment-metadata.ts)
38
+ VALID_STATUSES=("planning" "active" "backlog" "paused" "completed" "abandoned")
39
+
40
+ # Get suggested correction for invalid status
41
+ get_correction() {
42
+ local status="$1"
43
+ case "$status" in
44
+ "planned") echo "planning" ;;
45
+ "in-progress"|"in_progress") echo "active" ;;
46
+ "done") echo "completed" ;;
47
+ "cancelled"|"canceled") echo "abandoned" ;;
48
+ *) echo "" ;;
49
+ esac
50
+ }
51
+
52
+ # Validate a single spec file
53
+ validate_spec() {
54
+ local spec_file="$1"
55
+ local increment_id=$(basename "$(dirname "$spec_file")")
56
+
57
+ if [[ ! -f "$spec_file" ]]; then
58
+ echo "❌ Error: spec.md not found for increment $increment_id"
59
+ return 2
60
+ fi
61
+
62
+ # Extract status from YAML frontmatter
63
+ local status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' "' || echo "")
64
+
65
+ if [[ -z "$status" ]]; then
66
+ echo "⚠️ Warning: No status field in $increment_id/spec.md"
67
+ return 0 # Not an error, just a warning
68
+ fi
69
+
70
+ # Check if status is valid
71
+ local is_valid=0
72
+ for valid_status in "${VALID_STATUSES[@]}"; do
73
+ if [[ "$status" == "$valid_status" ]]; then
74
+ is_valid=1
75
+ break
76
+ fi
77
+ done
78
+
79
+ if [[ $is_valid -eq 1 ]]; then
80
+ echo "✅ $increment_id: status '$status' is valid"
81
+ return 0
82
+ else
83
+ # Invalid status found - suggest correction
84
+ echo ""
85
+ echo "❌ Invalid status in $increment_id/spec.md"
86
+ echo " Found: '$status'"
87
+ echo ""
88
+
89
+ # Check if we have a suggested correction
90
+ local correction=$(get_correction "$status")
91
+ if [[ -n "$correction" ]]; then
92
+ echo " 💡 Did you mean: '$correction'?"
93
+ echo ""
94
+ echo " Fix:"
95
+ echo " sed -i '' 's/^status: $status/status: $correction/' $spec_file"
96
+ else
97
+ echo " Valid statuses: ${VALID_STATUSES[*]}"
98
+ fi
99
+ echo ""
100
+
101
+ return 1
102
+ fi
103
+ }
104
+
105
+ # Validate all increments
106
+ validate_all() {
107
+ local exit_code=0
108
+ local total=0
109
+ local valid=0
110
+ local invalid=0
111
+
112
+ echo "Validating all increments in $INCREMENTS_DIR"
113
+ echo ""
114
+
115
+ for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
116
+ if [[ -f "$spec_file" ]]; then
117
+ total=$((total + 1))
118
+ if validate_spec "$spec_file"; then
119
+ valid=$((valid + 1))
120
+ else
121
+ invalid=$((invalid + 1))
122
+ exit_code=1
123
+ fi
124
+ fi
125
+ done
126
+
127
+ echo ""
128
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
129
+ echo "Summary: $total increments checked"
130
+ echo " ✅ Valid: $valid"
131
+ echo " ❌ Invalid: $invalid"
132
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
133
+
134
+ if [[ $invalid -gt 0 ]]; then
135
+ echo ""
136
+ echo "⚠️ Please fix invalid status values to use official IncrementStatus enum."
137
+ echo ""
138
+ echo "Valid statuses:"
139
+ for status in "${VALID_STATUSES[@]}"; do
140
+ echo " - $status"
141
+ done
142
+ fi
143
+
144
+ return $exit_code
145
+ }
146
+
147
+ # Main logic
148
+ if [[ $# -eq 0 ]]; then
149
+ echo "Usage: $0 <increment-id> | --all"
150
+ echo ""
151
+ echo "Examples:"
152
+ echo " $0 0042-test-infrastructure-cleanup"
153
+ echo " $0 --all"
154
+ exit 2
155
+ fi
156
+
157
+ if [[ "$1" == "--all" ]]; then
158
+ validate_all
159
+ else
160
+ INCREMENT_ID="$1"
161
+ SPEC_FILE="$INCREMENTS_DIR/$INCREMENT_ID/spec.md"
162
+ validate_spec "$SPEC_FILE"
163
+ fi
@@ -303,44 +303,26 @@ if [[ -d ".specweave/increments" ]]; then
303
303
  done)
304
304
 
305
305
  if [[ -n "$ACTIVE_INCREMENT" ]]; then
306
- # Use status line cache for consistent display
307
- STATUS_LINE_CACHE=".specweave/state/status-line.json"
308
- if [[ -f "$STATUS_LINE_CACHE" ]]; then
309
- # Get status line from cache (ultra-fast)
310
- STATUS_LINE=$(node -e "
311
- try {
312
- const { StatusLineManager } = require('./dist/src/core/status-line/status-line-manager.js');
313
- const manager = new StatusLineManager(process.cwd());
314
- const result = manager.render();
315
- console.log(result || '');
316
- } catch (e) {
317
- // Fallback: show increment name only
318
- console.log('');
319
- }
320
- " 2>/dev/null || echo "")
321
-
322
- if [[ -n "$STATUS_LINE" ]]; then
323
- CONTEXT="✓ $STATUS_LINE"
306
+ # Simple status: parse tasks.md for completion
307
+ TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
308
+ if [[ -f "$TASKS_FILE" ]]; then
309
+ # Count tasks (headers with T-NNN format - both ### and ####)
310
+ TOTAL_TASKS=$(grep -cE '^#{3,4}\s*T-[0-9]' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
311
+ # Count completed (various formats)
312
+ COMPLETED_TASKS=$(grep -cE '(✅ COMPLETE|\[COMPLETED\]|\[x\] Completed)' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
313
+
314
+ # Ensure valid numbers
315
+ TOTAL_TASKS=${TOTAL_TASKS:-0}
316
+ COMPLETED_TASKS=${COMPLETED_TASKS:-0}
317
+
318
+ if [[ "$TOTAL_TASKS" -gt 0 ]] 2>/dev/null; then
319
+ PERCENTAGE=$(( COMPLETED_TASKS * 100 / TOTAL_TASKS ))
320
+ CONTEXT=" Active: $ACTIVE_INCREMENT ($COMPLETED_TASKS/$TOTAL_TASKS tasks, $PERCENTAGE%)"
324
321
  else
325
- # Fallback: parse tasks.md manually
326
- TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
327
- if [[ -f "$TASKS_FILE" ]]; then
328
- TASK_STATS=$(grep -E "^\s*-\s*\[[ x]\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
329
- COMPLETED_TASKS=$(grep -E "^\s*-\s*\[x\]" "$TASKS_FILE" 2>/dev/null | wc -l | xargs || echo "0")
330
-
331
- if [[ "$TASK_STATS" -gt 0 ]]; then
332
- PERCENTAGE=$(( COMPLETED_TASKS * 100 / TASK_STATS ))
333
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT ($PERCENTAGE% complete, $COMPLETED_TASKS/$TASK_STATS tasks)"
334
- else
335
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
336
- fi
337
- else
338
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
339
- fi
322
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
340
323
  fi
341
324
  else
342
- # No cache, fall back to increment name only
343
- CONTEXT="✓ Active Increment: $ACTIVE_INCREMENT"
325
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
344
326
  fi
345
327
  fi
346
328
  fi
@@ -20,13 +20,22 @@ async function updateTasksMd(incrementId) {
20
20
  console.log(`\u{1F4D6} Read tasks.md (${lines.length} lines)`);
21
21
  const tasks = parseTaskStatus(lines);
22
22
  console.log(`\u{1F4CB} Found ${tasks.length} tasks`);
23
- const completedTasks = detectCompletedTasks(lines);
24
- if (completedTasks.length === 0) {
25
- console.log("\u2705 No new task completions detected");
23
+ const { completedTasks, fixes } = detectCompletedTasks(lines);
24
+ let updatedContent = originalContent;
25
+ let autoFixedCount = 0;
26
+ if (fixes.length > 0) {
27
+ console.log(`\u{1F527} Auto-fixing ${fixes.length} task consistency issue(s)...`);
28
+ updatedContent = applyConsistencyFixes(originalContent, fixes);
29
+ autoFixedCount = fixes.length;
30
+ console.log("\u2705 Task consistency auto-fixed");
31
+ }
32
+ if (completedTasks.length === 0 && fixes.length === 0) {
33
+ console.log("\u2705 No new task completions or consistency fixes needed");
26
34
  return;
27
35
  }
28
- console.log(`\u{1F3AF} Detected ${completedTasks.length} completed task(s):`, completedTasks);
29
- let updatedContent = originalContent;
36
+ if (completedTasks.length > 0) {
37
+ console.log(`\u{1F3AF} Detected ${completedTasks.length} completed task(s):`, completedTasks);
38
+ }
30
39
  for (const taskId of completedTasks) {
31
40
  updatedContent = markTaskComplete(updatedContent, taskId);
32
41
  }
@@ -38,6 +47,9 @@ async function updateTasksMd(incrementId) {
38
47
  updatedContent = updateProgressHeader(updatedContent, completedCount, totalTasks, progress);
39
48
  await fs.writeFile(tasksPath, updatedContent, "utf-8");
40
49
  console.log(`\u2705 Updated ${tasksPath}`);
50
+ if (autoFixedCount > 0) {
51
+ console.log(`\u{1F527} Auto-fixed ${autoFixedCount} consistency issue(s)`);
52
+ }
41
53
  console.log(` Completed: ${completedCount}/${totalTasks}`);
42
54
  console.log(` Progress: ${progress}%
43
55
  `);
@@ -78,6 +90,7 @@ function parseTaskStatus(lines) {
78
90
  }
79
91
  function detectCompletedTasks(lines) {
80
92
  const completedTasks = [];
93
+ const fixes = [];
81
94
  const warnings = [];
82
95
  const taskPattern = /^###\s+(T-\d+[-A-Z]*):?\s+(.+)/;
83
96
  for (let i = 0; i < lines.length; i++) {
@@ -99,17 +112,33 @@ function detectCompletedTasks(lines) {
99
112
  completedTasks.push(taskId);
100
113
  }
101
114
  } else {
115
+ fixes.push({
116
+ taskId,
117
+ lineNumber: i,
118
+ action: "remove-complete-marker",
119
+ currentLine: line
120
+ });
102
121
  warnings.push(`${taskId}: Header has \u2705 COMPLETE but not all checkboxes checked`);
103
122
  }
104
123
  continue;
105
124
  }
106
125
  if (hasCompleteMarker && !implementationSection) {
107
- if (!completedTasks.includes(taskId)) {
108
- completedTasks.push(taskId);
109
- }
126
+ fixes.push({
127
+ taskId,
128
+ lineNumber: i,
129
+ action: "remove-complete-marker",
130
+ currentLine: line
131
+ });
132
+ warnings.push(`${taskId}: Header has \u2705 COMPLETE but no implementation section to verify`);
110
133
  continue;
111
134
  }
112
135
  if (!hasCompleteMarker && implementationSection && allCheckboxesComplete) {
136
+ fixes.push({
137
+ taskId,
138
+ lineNumber: i,
139
+ action: "add-complete-marker",
140
+ currentLine: line
141
+ });
113
142
  warnings.push(`${taskId}: All checkboxes checked but header missing \u2705 COMPLETE`);
114
143
  if (!completedTasks.includes(taskId)) {
115
144
  completedTasks.push(taskId);
@@ -131,7 +160,21 @@ function detectCompletedTasks(lines) {
131
160
  warnings.forEach((w) => console.warn(` ${w}`));
132
161
  console.warn("");
133
162
  }
134
- return completedTasks;
163
+ return { completedTasks, fixes };
164
+ }
165
+ function applyConsistencyFixes(content, fixes) {
166
+ const lines = content.split("\n");
167
+ for (const fix of fixes.reverse()) {
168
+ const line = lines[fix.lineNumber];
169
+ if (fix.action === "remove-complete-marker") {
170
+ const fixed = line.replace(/\s*✅\s*COMPLETE\s*/g, "");
171
+ lines[fix.lineNumber] = fixed;
172
+ } else if (fix.action === "add-complete-marker") {
173
+ const fixed = line.trim() + " \u2705 COMPLETE";
174
+ lines[fix.lineNumber] = fixed;
175
+ }
176
+ }
177
+ return lines.join("\n");
135
178
  }
136
179
  function findNextTaskStart(lines, startIndex) {
137
180
  const taskPattern = /^###\s+T-\d+/;
@@ -32,6 +32,13 @@ interface TaskMatch {
32
32
  currentStatus: 'pending' | 'in_progress' | 'completed';
33
33
  }
34
34
 
35
+ interface TaskConsistencyFix {
36
+ taskId: string;
37
+ lineNumber: number;
38
+ action: 'add-complete-marker' | 'remove-complete-marker';
39
+ currentLine: string;
40
+ }
41
+
35
42
  /**
36
43
  * Main function - update tasks.md for given increment
37
44
  */
@@ -62,20 +69,30 @@ async function updateTasksMd(incrementId: string): Promise<void> {
62
69
  const tasks = parseTaskStatus(lines);
63
70
  console.log(`📋 Found ${tasks.length} tasks`);
64
71
 
65
- // 4. Get recently completed tasks from environment (passed by hook)
66
- // For now, we'll mark tasks as complete if they match a heuristic
67
- // TODO: In future, hook will pass completed task IDs via env var
68
- const completedTasks = detectCompletedTasks(lines);
72
+ // 4. Get recently completed tasks and consistency fixes
73
+ const { completedTasks, fixes } = detectCompletedTasks(lines);
69
74
 
70
- if (completedTasks.length === 0) {
71
- console.log('✅ No new task completions detected');
75
+ // 4a. Apply consistency fixes FIRST (if any)
76
+ let updatedContent = originalContent;
77
+ let autoFixedCount = 0;
78
+
79
+ if (fixes.length > 0) {
80
+ console.log(`🔧 Auto-fixing ${fixes.length} task consistency issue(s)...`);
81
+ updatedContent = applyConsistencyFixes(originalContent, fixes);
82
+ autoFixedCount = fixes.length;
83
+ console.log('✅ Task consistency auto-fixed');
84
+ }
85
+
86
+ if (completedTasks.length === 0 && fixes.length === 0) {
87
+ console.log('✅ No new task completions or consistency fixes needed');
72
88
  return;
73
89
  }
74
90
 
75
- console.log(`🎯 Detected ${completedTasks.length} completed task(s):`, completedTasks);
91
+ if (completedTasks.length > 0) {
92
+ console.log(`🎯 Detected ${completedTasks.length} completed task(s):`, completedTasks);
93
+ }
76
94
 
77
95
  // 5. Update task status in content
78
- let updatedContent = originalContent;
79
96
 
80
97
  for (const taskId of completedTasks) {
81
98
  updatedContent = markTaskComplete(updatedContent, taskId);
@@ -96,6 +113,9 @@ async function updateTasksMd(incrementId: string): Promise<void> {
96
113
  await fs.writeFile(tasksPath, updatedContent, 'utf-8');
97
114
 
98
115
  console.log(`✅ Updated ${tasksPath}`);
116
+ if (autoFixedCount > 0) {
117
+ console.log(`🔧 Auto-fixed ${autoFixedCount} consistency issue(s)`);
118
+ }
99
119
  console.log(` Completed: ${completedCount}/${totalTasks}`);
100
120
  console.log(` Progress: ${progress}%\n`);
101
121
 
@@ -158,8 +178,9 @@ function parseTaskStatus(lines: string[]): TaskMatch[] {
158
178
  *
159
179
  * @see .specweave/increments/0037/reports/ULTRATHINK-COMPLETE-MARKER-VS-CHECKBOXES.md
160
180
  */
161
- function detectCompletedTasks(lines: string[]): string[] {
181
+ function detectCompletedTasks(lines: string[]): { completedTasks: string[]; fixes: TaskConsistencyFix[] } {
162
182
  const completedTasks: string[] = [];
183
+ const fixes: TaskConsistencyFix[] = [];
163
184
  const warnings: string[] = [];
164
185
  const taskPattern = /^###\s+(T-\d+[-A-Z]*):?\s+(.+)/;
165
186
 
@@ -190,22 +211,38 @@ function detectCompletedTasks(lines: string[]): string[] {
190
211
  completedTasks.push(taskId);
191
212
  }
192
213
  } else {
193
- // ⚠️ INCONSISTENT: Header says COMPLETE but checkboxes incomplete
214
+ // ⚠️ INCONSISTENT: Header says COMPLETE but checkboxes incomplete - AUTO-FIX
215
+ fixes.push({
216
+ taskId,
217
+ lineNumber: i,
218
+ action: 'remove-complete-marker',
219
+ currentLine: line
220
+ });
194
221
  warnings.push(`${taskId}: Header has ✅ COMPLETE but not all checkboxes checked`);
195
222
  }
196
223
  continue;
197
224
  }
198
225
 
199
- // If no implementation section, trust the header marker
226
+ // If no implementation section, remove COMPLETE marker (can't verify)
200
227
  if (hasCompleteMarker && !implementationSection) {
201
- if (!completedTasks.includes(taskId)) {
202
- completedTasks.push(taskId);
203
- }
228
+ fixes.push({
229
+ taskId,
230
+ lineNumber: i,
231
+ action: 'remove-complete-marker',
232
+ currentLine: line
233
+ });
234
+ warnings.push(`${taskId}: Header has ✅ COMPLETE but no implementation section to verify`);
204
235
  continue;
205
236
  }
206
237
 
207
- // Warn if checkboxes all complete but header missing marker
238
+ // Warn if checkboxes all complete but header missing marker - AUTO-FIX
208
239
  if (!hasCompleteMarker && implementationSection && allCheckboxesComplete) {
240
+ fixes.push({
241
+ taskId,
242
+ lineNumber: i,
243
+ action: 'add-complete-marker',
244
+ currentLine: line
245
+ });
209
246
  warnings.push(`${taskId}: All checkboxes checked but header missing ✅ COMPLETE`);
210
247
  // Still count as complete (checkboxes are source of truth for work done)
211
248
  if (!completedTasks.includes(taskId)) {
@@ -233,7 +270,31 @@ function detectCompletedTasks(lines: string[]): string[] {
233
270
  console.warn('');
234
271
  }
235
272
 
236
- return completedTasks;
273
+ return { completedTasks, fixes };
274
+ }
275
+
276
+ /**
277
+ * Apply task consistency fixes to content
278
+ */
279
+ function applyConsistencyFixes(content: string, fixes: TaskConsistencyFix[]): string {
280
+ const lines = content.split('\n');
281
+
282
+ // Apply fixes (process in reverse order to preserve line numbers)
283
+ for (const fix of fixes.reverse()) {
284
+ const line = lines[fix.lineNumber];
285
+
286
+ if (fix.action === 'remove-complete-marker') {
287
+ // Remove ✅ COMPLETE from header
288
+ const fixed = line.replace(/\s*✅\s*COMPLETE\s*/g, '');
289
+ lines[fix.lineNumber] = fixed;
290
+ } else if (fix.action === 'add-complete-marker') {
291
+ // Add ✅ COMPLETE to header (before any trailing spaces)
292
+ const fixed = line.trim() + ' ✅ COMPLETE';
293
+ lines[fix.lineNumber] = fixed;
294
+ }
295
+ }
296
+
297
+ return lines.join('\n');
237
298
  }
238
299
 
239
300
  /**
@@ -0,0 +1,24 @@
1
+ {
2
+ "region": "us-east-1",
3
+ "functionName": "my-lambda-function",
4
+ "runtime": "nodejs20.x",
5
+ "handler": "index.handler",
6
+ "memorySize": 128,
7
+ "timeout": 30,
8
+ "environment": "dev",
9
+ "databaseName": "my-dynamodb-table",
10
+ "primaryKey": "id",
11
+ "apiName": "my-api",
12
+ "apiStageName": "default",
13
+ "logRetentionDays": 90,
14
+ "corsOrigins": ["*"],
15
+ "projectName": "my-project",
16
+ "enableVpc": false,
17
+ "enableXray": false,
18
+ "enableSecretsManager": false,
19
+ "enableKms": false,
20
+ "enableEncryption": false,
21
+ "enablePointInTimeRecovery": false,
22
+ "enableStreams": false,
23
+ "enableRemoteState": false
24
+ }