specweave 0.22.14 → 0.23.1

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 (211) hide show
  1. package/CLAUDE.md +178 -1
  2. package/dist/src/cli/commands/import-external.d.ts +22 -0
  3. package/dist/src/cli/commands/import-external.d.ts.map +1 -0
  4. package/dist/src/cli/commands/import-external.js +282 -0
  5. package/dist/src/cli/commands/import-external.js.map +1 -0
  6. package/dist/src/cli/commands/init.d.ts.map +1 -1
  7. package/dist/src/cli/commands/init.js +46 -0
  8. package/dist/src/cli/commands/init.js.map +1 -1
  9. package/dist/src/cli/helpers/github-repo-selector.d.ts +59 -0
  10. package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -0
  11. package/dist/src/cli/helpers/github-repo-selector.js +265 -0
  12. package/dist/src/cli/helpers/github-repo-selector.js.map +1 -0
  13. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  14. package/dist/src/cli/helpers/issue-tracker/index.js +5 -17
  15. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  16. package/dist/src/config/types.d.ts +16 -16
  17. package/dist/src/core/increment/ac-status-manager.d.ts.map +1 -1
  18. package/dist/src/core/increment/ac-status-manager.js +4 -2
  19. package/dist/src/core/increment/ac-status-manager.js.map +1 -1
  20. package/dist/src/core/increment/completion-validator.d.ts +30 -1
  21. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  22. package/dist/src/core/increment/completion-validator.js +151 -3
  23. package/dist/src/core/increment/completion-validator.js.map +1 -1
  24. package/dist/src/core/increment/increment-archiver.d.ts +25 -0
  25. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  26. package/dist/src/core/increment/increment-archiver.js +130 -3
  27. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  28. package/dist/src/core/living-docs/feature-archiver.d.ts +37 -0
  29. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  30. package/dist/src/core/living-docs/feature-archiver.js +262 -18
  31. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  32. package/dist/src/core/living-docs/feature-id-manager.d.ts +17 -0
  33. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -1
  34. package/dist/src/core/living-docs/feature-id-manager.js +25 -0
  35. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -1
  36. package/dist/src/core/living-docs/living-docs-sync.d.ts +14 -0
  37. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  38. package/dist/src/core/living-docs/living-docs-sync.js +46 -0
  39. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  40. package/dist/src/core/repo-structure/repo-id-generator.d.ts +20 -0
  41. package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
  42. package/dist/src/core/repo-structure/repo-id-generator.js +44 -0
  43. package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
  44. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  45. package/dist/src/core/repo-structure/repo-structure-manager.js +5 -2
  46. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  47. package/dist/src/core/sync/sync-event-logger.d.ts +15 -1
  48. package/dist/src/core/sync/sync-event-logger.d.ts.map +1 -1
  49. package/dist/src/core/sync/sync-event-logger.js +39 -1
  50. package/dist/src/core/sync/sync-event-logger.js.map +1 -1
  51. package/dist/src/core/types/sync-config-validator.d.ts +57 -0
  52. package/dist/src/core/types/sync-config-validator.d.ts.map +1 -0
  53. package/dist/src/core/types/sync-config-validator.js +116 -0
  54. package/dist/src/core/types/sync-config-validator.js.map +1 -0
  55. package/dist/src/importers/duplicate-detector.d.ts +107 -0
  56. package/dist/src/importers/duplicate-detector.d.ts.map +1 -0
  57. package/dist/src/importers/duplicate-detector.js +189 -0
  58. package/dist/src/importers/duplicate-detector.js.map +1 -0
  59. package/dist/src/importers/import-coordinator.d.ts +15 -0
  60. package/dist/src/importers/import-coordinator.d.ts.map +1 -1
  61. package/dist/src/importers/import-coordinator.js +43 -1
  62. package/dist/src/importers/import-coordinator.js.map +1 -1
  63. package/dist/src/importers/item-converter.d.ts +5 -0
  64. package/dist/src/importers/item-converter.d.ts.map +1 -1
  65. package/dist/src/importers/item-converter.js +27 -2
  66. package/dist/src/importers/item-converter.js.map +1 -1
  67. package/dist/src/importers/rate-limiter.d.ts +128 -0
  68. package/dist/src/importers/rate-limiter.d.ts.map +1 -0
  69. package/dist/src/importers/rate-limiter.js +200 -0
  70. package/dist/src/importers/rate-limiter.js.map +1 -0
  71. package/dist/src/init/compliance/types.d.ts +2 -2
  72. package/dist/src/integrations/ado/ado-client.d.ts +6 -0
  73. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  74. package/dist/src/integrations/ado/ado-client.js +23 -0
  75. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  76. package/dist/src/integrations/jira/jira-client.d.ts +6 -0
  77. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  78. package/dist/src/integrations/jira/jira-client.js +38 -0
  79. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  80. package/dist/src/sync/external-item-sync-service.d.ts +150 -0
  81. package/dist/src/sync/external-item-sync-service.d.ts.map +1 -0
  82. package/dist/src/sync/external-item-sync-service.js +241 -0
  83. package/dist/src/sync/external-item-sync-service.js.map +1 -0
  84. package/dist/src/sync/format-preservation-sync.d.ts +90 -0
  85. package/dist/src/sync/format-preservation-sync.d.ts.map +1 -0
  86. package/dist/src/sync/format-preservation-sync.js +173 -0
  87. package/dist/src/sync/format-preservation-sync.js.map +1 -0
  88. package/dist/src/sync/index.d.ts +8 -0
  89. package/dist/src/sync/index.d.ts.map +1 -0
  90. package/dist/src/sync/index.js +6 -0
  91. package/dist/src/sync/index.js.map +1 -0
  92. package/dist/src/sync/sync-coordinator.d.ts +49 -0
  93. package/dist/src/sync/sync-coordinator.d.ts.map +1 -0
  94. package/dist/src/sync/sync-coordinator.js +248 -0
  95. package/dist/src/sync/sync-coordinator.js.map +1 -0
  96. package/dist/src/sync/sync-metadata.d.ts +75 -0
  97. package/dist/src/sync/sync-metadata.d.ts.map +1 -0
  98. package/dist/src/sync/sync-metadata.js +100 -0
  99. package/dist/src/sync/sync-metadata.js.map +1 -0
  100. package/dist/src/types/living-docs-us-file.d.ts +63 -0
  101. package/dist/src/types/living-docs-us-file.d.ts.map +1 -0
  102. package/dist/src/types/living-docs-us-file.js +27 -0
  103. package/dist/src/types/living-docs-us-file.js.map +1 -0
  104. package/dist/src/validators/format-preservation-validator.d.ts +127 -0
  105. package/dist/src/validators/format-preservation-validator.d.ts.map +1 -0
  106. package/dist/src/validators/format-preservation-validator.js +187 -0
  107. package/dist/src/validators/format-preservation-validator.js.map +1 -0
  108. package/package.json +3 -2
  109. package/plugins/specweave/.claude-plugin/plugin.json +20 -0
  110. package/plugins/specweave/commands/specweave-archive-features.md +11 -1
  111. package/plugins/specweave/commands/specweave-archive.md +51 -15
  112. package/plugins/specweave/commands/specweave-import-docs.md +88 -278
  113. package/plugins/specweave/commands/specweave-import-external.md +407 -0
  114. package/plugins/specweave/hooks/post-edit-spec.sh +94 -0
  115. package/plugins/specweave/hooks/post-increment-completion.sh +0 -0
  116. package/plugins/specweave/hooks/post-spec-update.sh +0 -0
  117. package/plugins/specweave/hooks/post-task-completion.sh +13 -3
  118. package/plugins/specweave/hooks/post-write-spec.sh +91 -0
  119. package/plugins/specweave/lib/hooks/auto-transition.js +1 -1
  120. package/plugins/specweave/lib/hooks/auto-transition.js.bak +50 -0
  121. package/plugins/specweave/lib/hooks/auto-transition.ts +1 -1
  122. package/plugins/specweave/lib/hooks/auto-transition.ts.bak +84 -0
  123. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
  124. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +89 -0
  125. package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +142 -0
  126. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +269 -0
  127. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
  128. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +60 -0
  129. package/plugins/specweave/lib/hooks/invoke-translator-skill.js +1 -1
  130. package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +155 -0
  131. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts +1 -1
  132. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +264 -0
  133. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
  134. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +42 -0
  135. package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +110 -0
  136. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +178 -0
  137. package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
  138. package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +45 -0
  139. package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +92 -0
  140. package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +156 -0
  141. package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
  142. package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +33 -0
  143. package/plugins/specweave/lib/hooks/reflection-parser.js.bak +301 -0
  144. package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +484 -0
  145. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
  146. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +56 -0
  147. package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +182 -0
  148. package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +306 -0
  149. package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
  150. package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +64 -0
  151. package/plugins/specweave/lib/hooks/reflection-storage.js.bak +231 -0
  152. package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +369 -0
  153. package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
  154. package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +43 -0
  155. package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +132 -0
  156. package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +258 -0
  157. package/plugins/specweave/lib/hooks/sync-cache.js.bak +294 -0
  158. package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +1 -0
  159. package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +27 -0
  160. package/plugins/specweave/lib/hooks/sync-living-docs.js +35 -1
  161. package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +339 -0
  162. package/plugins/specweave/lib/hooks/sync-us-tasks.js +179 -3
  163. package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +476 -0
  164. package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
  165. package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +59 -0
  166. package/plugins/specweave/lib/hooks/translate-file.js +1 -1
  167. package/plugins/specweave/lib/hooks/translate-file.js.bak +289 -0
  168. package/plugins/specweave/lib/hooks/translate-file.ts +1 -1
  169. package/plugins/specweave/lib/hooks/translate-file.ts.bak +428 -0
  170. package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
  171. package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +13 -0
  172. package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +119 -0
  173. package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +224 -0
  174. package/plugins/specweave/lib/hooks/update-ac-status.js +1 -1
  175. package/plugins/specweave/lib/hooks/update-ac-status.js.bak +51 -0
  176. package/plugins/specweave/lib/hooks/update-ac-status.ts +1 -1
  177. package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +103 -0
  178. package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +1 -0
  179. package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +29 -0
  180. package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +296 -0
  181. package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +489 -0
  182. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.d.ts +115 -0
  183. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js +345 -0
  184. package/plugins/specweave/lib/vendor/core/increment/ac-status-manager.js.map +1 -0
  185. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.d.ts +106 -0
  186. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js +220 -0
  187. package/plugins/specweave/lib/vendor/core/increment/active-increment-manager.js.map +1 -0
  188. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +60 -0
  189. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +192 -0
  190. package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -0
  191. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.d.ts +52 -0
  192. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js +276 -0
  193. package/plugins/specweave/lib/vendor/core/increment/duplicate-detector.js.map +1 -0
  194. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +163 -0
  195. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +541 -0
  196. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -0
  197. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +157 -0
  198. package/plugins/specweave/lib/vendor/core/types/increment-metadata.js +191 -0
  199. package/plugins/specweave/lib/vendor/core/types/increment-metadata.js.map +1 -0
  200. package/plugins/specweave/lib/vendor/generators/spec/task-parser.d.ts +95 -0
  201. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js +301 -0
  202. package/plugins/specweave/lib/vendor/generators/spec/task-parser.js.map +1 -0
  203. package/plugins/specweave/lib/vendor/utils/logger.d.ts +48 -0
  204. package/plugins/specweave/lib/vendor/utils/logger.js +53 -0
  205. package/plugins/specweave/lib/vendor/utils/logger.js.map +1 -0
  206. package/plugins/specweave/lib/vendor/utils/translation.d.ts +187 -0
  207. package/plugins/specweave/lib/vendor/utils/translation.js +414 -0
  208. package/plugins/specweave/lib/vendor/utils/translation.js.map +1 -0
  209. package/plugins/specweave-github/commands/specweave-github-update-user-story.md +1 -1
  210. package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +1 -1
  211. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +531 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * File Translation CLI Utility
3
+ *
4
+ * Translates a single file from detected source language to target language
5
+ * using the translation utilities and LLM invocation.
6
+ *
7
+ * This script is called from:
8
+ * - Post-increment-planning hook (auto-translate spec.md, plan.md, tasks.md)
9
+ * - Post-task-completion hook (auto-translate living docs)
10
+ * - Manual /specweave:translate command
11
+ *
12
+ * Usage:
13
+ * node translate-file.js <file-path> [--target-lang en] [--preview]
14
+ *
15
+ * @see src/utils/translation.ts
16
+ * @see .specweave/increments/0006-llm-native-i18n/reports/DESIGN-POST-GENERATION-TRANSLATION.md
17
+ */
18
+
19
+ import fs from 'fs-extra';
20
+ import path from 'path';
21
+ import {
22
+ detectLanguage,
23
+ prepareTranslation,
24
+ postProcessTranslation,
25
+ validateTranslation,
26
+ getLanguageName,
27
+ formatCost,
28
+ type SupportedLanguage,
29
+ } from '../../../../dist/src/utils/translation.js';
30
+
31
+ /**
32
+ * CLI options
33
+ */
34
+ interface CLIOptions {
35
+ filePath: string;
36
+ targetLang: SupportedLanguage;
37
+ preview: boolean;
38
+ verbose: boolean;
39
+ }
40
+
41
+ /**
42
+ * Translation result
43
+ */
44
+ interface FileTranslationResult {
45
+ success: boolean;
46
+ filePath: string;
47
+ sourceLanguage: SupportedLanguage;
48
+ targetLanguage: SupportedLanguage;
49
+ warnings: string[];
50
+ cost: number;
51
+ tokensUsed: number;
52
+ preview?: string;
53
+ }
54
+
55
+ /**
56
+ * Main translation function
57
+ *
58
+ * @param options - CLI options
59
+ * @returns Translation result
60
+ */
61
+ export async function translateFile(options: CLIOptions): Promise<FileTranslationResult> {
62
+ const { filePath, targetLang, preview, verbose } = options;
63
+
64
+ // 1. Validate file exists
65
+ if (!await fs.pathExists(filePath)) {
66
+ throw new Error(`File not found: ${filePath}`);
67
+ }
68
+
69
+ if (verbose) {
70
+ console.log(`📄 Reading file: ${filePath}`);
71
+ }
72
+
73
+ // 2. Read original content
74
+ const originalContent = await fs.readFile(filePath, 'utf-8');
75
+
76
+ // 3. Detect source language
77
+ const detectionResult = detectLanguage(originalContent);
78
+ const sourceLanguage = detectionResult.language;
79
+
80
+ if (verbose) {
81
+ console.log(`🔍 Detected language: ${getLanguageName(sourceLanguage)} (confidence: ${(detectionResult.confidence * 100).toFixed(0)}%)`);
82
+ }
83
+
84
+ // 4. Check if already in target language
85
+ if (sourceLanguage === targetLang) {
86
+ if (verbose) {
87
+ console.log(`✅ File already in ${getLanguageName(targetLang)}, skipping translation`);
88
+ }
89
+ return {
90
+ success: true,
91
+ filePath,
92
+ sourceLanguage,
93
+ targetLanguage: targetLang,
94
+ warnings: [`Already in ${getLanguageName(targetLang)}`],
95
+ cost: 0,
96
+ tokensUsed: 0,
97
+ };
98
+ }
99
+
100
+ // 5. Check if source language is unknown
101
+ if (sourceLanguage === 'unknown') {
102
+ if (verbose) {
103
+ console.warn(`⚠️ Could not detect language, assuming English`);
104
+ }
105
+ // Assume English if detection fails
106
+ return {
107
+ success: false,
108
+ filePath,
109
+ sourceLanguage: 'unknown',
110
+ targetLanguage: targetLang,
111
+ warnings: ['Language detection failed - file may already be in English or mixed language'],
112
+ cost: 0,
113
+ tokensUsed: 0,
114
+ };
115
+ }
116
+
117
+ // 6. Prepare translation
118
+ if (verbose) {
119
+ console.log(`🌐 Translating from ${getLanguageName(sourceLanguage)} to ${getLanguageName(targetLang)}...`);
120
+ console.log(`💰 Estimated cost: ${formatCost(0.003)} (using Haiku)`);
121
+ }
122
+
123
+ const prepared = prepareTranslation(originalContent, sourceLanguage, targetLang);
124
+
125
+ // 7. Invoke LLM for translation
126
+ // NOTE: This is where we call the actual LLM
127
+ // For now, we'll create a simple prompt that can be used with Claude Code's Task tool
128
+ const translatedContent = await invokeLLMTranslation(prepared.prompt, verbose);
129
+
130
+ // 8. Post-process translation
131
+ const finalContent = postProcessTranslation(translatedContent, prepared.preserved);
132
+
133
+ // 9. Validate translation
134
+ const warnings = validateTranslation(originalContent, finalContent);
135
+
136
+ if (warnings.length > 0 && verbose) {
137
+ console.warn(`⚠️ Translation warnings:`);
138
+ warnings.forEach(w => console.warn(` - ${w}`));
139
+ }
140
+
141
+ // 10. Preview or write
142
+ if (preview) {
143
+ if (verbose) {
144
+ console.log(`\n📋 PREVIEW (first 500 chars):\n`);
145
+ console.log(finalContent.substring(0, 500));
146
+ console.log(`\n... (${finalContent.length} total characters)\n`);
147
+ }
148
+ return {
149
+ success: true,
150
+ filePath,
151
+ sourceLanguage,
152
+ targetLanguage: targetLang,
153
+ warnings,
154
+ cost: prepared.estimatedCost,
155
+ tokensUsed: prepared.estimatedTokens,
156
+ preview: finalContent,
157
+ };
158
+ } else {
159
+ // Write translated content back to file
160
+ await fs.writeFile(filePath, finalContent, 'utf-8');
161
+
162
+ if (verbose) {
163
+ console.log(`✅ Translation complete: ${filePath}`);
164
+ console.log(` Tokens used: ${prepared.estimatedTokens.toLocaleString()}`);
165
+ console.log(` Cost: ${formatCost(prepared.estimatedCost)}`);
166
+ }
167
+
168
+ return {
169
+ success: true,
170
+ filePath,
171
+ sourceLanguage,
172
+ targetLanguage: targetLang,
173
+ warnings,
174
+ cost: prepared.estimatedCost,
175
+ tokensUsed: prepared.estimatedTokens,
176
+ };
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Invokes LLM for translation using Anthropic API
182
+ *
183
+ * PRODUCTION IMPLEMENTATION:
184
+ * 1. Checks for ANTHROPIC_API_KEY in environment
185
+ * 2. If available: Uses Anthropic API directly (fully automatic)
186
+ * 3. If not available: Provides clear instructions for manual translation
187
+ *
188
+ * @param prompt - Translation prompt
189
+ * @param verbose - Show detailed output
190
+ * @returns Translated content
191
+ */
192
+ async function invokeLLMTranslation(prompt: string, verbose: boolean): Promise<string> {
193
+ // Extract the content to translate (between --- markers)
194
+ const contentMatch = prompt.match(/SOURCE DOCUMENT[^\n]*:\n---\n([\s\S]*?)\n---/);
195
+ const contentToTranslate = contentMatch ? contentMatch[1] : '';
196
+
197
+ // Check if ANTHROPIC_API_KEY is available
198
+ const apiKey = process.env.ANTHROPIC_API_KEY;
199
+
200
+ if (apiKey) {
201
+ // Fully automatic translation using Anthropic API
202
+ if (verbose) {
203
+ console.log(`\n🤖 Translating via Anthropic API (Haiku model)...`);
204
+ }
205
+
206
+ try {
207
+ // Dynamic import of Anthropic SDK (allows graceful fallback if not installed)
208
+ const Anthropic = await import('@anthropic-ai/sdk').then(m => m.default);
209
+
210
+ const anthropic = new Anthropic({
211
+ apiKey,
212
+ });
213
+
214
+ const message = await anthropic.messages.create({
215
+ model: 'claude-3-haiku-20240307',
216
+ max_tokens: 8000,
217
+ messages: [
218
+ {
219
+ role: 'user',
220
+ content: prompt,
221
+ },
222
+ ],
223
+ });
224
+
225
+ // Extract translated content from response
226
+ const translatedContent = message.content[0].type === 'text'
227
+ ? message.content[0].text
228
+ : contentToTranslate;
229
+
230
+ if (verbose) {
231
+ console.log(`✅ Translation complete via API`);
232
+ console.log(` Model: claude-3-haiku-20240307`);
233
+ console.log(` Input tokens: ${message.usage.input_tokens}`);
234
+ console.log(` Output tokens: ${message.usage.output_tokens}`);
235
+ console.log(` Cost: ~$${((message.usage.input_tokens * 0.25 + message.usage.output_tokens * 1.25) / 1000000).toFixed(4)}`);
236
+ }
237
+
238
+ return translatedContent;
239
+ } catch (error: any) {
240
+ console.error(`\n❌ API translation failed: ${error.message}`);
241
+ console.error(` Falling back to manual translation instructions\n`);
242
+ // Fall through to manual instructions
243
+ }
244
+ }
245
+
246
+ // Fallback: Manual translation instructions
247
+ const isInteractive = process.stdout.isTTY && process.env.CLAUDE_CODE_SESSION;
248
+
249
+ if (isInteractive) {
250
+ // Interactive mode: Output prompt for Claude to process
251
+ if (verbose) {
252
+ console.log(`\n🤖 Invoking Claude Code translator skill...`);
253
+ console.log(` (Tip: Set ANTHROPIC_API_KEY for fully automatic translation)\n`);
254
+ }
255
+
256
+ // Output the translation prompt
257
+ // The translator skill should auto-activate on this prompt
258
+ console.log('\n' + '='.repeat(80));
259
+ console.log('TRANSLATION REQUEST (translator skill will auto-activate):');
260
+ console.log('='.repeat(80));
261
+ console.log(prompt);
262
+ console.log('='.repeat(80) + '\n');
263
+
264
+ // In interactive mode, we expect the user/Claude to provide translation
265
+ // For now, return a marker indicating manual intervention needed
266
+ return `<!-- ⚠️ TRANSLATION IN PROGRESS - Manual translation required via translator skill -->\n\n${contentToTranslate}`;
267
+ } else {
268
+ // Non-interactive/automated mode: Provide clear instructions
269
+ if (verbose) {
270
+ console.log(`\n🤖 Generating translation (automated mode)...`);
271
+ }
272
+
273
+ console.error('\n⚠️ AUTO-TRANSLATION REQUIRES MANUAL STEP:');
274
+ console.error(' Option A (Recommended): Set ANTHROPIC_API_KEY environment variable');
275
+ console.error(' Option B: Run /specweave:translate <file-path>');
276
+ console.error(' Option C: Manually translate the content\n');
277
+
278
+ // Return original content with clear marker
279
+ return `<!-- ⚠️ AUTO-TRANSLATION PENDING -->\n<!-- Set ANTHROPIC_API_KEY for automatic translation -->\n<!-- Or run: /specweave:translate to complete -->\n<!-- Original content below -->\n\n${contentToTranslate}`;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Batch translate multiple files
285
+ *
286
+ * @param filePaths - Array of file paths to translate
287
+ * @param targetLang - Target language
288
+ * @param preview - Preview mode
289
+ * @param verbose - Verbose output
290
+ * @returns Array of translation results
291
+ */
292
+ export async function batchTranslateFiles(
293
+ filePaths: string[],
294
+ targetLang: SupportedLanguage = 'en',
295
+ preview: boolean = false,
296
+ verbose: boolean = false
297
+ ): Promise<FileTranslationResult[]> {
298
+ const results: FileTranslationResult[] = [];
299
+
300
+ if (verbose) {
301
+ console.log(`\n🔄 Batch translating ${filePaths.length} file(s) to ${getLanguageName(targetLang)}...\n`);
302
+ }
303
+
304
+ for (const filePath of filePaths) {
305
+ try {
306
+ const result = await translateFile({
307
+ filePath,
308
+ targetLang,
309
+ preview,
310
+ verbose,
311
+ });
312
+ results.push(result);
313
+ } catch (error: any) {
314
+ if (verbose) {
315
+ console.error(`❌ Error translating ${filePath}: ${error.message}`);
316
+ }
317
+ results.push({
318
+ success: false,
319
+ filePath,
320
+ sourceLanguage: 'unknown',
321
+ targetLanguage: targetLang,
322
+ warnings: [error.message],
323
+ cost: 0,
324
+ tokensUsed: 0,
325
+ });
326
+ }
327
+ }
328
+
329
+ // Summary
330
+ if (verbose) {
331
+ const successful = results.filter(r => r.success).length;
332
+ const totalCost = results.reduce((sum, r) => sum + r.cost, 0);
333
+ const totalTokens = results.reduce((sum, r) => sum + r.tokensUsed, 0);
334
+
335
+ console.log(`\n📊 Batch Translation Summary:`);
336
+ console.log(` Successful: ${successful}/${filePaths.length}`);
337
+ console.log(` Total tokens: ${totalTokens.toLocaleString()}`);
338
+ console.log(` Total cost: ${formatCost(totalCost)}`);
339
+ }
340
+
341
+ return results;
342
+ }
343
+
344
+ /**
345
+ * Parse CLI arguments
346
+ */
347
+ function parseArgs(): CLIOptions {
348
+ const args = process.argv.slice(2);
349
+
350
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
351
+ console.log(`
352
+ Translation CLI Utility
353
+
354
+ Usage:
355
+ node translate-file.js <file-path> [options]
356
+
357
+ Options:
358
+ --target-lang <code> Target language (default: en)
359
+ --preview Preview translation without writing to file
360
+ --verbose, -v Show detailed output
361
+ --help, -h Show this help message
362
+
363
+ Supported Languages:
364
+ en (English), ru (Russian), es (Spanish), zh (Chinese),
365
+ de (German), fr (French), ja (Japanese), ko (Korean),
366
+ pt (Portuguese), ar (Arabic), he (Hebrew)
367
+
368
+ Examples:
369
+ # Translate Russian file to English
370
+ node translate-file.js .specweave/increments/0001/spec.md
371
+
372
+ # Preview translation
373
+ node translate-file.js spec.md --preview --verbose
374
+
375
+ # Translate to Spanish
376
+ node translate-file.js plan.md --target-lang es
377
+ `.trim());
378
+ process.exit(0);
379
+ }
380
+
381
+ const filePath = args[0];
382
+ let targetLang: SupportedLanguage = 'en';
383
+ let preview = false;
384
+ let verbose = false;
385
+
386
+ // Parse options
387
+ for (let i = 1; i < args.length; i++) {
388
+ const arg = args[i];
389
+
390
+ if (arg === '--target-lang' && args[i + 1]) {
391
+ targetLang = args[i + 1] as SupportedLanguage;
392
+ i++;
393
+ } else if (arg === '--preview') {
394
+ preview = true;
395
+ } else if (arg === '--verbose' || arg === '-v') {
396
+ verbose = true;
397
+ }
398
+ }
399
+
400
+ return {
401
+ filePath,
402
+ targetLang,
403
+ preview,
404
+ verbose,
405
+ };
406
+ }
407
+
408
+ /**
409
+ * CLI entry point
410
+ */
411
+ async function main(): Promise<void> {
412
+ try {
413
+ const options = parseArgs();
414
+ const result = await translateFile(options);
415
+
416
+ // Exit with appropriate code
417
+ process.exit(result.success ? 0 : 1);
418
+ } catch (error: any) {
419
+ console.error(`❌ Translation failed: ${error.message}`);
420
+ process.exit(1);
421
+ }
422
+ }
423
+
424
+ // Check if running as main module (ESM)
425
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
426
+ if (isMainModule) {
427
+ main();
428
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Living Docs Auto-Translation
3
+ *
4
+ * Automatically translates changed documentation files when language != 'en'
5
+ * and auto-translation is enabled in config.
6
+ *
7
+ * This runs as part of the post-task-completion hook.
8
+ */
9
+ /**
10
+ * Main function: Translate changed living docs
11
+ */
12
+ export declare function translateLivingDocs(incrementId: string): Promise<void>;
13
+ //# sourceMappingURL=translate-living-docs.d.ts.map
@@ -0,0 +1,119 @@
1
+ import { execSync } from "child_process";
2
+ import fs from "fs-extra";
3
+ async function translateLivingDocs(incrementId) {
4
+ try {
5
+ const configPath = ".specweave/config.json";
6
+ if (!fs.existsSync(configPath)) {
7
+ console.log("[translate-living-docs] No config found, skipping translation");
8
+ return;
9
+ }
10
+ const config = await fs.readJson(configPath);
11
+ if (!config.language || config.language === "en") {
12
+ console.log("[translate-living-docs] Project language is English, skipping translation");
13
+ return;
14
+ }
15
+ if (!config.translation?.autoTranslateLivingDocs) {
16
+ console.log("[translate-living-docs] Auto-translation disabled in config");
17
+ return;
18
+ }
19
+ console.log(`[translate-living-docs] Auto-translating docs from ${config.language} to English...`);
20
+ const changedFiles = await detectChangedDocs();
21
+ if (changedFiles.length === 0) {
22
+ console.log("[translate-living-docs] No documentation changes detected");
23
+ return;
24
+ }
25
+ console.log(`[translate-living-docs] Found ${changedFiles.length} changed file(s)`);
26
+ for (const file of changedFiles) {
27
+ try {
28
+ await translateFile(file, "en", config.translation);
29
+ console.log(`[translate-living-docs] \u2713 Translated: ${file} (${config.language} \u2192 en)`);
30
+ } catch (error) {
31
+ console.warn(`[translate-living-docs] \u26A0\uFE0F Failed to translate ${file}: ${error.message}`);
32
+ }
33
+ }
34
+ console.log(`[translate-living-docs] \u2705 Translation complete (${changedFiles.length} files)`);
35
+ } catch (error) {
36
+ console.error(`[translate-living-docs] Error: ${error.message}`);
37
+ }
38
+ }
39
+ async function detectChangedDocs() {
40
+ try {
41
+ const output = execSync(
42
+ "git diff --name-only .specweave/docs/",
43
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }
44
+ );
45
+ const files = output.split("\n").filter((f) => f.trim() && f.endsWith(".md")).map((f) => f.trim());
46
+ return files;
47
+ } catch (error) {
48
+ return [];
49
+ }
50
+ }
51
+ async function translateFile(filePath, targetLanguage, translationConfig) {
52
+ const originalContent = await fs.readFile(filePath, "utf-8");
53
+ if (originalContent.includes(`<!-- Translated to ${targetLanguage} -->`)) {
54
+ console.log(`[translate-living-docs] File already translated, skipping: ${filePath}`);
55
+ return;
56
+ }
57
+ const translationPrompt = generateTranslationPrompt(
58
+ originalContent,
59
+ targetLanguage,
60
+ translationConfig
61
+ );
62
+ const markedContent = `<!-- Translation needed to ${targetLanguage} -->
63
+ <!-- Original content below -->
64
+
65
+ ${originalContent}`;
66
+ console.log(`[translate-living-docs] Translation prompt generated for: ${filePath}`);
67
+ }
68
+ function generateTranslationPrompt(content, targetLanguage, translationConfig) {
69
+ const keepFrameworkTerms = translationConfig?.keepFrameworkTerms !== false;
70
+ const keepTechnicalTerms = translationConfig?.keepTechnicalTerms !== false;
71
+ return `
72
+ Translate the following markdown documentation to ${getLanguageName(targetLanguage)}.
73
+
74
+ CRITICAL RULES:
75
+ ${keepFrameworkTerms ? "- Keep framework terms in English: increment, spec.md, plan.md, tasks.md, COMPLETION-SUMMARY.md, living docs, PM gate, RFC, ADR, PRD, HLD, LLD" : ""}
76
+ ${keepTechnicalTerms ? "- Keep technical terms in English: TypeScript, npm, git, Docker, Kubernetes, API, CLI, REST, JSON, HTTP" : ""}
77
+ - Preserve ALL markdown formatting (headers, lists, code blocks, links)
78
+ - Do NOT translate code blocks
79
+ - Do NOT translate YAML frontmatter keys (only values if applicable)
80
+ - Preserve all emojis
81
+ - Preserve all file paths and URLs
82
+
83
+ Content to translate:
84
+ ---
85
+ ${content}
86
+ ---
87
+
88
+ Translated version:
89
+ `.trim();
90
+ }
91
+ function getLanguageName(code) {
92
+ const names = {
93
+ en: "English",
94
+ ru: "Russian (\u0420\u0443\u0441\u0441\u043A\u0438\u0439)",
95
+ es: "Spanish (Espa\xF1ol)",
96
+ zh: "Chinese (\u4E2D\u6587)",
97
+ de: "German (Deutsch)",
98
+ fr: "French (Fran\xE7ais)",
99
+ ja: "Japanese (\u65E5\u672C\u8A9E)",
100
+ ko: "Korean (\uD55C\uAD6D\uC5B4)",
101
+ pt: "Portuguese (Portugu\xEAs)"
102
+ };
103
+ return names[code] || code;
104
+ }
105
+ async function main() {
106
+ const args = process.argv.slice(2);
107
+ const incrementId = args[0] || "current";
108
+ await translateLivingDocs(incrementId);
109
+ }
110
+ const isMainModule = import.meta.url === `file://${process.argv[1]}`;
111
+ if (isMainModule) {
112
+ main().catch((error) => {
113
+ console.error("[translate-living-docs] Fatal error:", error);
114
+ process.exit(1);
115
+ });
116
+ }
117
+ export {
118
+ translateLivingDocs
119
+ };