specweave 0.26.14 → 0.27.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.
Files changed (189) hide show
  1. package/CLAUDE.md +73 -1
  2. package/README.md +111 -466
  3. package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts.map +1 -1
  4. package/dist/plugins/specweave-jira/lib/setup-wizard.js +57 -78
  5. package/dist/plugins/specweave-jira/lib/setup-wizard.js.map +1 -1
  6. package/dist/src/cli/commands/import-docs.d.ts.map +1 -1
  7. package/dist/src/cli/commands/import-docs.js +23 -31
  8. package/dist/src/cli/commands/import-docs.js.map +1 -1
  9. package/dist/src/cli/commands/import-external.d.ts.map +1 -1
  10. package/dist/src/cli/commands/import-external.js +6 -10
  11. package/dist/src/cli/commands/import-external.js.map +1 -1
  12. package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
  13. package/dist/src/cli/commands/init-multiproject.js +58 -73
  14. package/dist/src/cli/commands/init-multiproject.js.map +1 -1
  15. package/dist/src/cli/commands/init.d.ts +17 -11
  16. package/dist/src/cli/commands/init.d.ts.map +1 -1
  17. package/dist/src/cli/commands/init.js +221 -1874
  18. package/dist/src/cli/commands/init.js.map +1 -1
  19. package/dist/src/cli/commands/install.d.ts.map +1 -1
  20. package/dist/src/cli/commands/install.js +14 -22
  21. package/dist/src/cli/commands/install.js.map +1 -1
  22. package/dist/src/cli/commands/migrate-config.d.ts.map +1 -1
  23. package/dist/src/cli/commands/migrate-config.js +6 -10
  24. package/dist/src/cli/commands/migrate-config.js.map +1 -1
  25. package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
  26. package/dist/src/cli/commands/switch-project.js.map +1 -1
  27. package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -1
  28. package/dist/src/cli/helpers/ado-area-path-mapper.js +36 -49
  29. package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -1
  30. package/dist/src/cli/helpers/github/increment-profile-selector.d.ts.map +1 -1
  31. package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
  32. package/dist/src/cli/helpers/github/profile-manager.d.ts.map +1 -1
  33. package/dist/src/cli/helpers/github/profile-manager.js +8 -11
  34. package/dist/src/cli/helpers/github/profile-manager.js.map +1 -1
  35. package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -1
  36. package/dist/src/cli/helpers/github-repo-selector.js +26 -50
  37. package/dist/src/cli/helpers/github-repo-selector.js.map +1 -1
  38. package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -1
  39. package/dist/src/cli/helpers/import-strategy-prompter.js +39 -52
  40. package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -1
  41. package/dist/src/cli/helpers/init/config-detection.d.ts +40 -0
  42. package/dist/src/cli/helpers/init/config-detection.d.ts.map +1 -0
  43. package/dist/src/cli/helpers/init/config-detection.js +125 -0
  44. package/dist/src/cli/helpers/init/config-detection.js.map +1 -0
  45. package/dist/src/cli/helpers/init/directory-structure.d.ts +26 -0
  46. package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -0
  47. package/dist/src/cli/helpers/init/directory-structure.js +190 -0
  48. package/dist/src/cli/helpers/init/directory-structure.js.map +1 -0
  49. package/dist/src/cli/helpers/init/external-import.d.ts +15 -0
  50. package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -0
  51. package/dist/src/cli/helpers/init/external-import.js +251 -0
  52. package/dist/src/cli/helpers/init/external-import.js.map +1 -0
  53. package/dist/src/cli/helpers/init/index.d.ts +15 -0
  54. package/dist/src/cli/helpers/init/index.d.ts.map +1 -0
  55. package/dist/src/cli/helpers/init/index.js +26 -0
  56. package/dist/src/cli/helpers/init/index.js.map +1 -0
  57. package/dist/src/cli/helpers/init/next-steps.d.ts +15 -0
  58. package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -0
  59. package/dist/src/cli/helpers/init/next-steps.js +72 -0
  60. package/dist/src/cli/helpers/init/next-steps.js.map +1 -0
  61. package/dist/src/cli/helpers/init/path-utils.d.ts +41 -0
  62. package/dist/src/cli/helpers/init/path-utils.d.ts.map +1 -0
  63. package/dist/src/cli/helpers/init/path-utils.js +146 -0
  64. package/dist/src/cli/helpers/init/path-utils.js.map +1 -0
  65. package/dist/src/cli/helpers/init/plugin-installer.d.ts +28 -0
  66. package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -0
  67. package/dist/src/cli/helpers/init/plugin-installer.js +238 -0
  68. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -0
  69. package/dist/src/cli/helpers/init/repository-setup.d.ts +28 -0
  70. package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -0
  71. package/dist/src/cli/helpers/init/repository-setup.js +78 -0
  72. package/dist/src/cli/helpers/init/repository-setup.js.map +1 -0
  73. package/dist/src/cli/helpers/init/smart-reinit.d.ts +30 -0
  74. package/dist/src/cli/helpers/init/smart-reinit.d.ts.map +1 -0
  75. package/dist/src/cli/helpers/init/smart-reinit.js +140 -0
  76. package/dist/src/cli/helpers/init/smart-reinit.js.map +1 -0
  77. package/dist/src/cli/helpers/init/testing-config.d.ts +27 -0
  78. package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -0
  79. package/dist/src/cli/helpers/init/testing-config.js +131 -0
  80. package/dist/src/cli/helpers/init/testing-config.js.map +1 -0
  81. package/dist/src/cli/helpers/init/types.d.ts +86 -0
  82. package/dist/src/cli/helpers/init/types.d.ts.map +1 -0
  83. package/dist/src/cli/helpers/init/types.js +5 -0
  84. package/dist/src/cli/helpers/init/types.js.map +1 -0
  85. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -1
  86. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +10 -12
  87. package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -1
  88. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  89. package/dist/src/cli/helpers/issue-tracker/ado.js +43 -60
  90. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  91. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  92. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +193 -230
  93. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  94. package/dist/src/cli/helpers/issue-tracker/github.d.ts.map +1 -1
  95. package/dist/src/cli/helpers/issue-tracker/github.js +43 -54
  96. package/dist/src/cli/helpers/issue-tracker/github.js.map +1 -1
  97. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  98. package/dist/src/cli/helpers/issue-tracker/index.js +27 -40
  99. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  100. package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
  101. package/dist/src/cli/helpers/issue-tracker/jira.js +54 -70
  102. package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
  103. package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -1
  104. package/dist/src/cli/helpers/smart-filter.js +62 -85
  105. package/dist/src/cli/helpers/smart-filter.js.map +1 -1
  106. package/dist/src/core/increment/auto-transition-manager.d.ts +12 -0
  107. package/dist/src/core/increment/auto-transition-manager.d.ts.map +1 -1
  108. package/dist/src/core/increment/auto-transition-manager.js +45 -0
  109. package/dist/src/core/increment/auto-transition-manager.js.map +1 -1
  110. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  111. package/dist/src/core/increment/metadata-manager.js +46 -0
  112. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  113. package/dist/src/core/increment/status-change-sync-trigger.d.ts +12 -0
  114. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
  115. package/dist/src/core/increment/status-change-sync-trigger.js +48 -2
  116. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
  117. package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -0
  118. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  119. package/dist/src/core/living-docs/living-docs-sync.js +40 -0
  120. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  121. package/dist/src/core/repo-structure/repo-bulk-discovery.d.ts.map +1 -1
  122. package/dist/src/core/repo-structure/repo-bulk-discovery.js +63 -83
  123. package/dist/src/core/repo-structure/repo-bulk-discovery.js.map +1 -1
  124. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  125. package/dist/src/core/repo-structure/repo-structure-manager.js +339 -424
  126. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  127. package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
  128. package/dist/src/core/sync/bidirectional-engine.js +21 -29
  129. package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
  130. package/dist/src/init/InitFlow.js +15 -19
  131. package/dist/src/init/InitFlow.js.map +1 -1
  132. package/dist/src/init/repo/types.d.ts +1 -1
  133. package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -1
  134. package/dist/src/integrations/ado/area-path-mapper.js +19 -23
  135. package/dist/src/integrations/ado/area-path-mapper.js.map +1 -1
  136. package/dist/src/utils/external-resource-validator.d.ts.map +1 -1
  137. package/dist/src/utils/external-resource-validator.js +41 -65
  138. package/dist/src/utils/external-resource-validator.js.map +1 -1
  139. package/dist/src/utils/project-detection.d.ts.map +1 -1
  140. package/dist/src/utils/project-detection.js +19 -21
  141. package/dist/src/utils/project-detection.js.map +1 -1
  142. package/dist/src/utils/project-validator.d.ts.map +1 -1
  143. package/dist/src/utils/project-validator.js +5 -7
  144. package/dist/src/utils/project-validator.js.map +1 -1
  145. package/package.json +2 -3
  146. package/plugins/specweave/agents/tech-lead/AGENT.md +9 -0
  147. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  148. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  149. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  150. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  151. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  152. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  153. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  154. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  155. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  156. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  157. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  158. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  159. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  160. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  161. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +12 -0
  162. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +45 -0
  163. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -1
  164. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +46 -0
  165. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  166. package/plugins/specweave/skills/increment-planner/SKILL.md +10 -5
  167. package/plugins/specweave/skills/specweave-framework/SKILL.md +6 -4
  168. package/plugins/specweave/templates/coding-standards.md.template +36 -0
  169. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  170. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  171. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
  172. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  173. package/plugins/specweave-ado/lib/project-selector.js +56 -67
  174. package/plugins/specweave-ado/lib/project-selector.ts +72 -85
  175. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1104 -0
  176. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  177. package/plugins/specweave-github/lib/repo-selector.js +55 -66
  178. package/plugins/specweave-github/lib/repo-selector.ts +73 -84
  179. package/plugins/specweave-jira/commands/import-projects.js +3 -5
  180. package/plugins/specweave-jira/commands/import-projects.ts +3 -5
  181. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  182. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  183. package/plugins/specweave-jira/lib/project-selector.js +60 -71
  184. package/plugins/specweave-jira/lib/project-selector.ts +78 -91
  185. package/plugins/specweave-jira/lib/setup-wizard.js +51 -72
  186. package/plugins/specweave-jira/lib/setup-wizard.ts +56 -74
  187. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1017 -0
  188. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  189. package/src/templates/CLAUDE.md.template +14 -0
@@ -0,0 +1,258 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave GitHub Sync Hook
4
+ # Runs after task completion to sync progress to GitHub Projects
5
+ #
6
+ # ARCHITECTURE (v0.19.0+): IMMUTABLE DESCRIPTIONS + PROGRESS COMMENTS
7
+ # - User Story files (.specweave/docs/internal/specs/) ↔ GitHub Issues
8
+ # - Issue descriptions created once (IMMUTABLE snapshot)
9
+ # - All updates via progress comments (audit trail)
10
+ #
11
+ # This hook is part of the specweave-github plugin and handles:
12
+ # - Finding which spec user stories the current work belongs to
13
+ # - Syncing progress via GitHub comments (NOT editing issue body)
14
+ # - Creating audit trail of all changes over time
15
+ # - Notifying stakeholders via GitHub notifications
16
+ #
17
+ # Dependencies:
18
+ # - Node.js and TypeScript CLI (dist/cli/commands/sync-spec-content.js)
19
+ # - GitHub CLI (gh) must be installed and authenticated
20
+ # - ProgressCommentBuilder (lib/progress-comment-builder.ts)
21
+
22
+ set -e
23
+
24
+ # ============================================================================
25
+ # PROJECT ROOT DETECTION
26
+ # ============================================================================
27
+
28
+ # Find project root by searching upward for .specweave/ directory
29
+ find_project_root() {
30
+ local dir="$1"
31
+ while [ "$dir" != "/" ]; do
32
+ if [ -d "$dir/.specweave" ]; then
33
+ echo "$dir"
34
+ return 0
35
+ fi
36
+ dir="$(dirname "$dir")"
37
+ done
38
+ # Fallback: try current directory
39
+ if [ -d "$(pwd)/.specweave" ]; then
40
+ pwd
41
+ else
42
+ echo "$(pwd)"
43
+ fi
44
+ }
45
+
46
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
47
+ cd "$PROJECT_ROOT" 2>/dev/null || true
48
+
49
+ # ============================================================================
50
+ # CONFIGURATION
51
+ # ============================================================================
52
+
53
+ LOGS_DIR=".specweave/logs"
54
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
55
+
56
+ mkdir -p "$LOGS_DIR" 2>/dev/null || true
57
+
58
+ # ============================================================================
59
+ # PRECONDITIONS CHECK
60
+ # ============================================================================
61
+
62
+ echo "[$(date)] [GitHub] 🔗 GitHub sync hook fired" >> "$DEBUG_LOG" 2>/dev/null || true
63
+
64
+ # Check if Node.js is available
65
+ if ! command -v node &> /dev/null; then
66
+ echo "[$(date)] [GitHub] ⚠️ Node.js not found, skipping GitHub sync" >> "$DEBUG_LOG" 2>/dev/null || true
67
+ cat <<EOF
68
+ {
69
+ "continue": true
70
+ }
71
+ EOF
72
+ exit 0
73
+ fi
74
+
75
+ # Check if github-spec-content-sync CLI exists
76
+ SYNC_CLI="$PROJECT_ROOT/dist/src/cli/commands/sync-spec-content.js"
77
+ if [ ! -f "$SYNC_CLI" ]; then
78
+ echo "[$(date)] [GitHub] ⚠️ sync-spec-content CLI not found at $SYNC_CLI, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
79
+ cat <<EOF
80
+ {
81
+ "continue": true
82
+ }
83
+ EOF
84
+ exit 0
85
+ fi
86
+
87
+ # Check for gh CLI
88
+ if ! command -v gh &> /dev/null; then
89
+ echo "[$(date)] [GitHub] ⚠️ GitHub CLI (gh) not found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
90
+ cat <<EOF
91
+ {
92
+ "continue": true
93
+ }
94
+ EOF
95
+ exit 0
96
+ fi
97
+
98
+ # ============================================================================
99
+ # DETECT ALL SPECS (Multi-Spec Support)
100
+ # ============================================================================
101
+
102
+ # Strategy: Use multi-spec detector to find ALL specs referenced in current increment
103
+
104
+ # 1. Detect current increment (temporary context)
105
+ CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 basename | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
106
+
107
+ if [ -z "$CURRENT_INCREMENT" ]; then
108
+ echo "[$(date)] [GitHub] ℹ️ No active increment, checking for spec changes..." >> "$DEBUG_LOG" 2>/dev/null || true
109
+ # Fall through to sync all changed specs
110
+ fi
111
+
112
+ # 2. Use TypeScript CLI to detect all specs
113
+ DETECT_CLI="$PROJECT_ROOT/dist/src/cli/commands/detect-specs.js"
114
+
115
+ if [ -f "$DETECT_CLI" ]; then
116
+ echo "[$(date)] [GitHub] 🔍 Detecting all specs in increment $CURRENT_INCREMENT..." >> "$DEBUG_LOG" 2>/dev/null || true
117
+
118
+ # Call detect-specs CLI and capture JSON output
119
+ DETECTION_RESULT=$(node "$DETECT_CLI" 2>> "$DEBUG_LOG" || echo "{}")
120
+
121
+ # Extract spec count
122
+ SPEC_COUNT=$(echo "$DETECTION_RESULT" | node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(0,'utf-8')); console.log(data.specs?.length || 0)")
123
+
124
+ echo "[$(date)] [GitHub] 📋 Detected $SPEC_COUNT spec(s)" >> "$DEBUG_LOG" 2>/dev/null || true
125
+
126
+ # Store detection result for later use
127
+ echo "$DETECTION_RESULT" > /tmp/specweave-detected-specs.json
128
+ else
129
+ echo "[$(date)] [GitHub] ⚠️ detect-specs CLI not found at $DETECT_CLI, falling back to git diff" >> "$DEBUG_LOG" 2>/dev/null || true
130
+ SPEC_COUNT=0
131
+ fi
132
+
133
+ # ============================================================================
134
+ # SYNC ALL DETECTED SPECS TO GITHUB (Multi-Spec Support)
135
+ # ============================================================================
136
+
137
+ if [ -f /tmp/specweave-detected-specs.json ] && [ "$SPEC_COUNT" -gt 0 ]; then
138
+ # Multi-spec sync: Loop through all detected specs
139
+ echo "[$(date)] [GitHub] 🔄 Syncing $SPEC_COUNT spec(s) to GitHub..." >> "$DEBUG_LOG" 2>/dev/null || true
140
+
141
+ # Extract spec paths using Node.js
142
+ SPEC_PATHS=$(node -e "
143
+ const fs = require('fs');
144
+ const data = JSON.parse(fs.readFileSync('/tmp/specweave-detected-specs.json', 'utf-8'));
145
+ const syncable = data.specs.filter(s => s.syncEnabled && s.project !== '_parent');
146
+ syncable.forEach(s => console.log(s.path));
147
+ " 2>> "$DEBUG_LOG")
148
+
149
+ # Count syncable specs
150
+ SYNCABLE_COUNT=$(echo "$SPEC_PATHS" | grep -v '^$' | wc -l | tr -d ' ')
151
+
152
+ if [ "$SYNCABLE_COUNT" -gt 0 ]; then
153
+ echo "[$(date)] [GitHub] 📋 Syncing $SYNCABLE_COUNT syncable spec(s) (excluding _parent)" >> "$DEBUG_LOG" 2>/dev/null || true
154
+
155
+ # Sync each spec
156
+ echo "$SPEC_PATHS" | while read -r SPEC_FILE; do
157
+ if [ -n "$SPEC_FILE" ] && [ -f "$SPEC_FILE" ]; then
158
+ # Extract project and spec ID from path
159
+ SPEC_NAME=$(basename "$SPEC_FILE" .md)
160
+ PROJECT=$(basename "$(dirname "$SPEC_FILE")")
161
+
162
+ echo "[$(date)] [GitHub] 🔄 Syncing $PROJECT/$SPEC_NAME..." >> "$DEBUG_LOG" 2>/dev/null || true
163
+
164
+ (cd "$PROJECT_ROOT" && node "$SYNC_CLI" --spec "$SPEC_FILE" --provider github) 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
165
+ echo "[$(date)] [GitHub] ⚠️ Spec sync failed for $PROJECT/$SPEC_NAME (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
166
+ }
167
+
168
+ echo "[$(date)] [GitHub] ✅ Synced $PROJECT/$SPEC_NAME" >> "$DEBUG_LOG" 2>/dev/null || true
169
+ fi
170
+ done
171
+
172
+ echo "[$(date)] [GitHub] ✅ Multi-spec sync complete ($SYNCABLE_COUNT synced)" >> "$DEBUG_LOG" 2>/dev/null || true
173
+ else
174
+ echo "[$(date)] [GitHub] ℹ️ No syncable specs (all specs are _parent or syncEnabled=false)" >> "$DEBUG_LOG" 2>/dev/null || true
175
+ fi
176
+
177
+ # Cleanup temp file
178
+ rm -f /tmp/specweave-detected-specs.json 2>/dev/null || true
179
+ else
180
+ # Fallback: Sync all modified specs (check git diff)
181
+ echo "[$(date)] [GitHub] 🔄 Checking for modified specs..." >> "$DEBUG_LOG" 2>/dev/null || true
182
+
183
+ MODIFIED_SPECS=$(git diff --name-only HEAD .specweave/docs/internal/specs/**/*.md 2>/dev/null || echo "")
184
+
185
+ if [ -n "$MODIFIED_SPECS" ]; then
186
+ echo "[$(date)] [GitHub] 📝 Found modified specs:" >> "$DEBUG_LOG" 2>/dev/null || true
187
+ echo "$MODIFIED_SPECS" >> "$DEBUG_LOG" 2>/dev/null || true
188
+
189
+ # Sync each modified spec
190
+ echo "$MODIFIED_SPECS" | while read -r SPEC_FILE; do
191
+ if [ -n "$SPEC_FILE" ] && [ -f "$SPEC_FILE" ]; then
192
+ echo "[$(date)] [GitHub] 🔄 Syncing $SPEC_FILE..." >> "$DEBUG_LOG" 2>/dev/null || true
193
+ (cd "$PROJECT_ROOT" && node "$SYNC_CLI" --spec "$SPEC_FILE" --provider github) 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || true
194
+ fi
195
+ done
196
+
197
+ echo "[$(date)] [GitHub] ✅ Batch spec sync complete" >> "$DEBUG_LOG" 2>/dev/null || true
198
+ else
199
+ echo "[$(date)] [GitHub] ℹ️ No modified specs found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
200
+ fi
201
+ fi
202
+
203
+ # ============================================================================
204
+ # EPIC GITHUB ISSUE SYNC (DEPRECATED v0.24.0+)
205
+ # ============================================================================
206
+ #
207
+ # ⚠️ DEPRECATED: SpecWeave now syncs ONLY at User Story level.
208
+ #
209
+ # Feature/Epic-level issues are no longer updated.
210
+ # Use /specweave-github:sync instead to sync User Story issues.
211
+ #
212
+ # To re-enable (NOT recommended):
213
+ # export SPECWEAVE_ENABLE_EPIC_SYNC=true
214
+ #
215
+ # @see .specweave/increments/0047-us-task-linkage/reports/GITHUB-TITLE-FORMAT-FIX-PLAN.md
216
+ # ============================================================================
217
+
218
+ if [ "$SPECWEAVE_ENABLE_EPIC_SYNC" = "true" ]; then
219
+ echo "[$(date)] [GitHub] 🔄 Checking for Epic GitHub issue update (DEPRECATED)..." >> "$DEBUG_LOG" 2>/dev/null || true
220
+
221
+ # Find active increment ID
222
+ ACTIVE_INCREMENT=$(ls -t .specweave/increments/ | grep -v '^\.' | while read inc; do
223
+ if [ -f ".specweave/increments/$inc/metadata.json" ]; then
224
+ STATUS=$(grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' ".specweave/increments/$inc/metadata.json" 2>/dev/null | sed 's/.*"\([^"]*\)".*/\1/' || true)
225
+ if [ "$STATUS" = "active" ]; then
226
+ echo "$inc"
227
+ break
228
+ fi
229
+ fi
230
+ done | head -1)
231
+
232
+ if [ -n "$ACTIVE_INCREMENT" ]; then
233
+ echo "[$(date)] [GitHub] 🎯 Active increment: $ACTIVE_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
234
+
235
+ # Run Epic sync script (silently, errors logged to debug log)
236
+ if [ -f "$PROJECT_ROOT/scripts/update-epic-github-issue.sh" ]; then
237
+ echo "[$(date)] [GitHub] 🚀 Updating Epic GitHub issue (DEPRECATED)..." >> "$DEBUG_LOG" 2>/dev/null || true
238
+ "$PROJECT_ROOT/scripts/update-epic-github-issue.sh" "$ACTIVE_INCREMENT" >> "$DEBUG_LOG" 2>&1 || true
239
+ echo "[$(date)] [GitHub] ⚠️ Epic sync is deprecated. Use /specweave-github:sync instead." >> "$DEBUG_LOG" 2>/dev/null || true
240
+ else
241
+ echo "[$(date)] [GitHub] ⚠️ Epic sync script not found, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
242
+ fi
243
+ else
244
+ echo "[$(date)] [GitHub] ℹ️ No active increment found, skipping Epic sync" >> "$DEBUG_LOG" 2>/dev/null || true
245
+ fi
246
+ else
247
+ echo "[$(date)] [GitHub] ℹ️ Epic sync disabled (sync at User Story level only)" >> "$DEBUG_LOG" 2>/dev/null || true
248
+ fi
249
+
250
+ # ============================================================================
251
+ # OUTPUT TO CLAUDE
252
+ # ============================================================================
253
+
254
+ cat <<EOF
255
+ {
256
+ "continue": true
257
+ }
258
+ EOF
@@ -1,4 +1,4 @@
1
- import inquirer from "inquirer";
1
+ import { select, checkbox, input, Separator } from "@inquirer/prompts";
2
2
  import { GitHubClient } from "./github-client.js";
3
3
  async function fetchAllGitHubRepos(owner, limit = 100) {
4
4
  console.log("\u{1F50D} Fetching GitHub repositories...");
@@ -34,29 +34,25 @@ async function selectGitHubRepos(options = {}) {
34
34
  console.log("\u{1F4CB} Available GitHub Repositories:\n");
35
35
  console.log(` Total: ${allRepos.length} repositories
36
36
  `);
37
- const { selectionMethod } = await inquirer.prompt([
38
- {
39
- type: "select",
40
- name: "selectionMethod",
41
- message: "How would you like to select repositories?",
42
- choices: [
43
- {
44
- name: `\u{1F4CB} Interactive (browse and select from ${allRepos.length} repositories)`,
45
- value: "interactive"
46
- },
37
+ const selectionMethod = await select({
38
+ message: "How would you like to select repositories?",
39
+ choices: [
40
+ {
41
+ name: `\u{1F4CB} Interactive (browse and select from ${allRepos.length} repositories)`,
42
+ value: "interactive"
43
+ },
44
+ {
45
+ name: "\u270F\uFE0F Manual entry (type repository names)",
46
+ value: "manual"
47
+ },
48
+ ...allowSelectAll ? [
47
49
  {
48
- name: "\u270F\uFE0F Manual entry (type repository names)",
49
- value: "manual"
50
- },
51
- ...allowSelectAll ? [
52
- {
53
- name: `\u2728 Select all (${allRepos.length} repositories)`,
54
- value: "all"
55
- }
56
- ] : []
57
- ]
58
- }
59
- ]);
50
+ name: `\u2728 Select all (${allRepos.length} repositories)`,
51
+ value: "all"
52
+ }
53
+ ] : []
54
+ ]
55
+ });
60
56
  if (selectionMethod === "all") {
61
57
  return {
62
58
  selectedRepos: allRepos.map((r) => r.fullName),
@@ -81,34 +77,31 @@ async function interactiveRepoSelection(allRepos, preSelected, minSelection, max
81
77
  value: r.fullName,
82
78
  checked: preSelected.includes(r.fullName)
83
79
  }));
84
- choices.push(
85
- new inquirer.Separator(),
80
+ const allChoices = [
81
+ ...choices,
82
+ new Separator(),
86
83
  {
87
84
  name: "\u270F\uFE0F Enter repository names manually instead",
88
85
  value: "__MANUAL__",
89
86
  checked: false
90
87
  }
91
- );
92
- const { selectedRepos } = await inquirer.prompt([
93
- {
94
- type: "checkbox",
95
- name: "selectedRepos",
96
- message: `Select GitHub repositories (${minSelection}${maxSelection ? `-${maxSelection}` : "+"} required):`,
97
- choices,
98
- pageSize,
99
- loop: false,
100
- validate: (selected) => {
101
- const actualSelected = selected.filter((k) => k !== "__MANUAL__");
102
- if (actualSelected.length < minSelection) {
103
- return `Please select at least ${minSelection} repository(ies)`;
104
- }
105
- if (maxSelection && actualSelected.length > maxSelection) {
106
- return `Please select at most ${maxSelection} repository(ies)`;
107
- }
108
- return true;
88
+ ];
89
+ const selectedRepos = await checkbox({
90
+ message: `Select GitHub repositories (${minSelection}${maxSelection ? `-${maxSelection}` : "+"} required):`,
91
+ choices: allChoices,
92
+ pageSize,
93
+ loop: false,
94
+ validate: (selected) => {
95
+ const actualSelected = selected.filter((k) => k !== "__MANUAL__");
96
+ if (actualSelected.length < minSelection) {
97
+ return `Please select at least ${minSelection} repository(ies)`;
98
+ }
99
+ if (maxSelection && actualSelected.length > maxSelection) {
100
+ return `Please select at most ${maxSelection} repository(ies)`;
109
101
  }
102
+ return true;
110
103
  }
111
- ]);
104
+ });
112
105
  if (selectedRepos.includes("__MANUAL__")) {
113
106
  return await manualRepoEntry(allRepos, minSelection, maxSelection);
114
107
  }
@@ -130,30 +123,26 @@ async function manualRepoEntry(allRepos, minSelection, maxSelection) {
130
123
  );
131
124
  console.log("");
132
125
  }
133
- const { manualRepos } = await inquirer.prompt([
134
- {
135
- type: "input",
136
- name: "manualRepos",
137
- message: "Repository names:",
138
- validate: (input) => {
139
- if (!input.trim()) {
140
- return "Please enter at least one repository name";
141
- }
142
- const repos = input.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
143
- if (repos.length < minSelection) {
144
- return `Please enter at least ${minSelection} repository name(s)`;
145
- }
146
- if (maxSelection && repos.length > maxSelection) {
147
- return `Please enter at most ${maxSelection} repository name(s)`;
148
- }
149
- const invalidRepos = repos.filter((r) => !/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(r));
150
- if (invalidRepos.length > 0) {
151
- return `Invalid repository format: ${invalidRepos.join(", ")}. Use owner/repo format (e.g., octocat/Hello-World).`;
152
- }
153
- return true;
126
+ const manualRepos = await input({
127
+ message: "Repository names:",
128
+ validate: (inputValue) => {
129
+ if (!inputValue.trim()) {
130
+ return "Please enter at least one repository name";
131
+ }
132
+ const repos = inputValue.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
133
+ if (repos.length < minSelection) {
134
+ return `Please enter at least ${minSelection} repository name(s)`;
135
+ }
136
+ if (maxSelection && repos.length > maxSelection) {
137
+ return `Please enter at most ${maxSelection} repository name(s)`;
154
138
  }
139
+ const invalidRepos = repos.filter((r) => !/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(r));
140
+ if (invalidRepos.length > 0) {
141
+ return `Invalid repository format: ${invalidRepos.join(", ")}. Use owner/repo format (e.g., octocat/Hello-World).`;
142
+ }
143
+ return true;
155
144
  }
156
- ]);
145
+ });
157
146
  const selectedRepos = manualRepos.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
158
147
  const knownRepos = allRepos.map((r) => r.fullName);
159
148
  const unknownRepos = selectedRepos.filter((r) => !knownRepos.includes(r));
@@ -9,7 +9,7 @@
9
9
  * - Validates repo names
10
10
  */
11
11
 
12
- import inquirer from 'inquirer';
12
+ import { select, checkbox, input, Separator } from '@inquirer/prompts';
13
13
  import { GitHubClient } from './github-client.js';
14
14
 
15
15
  // ============================================================================
@@ -109,31 +109,27 @@ export async function selectGitHubRepos(
109
109
  console.log(` Total: ${allRepos.length} repositories\n`);
110
110
 
111
111
  // Decide selection method
112
- const { selectionMethod } = await inquirer.prompt([
113
- {
114
- type: 'select',
115
- name: 'selectionMethod',
116
- message: 'How would you like to select repositories?',
117
- choices: [
118
- {
119
- name: `📋 Interactive (browse and select from ${allRepos.length} repositories)`,
120
- value: 'interactive',
121
- },
122
- {
123
- name: '✏️ Manual entry (type repository names)',
124
- value: 'manual',
125
- },
126
- ...(allowSelectAll
127
- ? [
128
- {
129
- name: `✨ Select all (${allRepos.length} repositories)`,
130
- value: 'all',
131
- },
132
- ]
133
- : []),
134
- ],
135
- },
136
- ]);
112
+ const selectionMethod = await select({
113
+ message: 'How would you like to select repositories?',
114
+ choices: [
115
+ {
116
+ name: `📋 Interactive (browse and select from ${allRepos.length} repositories)`,
117
+ value: 'interactive',
118
+ },
119
+ {
120
+ name: '✏️ Manual entry (type repository names)',
121
+ value: 'manual',
122
+ },
123
+ ...(allowSelectAll
124
+ ? [
125
+ {
126
+ name: `✨ Select all (${allRepos.length} repositories)`,
127
+ value: 'all',
128
+ },
129
+ ]
130
+ : []),
131
+ ],
132
+ });
137
133
 
138
134
  if (selectionMethod === 'all') {
139
135
  return {
@@ -175,38 +171,35 @@ async function interactiveRepoSelection(
175
171
  }));
176
172
 
177
173
  // Add manual entry option at the end
178
- choices.push(
179
- new inquirer.Separator(),
174
+ const allChoices = [
175
+ ...choices,
176
+ new Separator(),
180
177
  {
181
178
  name: '✏️ Enter repository names manually instead',
182
179
  value: '__MANUAL__',
183
180
  checked: false,
184
- } as any
185
- );
181
+ },
182
+ ];
186
183
 
187
- const { selectedRepos } = await inquirer.prompt([
188
- {
189
- type: 'checkbox',
190
- name: 'selectedRepos',
191
- message: `Select GitHub repositories (${minSelection}${maxSelection ? `-${maxSelection}` : '+'} required):`,
192
- choices,
193
- pageSize,
194
- loop: false,
195
- validate: (selected: string[]) => {
196
- const actualSelected = selected.filter((k) => k !== '__MANUAL__');
197
-
198
- if (actualSelected.length < minSelection) {
199
- return `Please select at least ${minSelection} repository(ies)`;
200
- }
201
-
202
- if (maxSelection && actualSelected.length > maxSelection) {
203
- return `Please select at most ${maxSelection} repository(ies)`;
204
- }
205
-
206
- return true;
207
- },
184
+ const selectedRepos = await checkbox({
185
+ message: `Select GitHub repositories (${minSelection}${maxSelection ? `-${maxSelection}` : '+'} required):`,
186
+ choices: allChoices,
187
+ pageSize,
188
+ loop: false,
189
+ validate: (selected: readonly string[]) => {
190
+ const actualSelected = selected.filter((k) => k !== '__MANUAL__');
191
+
192
+ if (actualSelected.length < minSelection) {
193
+ return `Please select at least ${minSelection} repository(ies)`;
194
+ }
195
+
196
+ if (maxSelection && actualSelected.length > maxSelection) {
197
+ return `Please select at most ${maxSelection} repository(ies)`;
198
+ }
199
+
200
+ return true;
208
201
  },
209
- ]);
202
+ });
210
203
 
211
204
  // Check if user chose manual entry
212
205
  if (selectedRepos.includes('__MANUAL__')) {
@@ -243,39 +236,35 @@ async function manualRepoEntry(
243
236
  console.log('');
244
237
  }
245
238
 
246
- const { manualRepos } = await inquirer.prompt([
247
- {
248
- type: 'input',
249
- name: 'manualRepos',
250
- message: 'Repository names:',
251
- validate: (input: string) => {
252
- if (!input.trim()) {
253
- return 'Please enter at least one repository name';
254
- }
255
-
256
- const repos = input
257
- .split(',')
258
- .map((r) => r.trim())
259
- .filter((r) => r.length > 0);
260
-
261
- if (repos.length < minSelection) {
262
- return `Please enter at least ${minSelection} repository name(s)`;
263
- }
264
-
265
- if (maxSelection && repos.length > maxSelection) {
266
- return `Please enter at most ${maxSelection} repository name(s)`;
267
- }
268
-
269
- // Validate format (owner/repo)
270
- const invalidRepos = repos.filter((r) => !/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(r));
271
- if (invalidRepos.length > 0) {
272
- return `Invalid repository format: ${invalidRepos.join(', ')}. Use owner/repo format (e.g., octocat/Hello-World).`;
273
- }
274
-
275
- return true;
276
- },
239
+ const manualRepos = await input({
240
+ message: 'Repository names:',
241
+ validate: (inputValue: string) => {
242
+ if (!inputValue.trim()) {
243
+ return 'Please enter at least one repository name';
244
+ }
245
+
246
+ const repos = inputValue
247
+ .split(',')
248
+ .map((r) => r.trim())
249
+ .filter((r) => r.length > 0);
250
+
251
+ if (repos.length < minSelection) {
252
+ return `Please enter at least ${minSelection} repository name(s)`;
253
+ }
254
+
255
+ if (maxSelection && repos.length > maxSelection) {
256
+ return `Please enter at most ${maxSelection} repository name(s)`;
257
+ }
258
+
259
+ // Validate format (owner/repo)
260
+ const invalidRepos = repos.filter((r) => !/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(r));
261
+ if (invalidRepos.length > 0) {
262
+ return `Invalid repository format: ${invalidRepos.join(', ')}. Use owner/repo format (e.g., octocat/Hello-World).`;
263
+ }
264
+
265
+ return true;
277
266
  },
278
- ]);
267
+ });
279
268
 
280
269
  const selectedRepos = manualRepos
281
270
  .split(',')
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import inquirer from "inquirer";
2
+ import { confirm } from "@inquirer/prompts";
3
3
  import { existsSync } from "fs";
4
4
  import path from "path";
5
5
  import { JiraClient } from "../../../src/integrations/jira/jira-client.js";
@@ -98,12 +98,10 @@ async function importProjects(options = {}) {
98
98
  `));
99
99
  return;
100
100
  }
101
- const { confirmed } = await inquirer.prompt([{
102
- type: "confirm",
103
- name: "confirmed",
101
+ const confirmed = await confirm({
104
102
  message: `Import ${newProjects.length} new project(s)?`,
105
103
  default: true
106
- }]);
104
+ });
107
105
  if (!confirmed) {
108
106
  console.log(chalk.yellow("\n\u23ED\uFE0F Import canceled\n"));
109
107
  return;
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  import chalk from 'chalk';
21
- import inquirer from 'inquirer';
21
+ import { confirm } from '@inquirer/prompts';
22
22
  import { existsSync } from 'fs';
23
23
  import path from 'path';
24
24
  import { JiraClient } from '../../../src/integrations/jira/jira-client.js';
@@ -175,12 +175,10 @@ export async function importProjects(options: ImportProjectsOptions = {}): Promi
175
175
  }
176
176
 
177
177
  // Step 9: Confirm import
178
- const { confirmed } = await inquirer.prompt([{
179
- type: 'confirm',
180
- name: 'confirmed',
178
+ const confirmed = await confirm({
181
179
  message: `Import ${newProjects.length} new project(s)?`,
182
180
  default: true
183
- }]);
181
+ });
184
182
 
185
183
  if (!confirmed) {
186
184
  console.log(chalk.yellow('\n⏭️ Import canceled\n'));