specweave 0.28.9 → 0.28.13

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 (58) hide show
  1. package/dist/src/cli/commands/init.d.ts.map +1 -1
  2. package/dist/src/cli/commands/init.js +35 -8
  3. package/dist/src/cli/commands/init.js.map +1 -1
  4. package/dist/src/cli/helpers/init/index.d.ts +2 -0
  5. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  6. package/dist/src/cli/helpers/init/index.js +4 -0
  7. package/dist/src/cli/helpers/init/index.js.map +1 -1
  8. package/dist/src/cli/helpers/init/language-selection.d.ts +40 -0
  9. package/dist/src/cli/helpers/init/language-selection.d.ts.map +1 -0
  10. package/dist/src/cli/helpers/init/language-selection.js +281 -0
  11. package/dist/src/cli/helpers/init/language-selection.js.map +1 -0
  12. package/dist/src/cli/helpers/init/repository-setup.d.ts +2 -0
  13. package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -1
  14. package/dist/src/cli/helpers/init/repository-setup.js +156 -12
  15. package/dist/src/cli/helpers/init/repository-setup.js.map +1 -1
  16. package/dist/src/cli/helpers/init/translation-config.d.ts +61 -0
  17. package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -0
  18. package/dist/src/cli/helpers/init/translation-config.js +437 -0
  19. package/dist/src/cli/helpers/init/translation-config.js.map +1 -0
  20. package/dist/src/cli/helpers/init/types.d.ts +33 -0
  21. package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
  22. package/dist/src/core/config/types.d.ts +143 -0
  23. package/dist/src/core/config/types.d.ts.map +1 -1
  24. package/dist/src/core/config/types.js.map +1 -1
  25. package/dist/src/core/repo-structure/repo-id-generator.d.ts +24 -95
  26. package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
  27. package/dist/src/core/repo-structure/repo-id-generator.js +31 -223
  28. package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
  29. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  30. package/dist/src/core/repo-structure/repo-structure-manager.js +12 -46
  31. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  32. package/dist/src/sync/sync-coordinator.d.ts +5 -0
  33. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  34. package/dist/src/sync/sync-coordinator.js +104 -6
  35. package/dist/src/sync/sync-coordinator.js.map +1 -1
  36. package/dist/src/utils/multi-repo-detector.d.ts +85 -0
  37. package/dist/src/utils/multi-repo-detector.d.ts.map +1 -0
  38. package/dist/src/utils/multi-repo-detector.js +264 -0
  39. package/dist/src/utils/multi-repo-detector.js.map +1 -0
  40. package/package.json +1 -1
  41. package/plugins/specweave/agents/pm/AGENT.md +178 -0
  42. package/plugins/specweave/agents/test-aware-planner/AGENT.md +54 -0
  43. package/plugins/specweave/commands/specweave-increment.md +30 -0
  44. package/plugins/specweave/commands/specweave-save.md +838 -0
  45. package/plugins/specweave/hooks/hooks.json +12 -0
  46. package/plugins/specweave/hooks/lib/update-status-line.sh +9 -1
  47. package/plugins/specweave/hooks/post-increment-completion.sh +4 -3
  48. package/plugins/specweave/hooks/post-increment-planning.sh +95 -51
  49. package/plugins/specweave/hooks/post-metadata-change.sh +18 -4
  50. package/plugins/specweave/hooks/pre-task-completion-edit.sh +355 -0
  51. package/plugins/specweave/lib/hooks/sync-living-docs.js +43 -0
  52. package/plugins/specweave/skills/increment-planner/SKILL.md +252 -2
  53. package/plugins/specweave/skills/spec-generator/SKILL.md +163 -0
  54. package/plugins/specweave/skills/umbrella-repo-detector/SKILL.md +286 -0
  55. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +18 -0
  56. package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
  57. package/plugins/specweave-release/commands/specweave-release-npm.md +14 -22
  58. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +27 -0
@@ -1,5 +1,17 @@
1
1
  {
2
2
  "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Edit",
6
+ "matcher_content": "\\.specweave/increments/[0-9]{4}-.+/tasks\\.md",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-task-completion-edit.sh"
11
+ }
12
+ ]
13
+ }
14
+ ],
3
15
  "UserPromptSubmit": [
4
16
  {
5
17
  "hooks": [
@@ -58,9 +58,17 @@ fi
58
58
  # ============================================================================
59
59
  # TTL CHECK (10 seconds - balanced for UX vs performance)
60
60
  # ============================================================================
61
+ # TTL check (skip if --force flag provided or SPECWEAVE_FORCE_STATUS_UPDATE=1)
62
+ # ============================================================================
61
63
  TTL_SECONDS=10
64
+ FORCE_UPDATE=0
65
+
66
+ # Check for --force flag
67
+ if [[ "${1:-}" == "--force" ]] || [[ "${SPECWEAVE_FORCE_STATUS_UPDATE:-0}" == "1" ]]; then
68
+ FORCE_UPDATE=1
69
+ fi
62
70
 
63
- if [[ -f "$CACHE_FILE" ]]; then
71
+ if [[ "$FORCE_UPDATE" -eq 0 ]] && [[ -f "$CACHE_FILE" ]]; then
64
72
  if [[ "$(uname)" == "Darwin" ]]; then
65
73
  CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) ))
66
74
  else
@@ -21,8 +21,8 @@ if [ -z "$INCREMENT_ID" ]; then
21
21
  exit 0
22
22
  fi
23
23
 
24
- # Get project root
25
- PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
24
+ # Get project root (3 levels up from plugins/specweave/hooks/)
25
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
26
26
  INCREMENT_DIR="$PROJECT_ROOT/.specweave/increments/$INCREMENT_ID"
27
27
 
28
28
  # ============================================================================
@@ -135,6 +135,7 @@ if [ -f "$SPEC_FILE" ]; then
135
135
  echo "🔄 Syncing spec.md status to 'completed'..."
136
136
 
137
137
  # Read current status from spec.md frontmatter
138
+ # Use heredoc to avoid quote escaping issues in awk
138
139
  SPEC_STATUS=$(awk '
139
140
  BEGIN { in_frontmatter=0 }
140
141
  /^---$/ {
@@ -146,7 +147,7 @@ if [ -f "$SPEC_FILE" ]; then
146
147
  }
147
148
  in_frontmatter == 1 && /^status:/ {
148
149
  gsub(/^status:[ \t]*/, "");
149
- gsub(/["'\''']/, "");
150
+ gsub(/["'"'"']/, "");
150
151
  print;
151
152
  exit
152
153
  }
@@ -58,10 +58,12 @@ cd "$PROJECT_ROOT" 2>/dev/null || true
58
58
  # CONFIGURATION
59
59
  # ============================================================================
60
60
 
61
- # Translation settings (can be overridden by .specweave/config.json)
62
- TRANSLATION_ENABLED=true
63
- AUTO_TRANSLATE_INTERNAL_DOCS=true
64
- TARGET_LANGUAGE="en" # Always translate TO English (for maintainability)
61
+ # Translation settings (loaded from .specweave/config.json)
62
+ TRANSLATION_ENABLED=false # Opt-in: User MUST enable in config
63
+ AUTO_TRANSLATE_INCREMENT_SPECS=false # Opt-in: scope.incrementSpecs
64
+ AUTO_TRANSLATE_LIVING_DOCS=false # Opt-in: scope.livingDocs
65
+ TARGET_LANGUAGE="en" # Loaded from config.language (default: en)
66
+ KEEP_ENGLISH_ORIGINALS=false # If true, create .en.md backups
65
67
 
66
68
  # Paths
67
69
  SPECWEAVE_DIR=".specweave"
@@ -96,24 +98,59 @@ log_error() {
96
98
 
97
99
  load_config() {
98
100
  if [ ! -f "$CONFIG_FILE" ]; then
99
- log_debug "No config file found, using defaults"
101
+ log_debug "No config file found, using defaults (translation disabled)"
100
102
  return
101
103
  fi
102
104
 
103
- # Check if translation is enabled in config
104
- local translation_enabled=$(cat "$CONFIG_FILE" | grep -o '"enabled"[[:space:]]*:[[:space:]]*\(true\|false\)' | grep -o '\(true\|false\)' || echo "true")
105
+ # Load target language from config.language (default: en)
106
+ local config_language=$(cat "$CONFIG_FILE" | grep -o '"language"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "en")
107
+ if [ -n "$config_language" ]; then
108
+ TARGET_LANGUAGE="$config_language"
109
+ log_debug "Target language: $TARGET_LANGUAGE"
110
+ fi
111
+
112
+ # If target language is English, no translation needed
113
+ if [ "$TARGET_LANGUAGE" = "en" ]; then
114
+ TRANSLATION_ENABLED=false
115
+ log_debug "Target language is English, translation not needed"
116
+ return
117
+ fi
118
+
119
+ # Check if translation is enabled in config.translation.enabled
120
+ # Search within "translation" block for "enabled"
121
+ local translation_section=$(cat "$CONFIG_FILE" | awk '/"translation"[[:space:]]*:/,/^[[:space:]]*}/' 2>/dev/null)
122
+ local translation_enabled=$(echo "$translation_section" | grep -o '"enabled"[[:space:]]*:[[:space:]]*\(true\|false\)' | head -1 | grep -o '\(true\|false\)' || echo "false")
105
123
 
106
- if [ "$translation_enabled" = "false" ]; then
124
+ if [ "$translation_enabled" = "true" ]; then
125
+ TRANSLATION_ENABLED=true
126
+ log_debug "Translation enabled in config"
127
+ else
107
128
  TRANSLATION_ENABLED=false
108
- log_debug "Translation disabled in config"
129
+ log_debug "Translation disabled in config (opt-in required)"
130
+ fi
131
+
132
+ # Check translation scope (within translation.scope block)
133
+ local scope_section=$(echo "$translation_section" | awk '/"scope"[[:space:]]*:/,/^[[:space:]]*}/' 2>/dev/null)
134
+
135
+ # scope.incrementSpecs - translate spec.md, plan.md, tasks.md
136
+ local scope_increment=$(echo "$scope_section" | grep -o '"incrementSpecs"[[:space:]]*:[[:space:]]*\(true\|false\)' | grep -o '\(true\|false\)' || echo "false")
137
+ if [ "$scope_increment" = "true" ]; then
138
+ AUTO_TRANSLATE_INCREMENT_SPECS=true
139
+ log_debug "Auto-translate increment specs enabled"
109
140
  fi
110
141
 
111
- # Check if auto-translation of internal docs is enabled
112
- local auto_translate=$(cat "$CONFIG_FILE" | grep -o '"autoTranslateInternalDocs"[[:space:]]*:[[:space:]]*\(true\|false\)' | grep -o '\(true\|false\)' || echo "true")
142
+ # scope.livingDocs - translate living docs on update
143
+ local scope_living=$(echo "$scope_section" | grep -o '"livingDocs"[[:space:]]*:[[:space:]]*\(true\|false\)' | grep -o '\(true\|false\)' || echo "false")
144
+ if [ "$scope_living" = "true" ]; then
145
+ AUTO_TRANSLATE_LIVING_DOCS=true
146
+ log_debug "Auto-translate living docs enabled"
147
+ fi
113
148
 
114
- if [ "$auto_translate" = "false" ]; then
115
- AUTO_TRANSLATE_INTERNAL_DOCS=false
116
- log_debug "Auto-translation of internal docs disabled in config"
149
+ # keepEnglishOriginals - create .en.md backups
150
+ local keep_originals=$(echo "$translation_section" | grep -o '"keepEnglishOriginals"[[:space:]]*:[[:space:]]*\(true\|false\)' | grep -o '\(true\|false\)' || echo "false")
151
+ if [ "$keep_originals" = "true" ]; then
152
+ KEEP_ENGLISH_ORIGINALS=true
153
+ log_debug "Keep English originals enabled"
117
154
  fi
118
155
  }
119
156
 
@@ -168,21 +205,21 @@ detect_file_language() {
168
205
  translate_file() {
169
206
  local file_path="$1"
170
207
  local file_name=$(basename "$file_path")
208
+ local target_lang="${TARGET_LANGUAGE:-en}"
171
209
 
172
- log_info " 📄 Translating $file_name..."
210
+ log_info " 📄 Translating $file_name to $target_lang..."
173
211
 
174
212
  # Call the translate-file.ts script
175
- # In production, this would invoke the LLM via Task tool
176
- # For now, we'll create a marker file to indicate translation is needed
213
+ # Uses TARGET_LANGUAGE from config (default: en)
177
214
 
178
215
  if [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-file.js" ]; then
179
216
  # Production: Use compiled TypeScript
180
- node "${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-file.js" "$file_path" --target-lang en --verbose 2>&1 | while read -r line; do
217
+ node "${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-file.js" "$file_path" --target-lang "$target_lang" --verbose 2>&1 | while read -r line; do
181
218
  echo " $line"
182
219
  done
183
220
 
184
221
  if [ ${PIPESTATUS[0]} -eq 0 ]; then
185
- log_info " ✅ $file_name translated successfully"
222
+ log_info " ✅ $file_name translated to $target_lang"
186
223
  return 0
187
224
  else
188
225
  log_error " ⚠️ Translation failed for $file_name"
@@ -191,7 +228,7 @@ translate_file() {
191
228
  else
192
229
  # Development/Testing: Just mark the file
193
230
  log_info " ℹ️ Translation script not compiled (run 'npm run build')"
194
- log_info " ℹ️ In production, $file_name would be translated to English"
231
+ log_info " ℹ️ In production, $file_name would be translated to $target_lang"
195
232
  return 0
196
233
  fi
197
234
  }
@@ -584,26 +621,20 @@ main() {
584
621
  # 1. Load configuration
585
622
  load_config
586
623
 
624
+ # Check if translation is needed (non-English target + enabled)
587
625
  if [ "$TRANSLATION_ENABLED" = "false" ]; then
588
- log_debug "Translation disabled, exiting"
589
- cat <<EOF
590
- {
591
- "continue": true,
592
- "message": "Translation disabled in config"
593
- }
594
- EOF
595
- exit 0
626
+ log_debug "Translation disabled or target is English, skipping translation"
627
+ # Continue with other hook tasks (GitHub sync, living docs, etc.)
596
628
  fi
597
629
 
598
- if [ "$AUTO_TRANSLATE_INTERNAL_DOCS" = "false" ]; then
599
- log_debug "Auto-translation of internal docs disabled, exiting"
600
- cat <<EOF
601
- {
602
- "continue": true,
603
- "message": "Auto-translation of internal docs disabled"
604
- }
605
- EOF
606
- exit 0
630
+ # Log translation configuration
631
+ if [ "$TRANSLATION_ENABLED" = "true" ]; then
632
+ log_info ""
633
+ log_info "🌐 Translation Configuration:"
634
+ log_info " Target language: $TARGET_LANGUAGE"
635
+ log_info " Increment specs: $AUTO_TRANSLATE_INCREMENT_SPECS"
636
+ log_info " Living docs: $AUTO_TRANSLATE_LIVING_DOCS"
637
+ log_info " Keep English originals: $KEEP_ENGLISH_ORIGINALS"
607
638
  fi
608
639
 
609
640
  # 2. Get latest increment directory
@@ -656,17 +687,25 @@ EOF
656
687
  fi
657
688
  fi
658
689
 
659
- # 4. Translate increment files (if needed)
690
+ # 4. Translate increment files (if enabled in scope and non-English content detected)
660
691
  local increment_success_count=0
661
692
  local increment_total_count=${#files_to_translate[@]}
662
693
 
663
- if [ "$needs_translation" = "true" ] && [ ${#files_to_translate[@]} -gt 0 ]; then
664
- # 5. Perform translation of increment files
694
+ if [ "$TRANSLATION_ENABLED" = "true" ] && [ "$AUTO_TRANSLATE_INCREMENT_SPECS" = "true" ] && [ "$needs_translation" = "true" ] && [ ${#files_to_translate[@]} -gt 0 ]; then
695
+ # 5. Perform translation of increment files TO user's language
665
696
  log_info ""
666
- log_info "🌐 Detected non-English content in increment $increment_id"
667
- log_info " Translating increment files to English..."
697
+ log_info "🌐 Translating increment $increment_id to $TARGET_LANGUAGE..."
668
698
  log_info ""
669
699
 
700
+ # Create English backups if configured
701
+ if [ "$KEEP_ENGLISH_ORIGINALS" = "true" ]; then
702
+ for file in "${files_to_translate[@]}"; do
703
+ local backup="${file%.md}.en.md"
704
+ cp "$file" "$backup" 2>/dev/null || true
705
+ log_debug "Created English backup: $backup"
706
+ done
707
+ fi
708
+
670
709
  for file in "${files_to_translate[@]}"; do
671
710
  if translate_file "$file"; then
672
711
  ((increment_success_count++))
@@ -675,19 +714,24 @@ EOF
675
714
 
676
715
  log_info ""
677
716
  if [ "$increment_success_count" -eq "$increment_total_count" ]; then
678
- log_info "✅ Increment files translation complete! All $increment_total_count file(s) now in English"
717
+ log_info "✅ Increment files translated to $TARGET_LANGUAGE! ($increment_total_count file(s))"
679
718
  else
680
- log_error "Increment files translation completed with errors: $increment_success_count/$increment_total_count files translated"
719
+ log_error "Translation completed with errors: $increment_success_count/$increment_total_count files"
681
720
  fi
682
- else
683
- log_info " All increment files already in English"
721
+ elif [ "$TRANSLATION_ENABLED" = "true" ] && [ "$AUTO_TRANSLATE_INCREMENT_SPECS" = "false" ]; then
722
+ log_debug "Increment spec translation disabled in scope.incrementSpecs"
723
+ elif [ "$needs_translation" = "false" ]; then
724
+ log_debug "All increment files already in target language"
684
725
  fi
685
726
 
686
- # 6. Translate living docs specs (if any were created)
687
- log_info ""
688
- log_info "🌐 Checking living docs for translation..."
689
-
690
- translate_living_docs_specs "$increment_id"
727
+ # 6. Translate living docs specs (if enabled in scope)
728
+ if [ "$TRANSLATION_ENABLED" = "true" ] && [ "$AUTO_TRANSLATE_LIVING_DOCS" = "true" ]; then
729
+ log_info ""
730
+ log_info "🌐 Checking living docs for translation to $TARGET_LANGUAGE..."
731
+ translate_living_docs_specs "$increment_id"
732
+ else
733
+ log_debug "Living docs translation disabled (scope.livingDocs=$AUTO_TRANSLATE_LIVING_DOCS)"
734
+ fi
691
735
 
692
736
  # ============================================================================
693
737
  # INCREMENT-LEVEL GITHUB ISSUE CREATION (DEPRECATED v0.24.0+)
@@ -170,6 +170,20 @@ CURRENT_STATUS=$(jq -r '.status // "unknown"' "$METADATA_PATH" 2>/dev/null)
170
170
 
171
171
  echo "[$(date)] post-metadata-change: Current status: $CURRENT_STATUS" >> "$DEBUG_LOG" 2>/dev/null || true
172
172
 
173
+ # ============================================================================
174
+ # CRITICAL FIX (v0.28.12): Remove guard BEFORE calling sub-hooks
175
+ # ============================================================================
176
+ # PROBLEM: Sub-hooks (post-increment-completion.sh) check for recursion guard
177
+ # and exit immediately if it exists. But the guard was created HERE (line 77),
178
+ # so sub-hooks NEVER ran - causing status line to never update!
179
+ #
180
+ # SOLUTION: Temporarily remove the guard before calling sub-hooks.
181
+ # The guard's purpose is to prevent THIS hook from being called recursively
182
+ # (if sub-hooks modify metadata.json). Sub-hooks have their OWN guards.
183
+ #
184
+ # See: Root cause analysis 2025-11-25 - status line never updates after /done
185
+ rm -f "$RECURSION_GUARD_FILE" 2>/dev/null || true
186
+
173
187
  # Dispatch to appropriate lifecycle hook based on status
174
188
  case "$CURRENT_STATUS" in
175
189
  completed)
@@ -186,9 +200,9 @@ case "$CURRENT_STATUS" in
186
200
  }
187
201
  else
188
202
  echo "[$(date)] post-metadata-change: post-increment-completion.sh not found or not executable" >> "$DEBUG_LOG" 2>/dev/null || true
189
- # Fallback: Update status line directly
190
- bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
191
203
  fi
204
+ # ALWAYS update status line after completion (force bypass TTL cache)
205
+ bash "$HOOK_DIR/lib/update-status-line.sh" --force 2>/dev/null || true
192
206
  ;;
193
207
 
194
208
  paused|resumed|abandoned)
@@ -205,9 +219,9 @@ case "$CURRENT_STATUS" in
205
219
  }
206
220
  else
207
221
  echo "[$(date)] post-metadata-change: post-increment-status-change.sh not found" >> "$DEBUG_LOG" 2>/dev/null || true
208
- # Fallback: Update status line directly
209
- bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
210
222
  fi
223
+ # ALWAYS update status line after status change (force bypass TTL cache)
224
+ bash "$HOOK_DIR/lib/update-status-line.sh" --force 2>/dev/null || true
211
225
  ;;
212
226
 
213
227
  active|planning|in-progress)