specweave 0.32.0 → 0.32.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 (191) hide show
  1. package/CLAUDE.md +176 -2
  2. package/README.md +22 -0
  3. package/bin/specweave.js +18 -1
  4. package/dist/src/cli/commands/cache.d.ts +17 -0
  5. package/dist/src/cli/commands/cache.d.ts.map +1 -0
  6. package/dist/src/cli/commands/cache.js +126 -0
  7. package/dist/src/cli/commands/cache.js.map +1 -0
  8. package/dist/src/cli/commands/init.js +1 -1
  9. package/dist/src/cli/commands/init.js.map +1 -1
  10. package/dist/src/cli/commands/plan/increment-detector.js +2 -2
  11. package/dist/src/cli/commands/plan/increment-detector.js.map +1 -1
  12. package/dist/src/cli/commands/sync-spec-commits.js +1 -1
  13. package/dist/src/cli/commands/sync-spec-commits.js.map +1 -1
  14. package/dist/src/cli/commands/sync-specs.js +2 -2
  15. package/dist/src/cli/commands/sync-specs.js.map +1 -1
  16. package/dist/src/cli/helpers/github/increment-profile-selector.js +1 -1
  17. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  18. package/dist/src/cli/workers/living-docs-worker.js +66 -1
  19. package/dist/src/cli/workers/living-docs-worker.js.map +1 -1
  20. package/dist/src/config/types.d.ts +203 -1208
  21. package/dist/src/config/types.d.ts.map +1 -1
  22. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -1
  23. package/dist/src/core/discrepancy/increment-generator.js +5 -2
  24. package/dist/src/core/discrepancy/increment-generator.js.map +1 -1
  25. package/dist/src/core/increment/duplicate-detector.js +2 -2
  26. package/dist/src/core/increment/duplicate-detector.js.map +1 -1
  27. package/dist/src/core/increment/increment-archiver.d.ts +24 -0
  28. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  29. package/dist/src/core/increment/increment-archiver.js +59 -2
  30. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  31. package/dist/src/core/increment/increment-status.js +2 -2
  32. package/dist/src/core/increment/increment-status.js.map +1 -1
  33. package/dist/src/core/increment/increment-utils.d.ts +85 -0
  34. package/dist/src/core/increment/increment-utils.d.ts.map +1 -1
  35. package/dist/src/core/increment/increment-utils.js +102 -4
  36. package/dist/src/core/increment/increment-utils.js.map +1 -1
  37. package/dist/src/core/increment/metadata-validator.js +1 -1
  38. package/dist/src/core/increment/metadata-validator.js.map +1 -1
  39. package/dist/src/core/living-docs/feature-id-manager.js +1 -1
  40. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  41. package/dist/src/core/living-docs/hierarchy-mapper.js +3 -3
  42. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  43. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts +18 -0
  44. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.d.ts.map +1 -0
  45. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js +247 -0
  46. package/dist/src/core/living-docs/intelligent-analyzer/architecture-generator.js.map +1 -0
  47. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts +15 -0
  48. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.d.ts.map +1 -0
  49. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js +138 -0
  50. package/dist/src/core/living-docs/intelligent-analyzer/deep-repo-analyzer.js.map +1 -0
  51. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts +24 -0
  52. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.d.ts.map +1 -0
  53. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js +198 -0
  54. package/dist/src/core/living-docs/intelligent-analyzer/file-sampler.js.map +1 -0
  55. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts +17 -0
  56. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.d.ts.map +1 -0
  57. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js +241 -0
  58. package/dist/src/core/living-docs/intelligent-analyzer/inconsistency-detector.js.map +1 -0
  59. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts +28 -0
  60. package/dist/src/core/living-docs/intelligent-analyzer/index.d.ts.map +1 -0
  61. package/dist/src/core/living-docs/intelligent-analyzer/index.js +197 -0
  62. package/dist/src/core/living-docs/intelligent-analyzer/index.js.map +1 -0
  63. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts +18 -0
  64. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.d.ts.map +1 -0
  65. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +154 -0
  66. package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js.map +1 -0
  67. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts +42 -0
  68. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.d.ts.map +1 -0
  69. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js +343 -0
  70. package/dist/src/core/living-docs/intelligent-analyzer/strategy-generator.js.map +1 -0
  71. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts +146 -0
  72. package/dist/src/core/living-docs/intelligent-analyzer/types.d.ts.map +1 -0
  73. package/dist/src/core/living-docs/intelligent-analyzer/types.js +7 -0
  74. package/dist/src/core/living-docs/intelligent-analyzer/types.js.map +1 -0
  75. package/dist/src/core/living-docs/living-docs-sync.d.ts +5 -0
  76. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  77. package/dist/src/core/living-docs/living-docs-sync.js +36 -2
  78. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  79. package/dist/src/core/sync/spec-increment-mapper.js +3 -3
  80. package/dist/src/core/sync/spec-increment-mapper.js.map +1 -1
  81. package/dist/src/importers/item-converter.d.ts +25 -0
  82. package/dist/src/importers/item-converter.d.ts.map +1 -1
  83. package/dist/src/importers/item-converter.js +135 -5
  84. package/dist/src/importers/item-converter.js.map +1 -1
  85. package/dist/src/init/architecture/types.d.ts +33 -140
  86. package/dist/src/init/architecture/types.d.ts.map +1 -1
  87. package/dist/src/init/compliance/types.d.ts +30 -27
  88. package/dist/src/init/compliance/types.d.ts.map +1 -1
  89. package/dist/src/init/repo/types.d.ts +11 -34
  90. package/dist/src/init/repo/types.d.ts.map +1 -1
  91. package/dist/src/init/research/src/config/types.d.ts +15 -82
  92. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  93. package/dist/src/init/research/types.d.ts +38 -93
  94. package/dist/src/init/research/types.d.ts.map +1 -1
  95. package/dist/src/init/team/types.d.ts +4 -42
  96. package/dist/src/init/team/types.d.ts.map +1 -1
  97. package/dist/src/types/dashboard-cache.d.ts +181 -0
  98. package/dist/src/types/dashboard-cache.d.ts.map +1 -0
  99. package/dist/src/types/dashboard-cache.js +65 -0
  100. package/dist/src/types/dashboard-cache.js.map +1 -0
  101. package/dist/src/utils/docs-validator.d.ts +131 -0
  102. package/dist/src/utils/docs-validator.d.ts.map +1 -0
  103. package/dist/src/utils/docs-validator.js +529 -0
  104. package/dist/src/utils/docs-validator.js.map +1 -0
  105. package/dist/src/utils/feature-id-collision.js +1 -1
  106. package/dist/src/utils/feature-id-collision.js.map +1 -1
  107. package/dist/src/utils/html-to-mdx.d.ts +1 -0
  108. package/dist/src/utils/html-to-mdx.d.ts.map +1 -1
  109. package/dist/src/utils/html-to-mdx.js +43 -5
  110. package/dist/src/utils/html-to-mdx.js.map +1 -1
  111. package/package.json +1 -1
  112. package/plugins/specweave/agents/pm/AGENT.md +10 -7
  113. package/plugins/specweave/commands/specweave-archive-features.md +5 -7
  114. package/plugins/specweave/commands/specweave-archive.md +2 -1
  115. package/plugins/specweave/commands/specweave-do.md +35 -1
  116. package/plugins/specweave/commands/specweave-done.md +96 -0
  117. package/plugins/specweave/commands/specweave-import-external.md +45 -18
  118. package/plugins/specweave/commands/specweave-increment.md +331 -33
  119. package/plugins/specweave/commands/specweave-jobs.md +2 -2
  120. package/plugins/specweave/commands/specweave-progress.md +4 -4
  121. package/plugins/specweave/commands/specweave-restore-feature.md +5 -4
  122. package/plugins/specweave/commands/specweave-sync-docs.md +1 -1
  123. package/plugins/specweave/commands/specweave-sync-specs.md +216 -322
  124. package/plugins/specweave/commands/specweave-validate-features.md +13 -8
  125. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  126. package/plugins/specweave/hooks/hooks.json +33 -4
  127. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  128. package/plugins/specweave/hooks/lib/common-setup.sh +375 -0
  129. package/plugins/specweave/hooks/lib/crash-prevention.sh +336 -0
  130. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  131. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  132. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  133. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  134. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  135. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  136. package/plugins/specweave/hooks/post-task-completion.sh +4 -23
  137. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  138. package/plugins/specweave/hooks/pre-command-deduplication.sh +1 -6
  139. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  140. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  141. package/plugins/specweave/hooks/pre-task-completion.sh +8 -37
  142. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  143. package/plugins/specweave/hooks/pre-tool-use.sh +2 -11
  144. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  145. package/plugins/specweave/hooks/universal/dispatcher.mjs +135 -42
  146. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +183 -0
  147. package/plugins/specweave/hooks/user-prompt-submit.sh +140 -38
  148. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  149. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +12 -0
  150. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +89 -0
  151. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +211 -0
  152. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +163 -0
  153. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +26 -28
  154. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +50 -0
  155. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +2 -2
  156. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -1
  157. package/plugins/specweave/scripts/README.md +166 -0
  158. package/plugins/specweave/scripts/cleanup-state.sh +142 -0
  159. package/plugins/specweave/scripts/force-kill.sh +142 -0
  160. package/plugins/specweave/scripts/jobs.js +171 -0
  161. package/plugins/specweave/scripts/progress.js +170 -0
  162. package/plugins/specweave/scripts/read-costs.sh +132 -0
  163. package/plugins/specweave/scripts/read-jobs.sh +324 -0
  164. package/plugins/specweave/scripts/read-progress.sh +185 -0
  165. package/plugins/specweave/scripts/read-status.sh +146 -0
  166. package/plugins/specweave/scripts/read-workflow.sh +173 -0
  167. package/plugins/specweave/scripts/rebuild-dashboard-cache.sh +327 -0
  168. package/plugins/specweave/scripts/session-watchdog.sh +192 -0
  169. package/plugins/specweave/scripts/status.js +154 -0
  170. package/plugins/specweave/scripts/update-dashboard-cache.sh +281 -0
  171. package/plugins/specweave/skills/increment-planner/SKILL.md +333 -24
  172. package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +17 -9
  173. package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +6 -2
  174. package/plugins/specweave/skills/instant-status/SKILL.md +70 -0
  175. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  176. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  177. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  178. package/plugins/specweave-docs/commands/build.md +32 -4
  179. package/plugins/specweave-docs/commands/preview.md +43 -1
  180. package/plugins/specweave-docs/commands/validate.md +250 -0
  181. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1262 -0
  182. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  183. package/plugins/specweave-github/lib/enhanced-github-sync.js +220 -0
  184. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  185. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  186. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1254 -0
  187. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  188. package/plugins/specweave/hooks/post-edit-spec.sh +0 -265
  189. package/plugins/specweave/hooks/post-write-spec.sh +0 -267
  190. package/plugins/specweave/hooks/pre-edit-spec.sh +0 -151
  191. package/plugins/specweave/hooks/pre-write-spec.sh +0 -151
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env bash
2
+ # update-dashboard-cache.sh - Incremental update of dashboard cache
3
+ #
4
+ # Usage: bash update-dashboard-cache.sh <incrementId> <changeType>
5
+ #
6
+ # Arguments:
7
+ # incrementId - Increment ID (e.g., "0117-instant-dashboard-cache")
8
+ # changeType - Type of change: metadata|tasks|spec|jobs
9
+ #
10
+ # Updates only the affected increment in the cache, not a full rebuild.
11
+ # Uses file locking for concurrent safety.
12
+ #
13
+ # Compatible with bash 3.x (macOS default)
14
+
15
+ set -e
16
+
17
+ INCREMENT_ID="${1:-}"
18
+ CHANGE_TYPE="${2:-}"
19
+
20
+ if [[ -z "$INCREMENT_ID" ]]; then
21
+ echo "❌ Usage: update-dashboard-cache.sh <incrementId> <changeType>"
22
+ exit 1
23
+ fi
24
+
25
+ # Find project root
26
+ PROJECT_ROOT="$PWD"
27
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
28
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
29
+ done
30
+
31
+ if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
32
+ echo "❌ No .specweave directory found"
33
+ exit 1
34
+ fi
35
+
36
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
37
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
38
+ CACHE_FILE="$STATE_DIR/dashboard.json"
39
+ LOCK_FILE="$STATE_DIR/.dashboard.lock"
40
+ TEMP_FILE="$STATE_DIR/.dashboard.json.tmp.$$"
41
+
42
+ # Check if jq is available
43
+ if ! command -v jq >/dev/null 2>&1; then
44
+ echo "❌ jq is required. Install with: brew install jq"
45
+ exit 1
46
+ fi
47
+
48
+ # Acquire file lock (with timeout)
49
+ acquire_lock() {
50
+ local timeout=5
51
+ local count=0
52
+ while [[ -f "$LOCK_FILE" ]] && [[ $count -lt $timeout ]]; do
53
+ sleep 0.1
54
+ count=$((count + 1))
55
+ done
56
+ echo "$$" > "$LOCK_FILE"
57
+ }
58
+
59
+ release_lock() {
60
+ rm -f "$LOCK_FILE"
61
+ }
62
+
63
+ # Cleanup on exit
64
+ cleanup() {
65
+ release_lock
66
+ rm -f "$TEMP_FILE"
67
+ }
68
+ trap cleanup EXIT
69
+
70
+ # If cache doesn't exist, do full rebuild
71
+ if [[ ! -f "$CACHE_FILE" ]]; then
72
+ bash "$PROJECT_ROOT/plugins/specweave/scripts/rebuild-dashboard-cache.sh" --quiet
73
+ exit 0
74
+ fi
75
+
76
+ # Find increment directory
77
+ INCREMENT_DIR=""
78
+ for dir in "$INCREMENTS_DIR"/*"$INCREMENT_ID"*/; do
79
+ if [[ -d "$dir" ]]; then
80
+ INCREMENT_DIR="$dir"
81
+ INCREMENT_ID=$(basename "$dir")
82
+ break
83
+ fi
84
+ done
85
+
86
+ if [[ -z "$INCREMENT_DIR" ]] || [[ ! -d "$INCREMENT_DIR" ]]; then
87
+ # Increment might have been archived or deleted
88
+ # Remove from cache if it exists
89
+ acquire_lock
90
+ if jq -e --arg id "$INCREMENT_ID" '.increments[$id]' "$CACHE_FILE" >/dev/null 2>&1; then
91
+ # Get old status for counter updates
92
+ old_status=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].status // "backlog"' "$CACHE_FILE")
93
+ old_type=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].type // "feature"' "$CACHE_FILE")
94
+ old_priority=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].priority // "P1"' "$CACHE_FILE")
95
+
96
+ # Remove increment and update counters
97
+ jq --arg id "$INCREMENT_ID" \
98
+ --arg old_status "$old_status" \
99
+ --arg old_type "$old_type" \
100
+ --arg old_priority "$old_priority" '
101
+ del(.increments[$id]) |
102
+ del(.mtimes[$id]) |
103
+ .summary.total -= 1 |
104
+ .summary[$old_status] -= 1 |
105
+ .summary.byType[$old_type] -= 1 |
106
+ .summary.byPriority[$old_priority] -= 1 |
107
+ .updatedAt = (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
108
+ ' "$CACHE_FILE" > "$TEMP_FILE"
109
+ mv "$TEMP_FILE" "$CACHE_FILE"
110
+ fi
111
+ exit 0
112
+ fi
113
+
114
+ metadata_file="$INCREMENT_DIR/metadata.json"
115
+ tasks_file="$INCREMENT_DIR/tasks.md"
116
+ spec_file="$INCREMENT_DIR/spec.md"
117
+
118
+ # Skip if no metadata
119
+ if [[ ! -f "$metadata_file" ]]; then
120
+ exit 0
121
+ fi
122
+
123
+ # Read current values
124
+ status=$(jq -r '.status // "backlog"' "$metadata_file" 2>/dev/null)
125
+ type=$(jq -r '.type // "feature"' "$metadata_file" 2>/dev/null)
126
+ priority=$(jq -r '.priority // "P1"' "$metadata_file" 2>/dev/null)
127
+ title=$(jq -r '.title // ""' "$metadata_file" 2>/dev/null)
128
+ project=$(jq -r '.project // ""' "$metadata_file" 2>/dev/null)
129
+ created_at=$(jq -r '.createdAt // ""' "$metadata_file" 2>/dev/null)
130
+ user_stories=$(jq -c '.userStories // []' "$metadata_file" 2>/dev/null || echo "[]")
131
+
132
+ # Count tasks
133
+ total_tasks=0
134
+ completed_tasks=0
135
+ if [[ -f "$tasks_file" ]]; then
136
+ total_tasks=$(grep -c "^### T-" "$tasks_file" 2>/dev/null) || total_tasks=0
137
+ completed_tasks=$(grep "^\*\*Status\*\*:.*\[x\]" "$tasks_file" 2>/dev/null | wc -l | tr -d ' ') || completed_tasks=0
138
+ total_tasks="${total_tasks:-0}"
139
+ completed_tasks="${completed_tasks:-0}"
140
+ fi
141
+ if [[ "$total_tasks" -eq 0 ]]; then
142
+ total_tasks=$(jq -r '.tasks.total // 0' "$metadata_file" 2>/dev/null) || total_tasks=0
143
+ completed_tasks=$(jq -r '.tasks.completed // 0' "$metadata_file" 2>/dev/null) || completed_tasks=0
144
+ total_tasks="${total_tasks:-0}"
145
+ completed_tasks="${completed_tasks:-0}"
146
+ fi
147
+
148
+ # Count ACs
149
+ total_acs=0
150
+ completed_acs=0
151
+ if [[ -f "$spec_file" ]]; then
152
+ total_acs=$(grep -c "\- \[.\] \*\*AC-" "$spec_file" 2>/dev/null) || total_acs=0
153
+ completed_acs=$(grep -c "\- \[x\] \*\*AC-" "$spec_file" 2>/dev/null) || completed_acs=0
154
+ total_acs="${total_acs:-0}"
155
+ completed_acs="${completed_acs:-0}"
156
+ fi
157
+ if [[ "$total_acs" -eq 0 ]]; then
158
+ total_acs=$(jq -r '.acceptanceCriteria.total // 0' "$metadata_file" 2>/dev/null) || total_acs=0
159
+ completed_acs=$(jq -r '.acceptanceCriteria.satisfied // 0' "$metadata_file" 2>/dev/null) || completed_acs=0
160
+ total_acs="${total_acs:-0}"
161
+ completed_acs="${completed_acs:-0}"
162
+ fi
163
+
164
+ # Get last activity
165
+ last_activity=""
166
+ if [[ "$(uname)" == "Darwin" ]]; then
167
+ last_activity=$(stat -f "%Sm" -t "%Y-%m-%dT%H:%M:%SZ" "$metadata_file" 2>/dev/null || echo "")
168
+ else
169
+ last_activity=$(stat -c "%y" "$metadata_file" 2>/dev/null | sed 's/ /T/' | cut -d. -f1)Z || echo ""
170
+ fi
171
+
172
+ # Get mtimes for stale detection
173
+ meta_mtime=0
174
+ tasks_mtime=0
175
+ spec_mtime=0
176
+ if [[ "$(uname)" == "Darwin" ]]; then
177
+ [[ -f "$metadata_file" ]] && meta_mtime=$(stat -f "%m" "$metadata_file" 2>/dev/null || echo "0")
178
+ [[ -f "$tasks_file" ]] && tasks_mtime=$(stat -f "%m" "$tasks_file" 2>/dev/null || echo "0")
179
+ [[ -f "$spec_file" ]] && spec_mtime=$(stat -f "%m" "$spec_file" 2>/dev/null || echo "0")
180
+ else
181
+ [[ -f "$metadata_file" ]] && meta_mtime=$(stat -c "%Y" "$metadata_file" 2>/dev/null || echo "0")
182
+ [[ -f "$tasks_file" ]] && tasks_mtime=$(stat -c "%Y" "$tasks_file" 2>/dev/null || echo "0")
183
+ [[ -f "$spec_file" ]] && spec_mtime=$(stat -c "%Y" "$spec_file" 2>/dev/null || echo "0")
184
+ fi
185
+
186
+ # Acquire lock for update
187
+ acquire_lock
188
+
189
+ # Get old values for delta update
190
+ old_status=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].status // ""' "$CACHE_FILE" 2>/dev/null)
191
+ old_type=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].type // ""' "$CACHE_FILE" 2>/dev/null)
192
+ old_priority=$(jq -r --arg id "$INCREMENT_ID" '.increments[$id].priority // ""' "$CACHE_FILE" 2>/dev/null)
193
+ is_new=false
194
+ if [[ -z "$old_status" ]]; then
195
+ is_new=true
196
+ fi
197
+
198
+ # Build the increment update
199
+ increment_json=$(jq -n \
200
+ --arg status "$status" \
201
+ --arg type "$type" \
202
+ --arg priority "$priority" \
203
+ --arg title "$title" \
204
+ --arg project "$project" \
205
+ --argjson total_tasks "$total_tasks" \
206
+ --argjson completed_tasks "$completed_tasks" \
207
+ --argjson total_acs "$total_acs" \
208
+ --argjson completed_acs "$completed_acs" \
209
+ --arg created_at "$created_at" \
210
+ --arg last_activity "$last_activity" \
211
+ --argjson user_stories "$user_stories" \
212
+ '{
213
+ status: $status,
214
+ type: $type,
215
+ priority: $priority,
216
+ title: $title,
217
+ project: $project,
218
+ tasks: { total: $total_tasks, completed: $completed_tasks },
219
+ acs: { total: $total_acs, completed: $completed_acs },
220
+ createdAt: $created_at,
221
+ lastActivity: $last_activity,
222
+ userStories: $user_stories
223
+ }')
224
+
225
+ # Build mtime update
226
+ mtime_json=$(jq -n \
227
+ --argjson meta "$meta_mtime" \
228
+ --argjson tasks "$tasks_mtime" \
229
+ --argjson spec "$spec_mtime" \
230
+ '{ metadata: $meta, tasks: $tasks, spec: $spec }')
231
+
232
+ # Apply incremental update
233
+ if [[ "$is_new" == "true" ]]; then
234
+ # New increment - add to cache and increment counters
235
+ jq --arg id "$INCREMENT_ID" \
236
+ --argjson inc "$increment_json" \
237
+ --argjson mtime "$mtime_json" \
238
+ --arg status "$status" \
239
+ --arg type "$type" \
240
+ --arg priority "$priority" '
241
+ .increments[$id] = $inc |
242
+ .mtimes[$id] = $mtime |
243
+ .summary.total += 1 |
244
+ .summary[$status] += 1 |
245
+ .summary.byType[$type] += 1 |
246
+ .summary.byPriority[$priority] += 1 |
247
+ .updatedAt = (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
248
+ ' "$CACHE_FILE" > "$TEMP_FILE"
249
+ else
250
+ # Existing increment - update and adjust counters if status/type/priority changed
251
+ jq --arg id "$INCREMENT_ID" \
252
+ --argjson inc "$increment_json" \
253
+ --argjson mtime "$mtime_json" \
254
+ --arg old_status "$old_status" \
255
+ --arg new_status "$status" \
256
+ --arg old_type "$old_type" \
257
+ --arg new_type "$type" \
258
+ --arg old_priority "$old_priority" \
259
+ --arg new_priority "$priority" '
260
+ .increments[$id] = $inc |
261
+ .mtimes[$id] = $mtime |
262
+ (if $old_status != $new_status then
263
+ .summary[$old_status] -= 1 |
264
+ .summary[$new_status] += 1
265
+ else . end) |
266
+ (if $old_type != $new_type then
267
+ .summary.byType[$old_type] -= 1 |
268
+ .summary.byType[$new_type] += 1
269
+ else . end) |
270
+ (if $old_priority != $new_priority then
271
+ .summary.byPriority[$old_priority] -= 1 |
272
+ .summary.byPriority[$new_priority] += 1
273
+ else . end) |
274
+ .updatedAt = (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
275
+ ' "$CACHE_FILE" > "$TEMP_FILE"
276
+ fi
277
+
278
+ # Atomic write
279
+ mv "$TEMP_FILE" "$CACHE_FILE"
280
+
281
+ # Success - no output for silent operation in hooks
@@ -211,43 +211,298 @@ jq '.sync.profiles | to_entries[] | select(.value.provider == "ado") | .value.co
211
211
  ls -la .specweave/docs/internal/specs/*/ # If sub-folders exist (not FS-XXX) → 2-level
212
212
  ```
213
213
 
214
- **Project/Board Selection (MANDATORY BEFORE STEP 4!):**
214
+ **Project/Board Selection - ULTRA-SMART LOGIC (MANDATORY BEFORE STEP 4!):**
215
215
 
216
- 1. **For 1-level structure:**
217
- - ASK user: "Which project should this increment sync to?"
218
- - Options: List from `structureConfig.projects`
219
- - Store selected `PROJECT_ID`
216
+ **⚠️ CORE PRINCIPLE: Each User Story belongs to exactly ONE project (1-level) or ONE project+board (2-level). An increment can contain USs spanning MULTIPLE projects/boards.**
220
217
 
221
- 2. **For 2-level structure:**
222
- - ASK user: "Which project should this increment sync to?"
223
- - Then ASK: "Which board/area path within {project}?"
224
- - Options: List from `structureConfig.boardsByProject[selectedProject]`
225
- - Store BOTH `PROJECT_ID` and `BOARD_ID`
218
+ ---
219
+
220
+ ### SMART SELECTION DECISION TREE
221
+
222
+ **RULE 1: NO QUESTION IF ONLY 1 OPTION**
223
+ ```
224
+ IF 1-level AND only 1 project → AUTO-SELECT silently
225
+ IF 2-level AND only 1 project AND only 1 board → AUTO-SELECT silently
226
+ ```
227
+
228
+ **RULE 2: KEYWORD-BASED AUTO-DETECTION**
229
+
230
+ Analyze feature description and US content for keywords:
231
+
232
+ **Project-Level Keywords (1-level and 2-level):**
233
+ ```
234
+ Frontend (FE) keywords:
235
+ UI, form, button, page, component, React, Vue, Angular, Next.js,
236
+ CSS, style, responsive, chart, dashboard, view, modal, widget,
237
+ Tailwind, Material-UI, Recharts
238
+
239
+ Backend (BE) keywords:
240
+ API, endpoint, REST, GraphQL, database, query, migration, service,
241
+ controller, authentication, JWT, session, middleware, CRUD,
242
+ Redis, PostgreSQL, MongoDB, microservice
243
+
244
+ Mobile keywords:
245
+ mobile, iOS, Android, React Native, Flutter, Expo, app, native,
246
+ push notification, offline, AsyncStorage, screen, touch, gesture
247
+
248
+ Infrastructure (INFRA) keywords:
249
+ deploy, CI/CD, Docker, Kubernetes, terraform, monitoring,
250
+ logging, pipeline, AWS, Azure, GCP, nginx, Helm, ArgoCD
251
+
252
+ Shared (SHARED) keywords:
253
+ types, interfaces, utilities, validators, shared, common, library,
254
+ SDK, models, constants, helpers
255
+ ```
226
256
 
227
- **VALIDATION RULE:**
257
+ **Board-Level Keywords (2-level structures only):**
228
258
  ```
229
- FORBIDDEN: Creating spec.md without PROJECT_ID set
230
- ❌ FORBIDDEN: Creating spec.md for 2-level without BOARD_ID set
231
- ❌ FORBIDDEN: Vague increments like "show last git commits" without project context
232
- ✅ REQUIRED: Always know WHERE this increment will sync BEFORE creating spec.md
259
+ When project has multiple boards, also match board-specific keywords:
260
+
261
+ analytics/reporting: analytics, metrics, KPI, dashboard, report, chart, graph
262
+ user-management: user, auth, login, registration, profile, permissions, roles
263
+ integrations: integration, webhook, API, third-party, sync, import, export
264
+ payments: payment, billing, subscription, invoice, stripe, checkout
265
+ notifications: notification, alert, email, SMS, push, messaging
266
+ devops/platform: deploy, infrastructure, monitoring, CI/CD, pipeline
267
+ ```
268
+
269
+ **RULE 3: CONFIDENCE CALCULATION FORMULA**
270
+ ```
271
+ confidence = (matched_keywords / total_feature_keywords) × 100
272
+
273
+ Example: "Add React login form with JWT authentication"
274
+ Keywords found: React (FE), login (FE), form (FE), JWT (BE), authentication (BE)
275
+ FE matches: 3, BE matches: 2
276
+ FE confidence: 3/5 = 60%
277
+ BE confidence: 2/5 = 40%
278
+ → Primary: FE (60%), Secondary: BE (40%)
279
+ → SUGGEST: "Frontend (60%), but also touches Backend (40%)"
280
+
281
+ If multiple projects have similar confidence (within 15%):
282
+ → Treat as MULTI-PROJECT feature
283
+ → Auto-split USs by detected keywords
284
+ ```
285
+
286
+ **RULE 4: CONFIDENCE-BASED DECISION**
287
+ ```
288
+ >80% single project → AUTO-SELECT with notification (no question)
289
+ 50-80% single project → SUGGEST with quick confirm option
290
+ Multiple projects within 15% → AUTO-SPLIT across projects
291
+ <50% OR ambiguous → ASK user with all options
292
+ ```
293
+
294
+ **RULE 5: FALLBACK TO DEFAULTS**
295
+ ```
296
+ IF US has explicit **Project**: field → USE IT
297
+ ELSE IF frontmatter has default_project → USE default_project
298
+ ELSE → ASK user (should not happen if flow followed correctly)
299
+
300
+ Same logic applies to **Board**: and default_board for 2-level
233
301
  ```
234
302
 
235
- **Example User Interaction:**
303
+ ---
236
304
 
305
+ ### DECISION FLOWCHART
306
+
307
+ ```
308
+ START
309
+
310
+
311
+ ┌─────────────────────────────────────┐
312
+ │ 1. Detect structure level (1 or 2) │
313
+ │ 2. Count available projects/boards │
314
+ └─────────────────────────────────────┘
315
+
316
+
317
+ ┌─────────────────────────────────────┐
318
+ │ ONLY 1 PROJECT? │
319
+ │ (1-level: 1 project) │
320
+ │ (2-level: 1 project + 1 board) │
321
+ └─────────────────────────────────────┘
322
+
323
+ ├── YES ──► AUTO-SELECT SILENTLY
324
+ │ Output: "✅ Project: {name} (auto-selected)"
325
+ │ NO QUESTION ASKED
326
+
327
+ ▼ NO
328
+ ┌─────────────────────────────────────┐
329
+ │ ANALYZE KEYWORDS in feature desc │
330
+ │ Calculate confidence per project │
331
+ └─────────────────────────────────────┘
332
+
333
+ ├── HIGH CONFIDENCE (>80% single) ──► AUTO-SELECT + NOTIFY
334
+ │ Output: "✅ Detected: {project} (keywords: form, React)"
335
+
336
+ ├── MULTI-PROJECT (within 15%) ──► AUTO-SPLIT USs
337
+ │ Output: "🔀 Multi-project detected:
338
+ │ • US-001 (Login UI) → web-app (60%)
339
+ │ • US-002 (Auth API) → api-service (55%)
340
+ │ Proceed? (Y/n)"
341
+
342
+ ├── MEDIUM CONFIDENCE (50-80%) ──► SUGGEST + CONFIRM
343
+ │ Output: "📍 Suggested: {project}. Confirm? (Y/n)"
344
+
345
+ ▼ LOW CONFIDENCE (<50%)
346
+ ┌─────────────────────────────────────┐
347
+ │ ASK USER with ALL options listed │
348
+ │ multiSelect: true │
349
+ │ Show complete list (never truncate)│
350
+ └─────────────────────────────────────┘
237
351
  ```
238
- 🔍 Detected 2-level structure (ADO area path mapping)
239
- Available projects: acme-corp
240
352
 
241
- 📁 Project: acme-corp
242
- Boards: clinical-insights, platform-engineering, digital-operations
353
+ ---
354
+
355
+ ### PER-USER-STORY ASSIGNMENT MODEL
356
+
357
+ **CRITICAL: Assignment is at USER STORY level, not increment level!**
358
+
359
+ Each US in spec.md has its own project (and board for 2-level):
360
+
361
+ ```markdown
362
+ ## User Stories
363
+
364
+ ### US-001: Login Form UI
365
+ **Project**: web-app
366
+ **Board**: frontend <!-- 2-level only -->
367
+ **As a** user...
243
368
 
244
- Which board should this increment sync to?
245
- > digital-operations
369
+ ### US-002: Auth API Endpoints
370
+ **Project**: api-service
371
+ **Board**: backend <!-- 2-level only -->
372
+ **As a** developer...
246
373
 
247
- Increment will sync to: internal/specs/acme-corp/digital-operations/FS-XXX/
374
+ ### US-003: Mobile Login Screen
375
+ **Project**: mobile-app
376
+ **Board**: mobile-team <!-- 2-level only -->
377
+ **As a** mobile user...
248
378
  ```
249
379
 
250
- **Store PROJECT_ID and BOARD_ID for use in STEP 4!**
380
+ **User can manually change project/board per US at any time by editing spec.md!**
381
+
382
+ ---
383
+
384
+ ### EXAMPLE SCENARIOS
385
+
386
+ **Scenario 1: Single Project (NO QUESTION)**
387
+ ```
388
+ Config: 1 project (my-app)
389
+ Feature: "Add user authentication"
390
+
391
+ → AUTO-SELECT: my-app
392
+ → Output: "✅ Project: my-app (single project - auto-selected)"
393
+ → NO question asked
394
+ ```
395
+
396
+ **Scenario 2: Multiple Projects, Clear Keywords (AUTO-DETECT)**
397
+ ```
398
+ Config: 3 projects (web-app, api-service, mobile-app)
399
+ Feature: "Add React dashboard with charts"
400
+
401
+ → Keyword analysis: "React" (FE), "dashboard" (FE), "charts" (FE)
402
+ → Confidence: 95% → web-app
403
+ → Output: "✅ Detected project: web-app (keywords: React, dashboard, charts)"
404
+ → NO question asked (high confidence)
405
+ ```
406
+
407
+ **Scenario 3: Multiple Projects, Multi-Area Feature (SMART SPLIT)**
408
+ ```
409
+ Config: 3 projects (web-app, api-service, shared-lib)
410
+ Feature: "User authentication with JWT"
411
+
412
+ → Analyze: This spans FE (login form) + BE (auth API) + possibly shared (types)
413
+ → Output:
414
+ "🔍 This feature likely spans multiple projects:
415
+
416
+ Based on 'user authentication with JWT', I'll create:
417
+ • US-001: Login/Register UI → web-app (keywords: UI, form)
418
+ • US-002: Auth API endpoints → api-service (keywords: API, JWT)
419
+ • US-003: Auth types/validators → shared-lib (keywords: types, shared)
420
+
421
+ ✅ Proceed with this assignment? (Y/n)
422
+ 💡 You can modify project per US in spec.md anytime"
423
+ ```
424
+
425
+ **Scenario 4: 2-Level, Single Project, Auto-Detect Board**
426
+ ```
427
+ Config: 1 project (enterprise-corp), 5 boards (analytics, frontend, backend, mobile, devops)
428
+ Feature: "Add reporting dashboard"
429
+
430
+ → Project: AUTO-SELECT enterprise-corp (only option)
431
+ → Board keywords: "reporting" → analytics, "dashboard" → frontend
432
+ → Confidence: 70% analytics, 60% frontend
433
+ → Output:
434
+ "✅ Project: enterprise-corp (auto-selected)
435
+ 📍 Suggested board: analytics (keyword: reporting)
436
+
437
+ Confirm or select different board:
438
+ 1. analytics (suggested)
439
+ 2. frontend
440
+ 3. backend
441
+ 4. mobile
442
+ 5. devops"
443
+ ```
444
+
445
+ **Scenario 5: Completely Ambiguous (ASK WITH ALL OPTIONS)**
446
+ ```
447
+ Config: 4 projects (proj-a, proj-b, proj-c, proj-d)
448
+ Feature: "Improve system performance"
449
+
450
+ → Keyword analysis: No clear project match
451
+ → Output:
452
+ "❓ Which project(s) should this increment target?
453
+
454
+ Available projects:
455
+ • proj-a - E-commerce frontend
456
+ • proj-b - Order processing API
457
+ • proj-c - Mobile shopping app
458
+ • proj-d - Infrastructure/DevOps
459
+
460
+ Select one or more (comma-separated, e.g., '1,2' or 'proj-a,proj-b'):"
461
+ ```
462
+
463
+ ---
464
+
465
+ ### VALIDATION RULES
466
+
467
+ ```
468
+ ❌ FORBIDDEN: Asking project question when only 1 project exists
469
+ ❌ FORBIDDEN: Asking board question when only 1 board exists in project
470
+ ❌ FORBIDDEN: Hiding options behind "Let me see all" - ALWAYS show complete list
471
+ ❌ FORBIDDEN: Truncating project/board lists
472
+ ❌ FORBIDDEN: Assigning ALL USs to same project when content clearly differs
473
+ ✅ REQUIRED: Auto-select when only 1 option available
474
+ ✅ REQUIRED: Use keyword matching before asking user
475
+ ✅ REQUIRED: Each US has explicit project (and board for 2-level) assignment
476
+ ✅ REQUIRED: Allow user to modify assignments per-US in spec.md
477
+ ✅ REQUIRED: When asking, show ALL options with descriptions
478
+ ```
479
+
480
+ ---
481
+
482
+ ### SPEC.MD YAML FORMAT
483
+
484
+ **1-Level Structure:**
485
+ ```yaml
486
+ ---
487
+ increment: 0045-user-auth
488
+ title: "User Authentication"
489
+ # Optional default (used if US doesn't specify)
490
+ default_project: web-app
491
+ ---
492
+ ```
493
+
494
+ **2-Level Structure:**
495
+ ```yaml
496
+ ---
497
+ increment: 0045-user-auth
498
+ title: "User Authentication"
499
+ # Optional defaults (used if US doesn't specify)
500
+ default_project: enterprise-corp
501
+ default_board: backend
502
+ ---
503
+ ```
504
+
505
+ **Store detected/selected values for use in STEP 4!**
251
506
 
252
507
  ---
253
508
 
@@ -445,6 +700,60 @@ const metadata = {
445
700
 
446
701
  **DO NOT invoke Task() tool to spawn agents from this skill!**
447
702
 
703
+ ### STEP 9: Trigger Living Docs & External Tool Sync (v0.32.2+)
704
+
705
+ **🔄 CRITICAL: After increment files are created, trigger sync to living docs AND external tools!**
706
+
707
+ This step uses the existing sync infrastructure to:
708
+ 1. Create living docs (FS-XXX folder with FEATURE.md and us-*.md files)
709
+ 2. Check permissions (`canUpsertInternalItems`) from `.specweave/config.json`
710
+ 3. Sync to external tools (GitHub/JIRA/ADO) if configured and permitted
711
+
712
+ **Run the sync-specs command:**
713
+
714
+ ```bash
715
+ /specweave:sync-specs {increment-id}
716
+ ```
717
+
718
+ **Expected output:**
719
+
720
+ ```
721
+ 🔄 Syncing increment to living docs...
722
+ ✅ Living docs synced: FS-021
723
+ Created: 4 files (FEATURE.md, us-001.md, us-002.md, us-003.md)
724
+
725
+ 📡 Syncing to external tools: github
726
+ 📋 Permissions: upsert=true, update=true, status=true
727
+ ✅ Synced to GitHub: 0 updated, 3 created
728
+ ```
729
+
730
+ **Permission handling (v0.32.2+):**
731
+
732
+ If `canUpsertInternalItems: false` in config:
733
+ ```
734
+ ⚠️ Skipping external sync - canUpsertInternalItems is disabled
735
+ 💡 Enable in .specweave/config.json: sync.settings.canUpsertInternalItems: true
736
+ ```
737
+
738
+ **Error handling:**
739
+
740
+ External tool sync failures are NON-BLOCKING:
741
+ ```
742
+ ⚠️ External sync failed: Rate limit exceeded
743
+ 💡 Run /specweave:sync-specs {increment-id} to retry
744
+ ```
745
+
746
+ **Output after sync:**
747
+
748
+ ```
749
+ ✅ Increment created and synced!
750
+
751
+ Next steps:
752
+ 1. Review the increment plan and docs
753
+ 2. Start implementation: /specweave:do {increment-id}
754
+ 3. Monitor status: /specweave:status {increment-id}
755
+ ```
756
+
448
757
  ---
449
758
 
450
759
  ## Model Selection for Tasks