specweave 0.26.2 → 0.26.3

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 (101) hide show
  1. package/dist/src/config/types.d.ts +1208 -203
  2. package/dist/src/config/types.d.ts.map +1 -1
  3. package/dist/src/init/architecture/types.d.ts +140 -33
  4. package/dist/src/init/architecture/types.d.ts.map +1 -1
  5. package/dist/src/init/compliance/types.d.ts +27 -30
  6. package/dist/src/init/compliance/types.d.ts.map +1 -1
  7. package/dist/src/init/repo/types.d.ts +34 -11
  8. package/dist/src/init/repo/types.d.ts.map +1 -1
  9. package/dist/src/init/research/src/config/types.d.ts +82 -15
  10. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  11. package/dist/src/init/research/types.d.ts +93 -38
  12. package/dist/src/init/research/types.d.ts.map +1 -1
  13. package/dist/src/init/team/types.d.ts +42 -4
  14. package/dist/src/init/team/types.d.ts.map +1 -1
  15. package/package.json +1 -1
  16. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +0 -1
  17. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  18. package/plugins/specweave/agents/pm/AGENT.md.bak +0 -1893
  19. package/plugins/specweave/hooks/docs-changed.sh.backup +0 -79
  20. package/plugins/specweave/hooks/human-input-required.sh.backup +0 -75
  21. package/plugins/specweave/hooks/lib/migrate-increment-work.sh.bak +0 -245
  22. package/plugins/specweave/hooks/lib/sync-spec-content.sh.bak +0 -149
  23. package/plugins/specweave/hooks/lib/validate-spec-status.sh.bak +0 -163
  24. package/plugins/specweave/hooks/post-first-increment.sh.backup +0 -61
  25. package/plugins/specweave/hooks/post-first-increment.sh.bak +0 -61
  26. package/plugins/specweave/hooks/post-increment-change.sh.backup +0 -98
  27. package/plugins/specweave/hooks/post-increment-completion.sh.backup +0 -231
  28. package/plugins/specweave/hooks/post-increment-planning.sh.backup +0 -1048
  29. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +0 -147
  30. package/plugins/specweave/hooks/post-spec-update.sh.backup +0 -158
  31. package/plugins/specweave/hooks/post-spec-update.sh.bak +0 -158
  32. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +0 -179
  33. package/plugins/specweave/hooks/post-user-story-complete.sh.bak +0 -179
  34. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +0 -83
  35. package/plugins/specweave/hooks/pre-command-deduplication.sh.bak +0 -83
  36. package/plugins/specweave/hooks/pre-implementation.sh.backup +0 -67
  37. package/plugins/specweave/hooks/pre-task-completion.sh.backup +0 -194
  38. package/plugins/specweave/hooks/pre-tool-use.sh.backup +0 -133
  39. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +0 -386
  40. package/plugins/specweave/hooks/user-prompt-submit.sh.bak +0 -386
  41. package/plugins/specweave/lib/hooks/auto-transition.js.bak +0 -50
  42. package/plugins/specweave/lib/hooks/auto-transition.ts.bak +0 -84
  43. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.js.bak +0 -0
  44. package/plugins/specweave/lib/hooks/git-diff-analyzer.d.ts.bak +0 -89
  45. package/plugins/specweave/lib/hooks/git-diff-analyzer.js.bak +0 -142
  46. package/plugins/specweave/lib/hooks/git-diff-analyzer.ts.bak +0 -269
  47. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.js.bak +0 -0
  48. package/plugins/specweave/lib/hooks/invoke-translator-skill.d.ts.bak +0 -60
  49. package/plugins/specweave/lib/hooks/invoke-translator-skill.js.bak +0 -155
  50. package/plugins/specweave/lib/hooks/invoke-translator-skill.ts.bak +0 -264
  51. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.js.bak +0 -0
  52. package/plugins/specweave/lib/hooks/prepare-reflection-context.d.ts.bak +0 -42
  53. package/plugins/specweave/lib/hooks/prepare-reflection-context.js.bak +0 -110
  54. package/plugins/specweave/lib/hooks/prepare-reflection-context.ts.bak +0 -178
  55. package/plugins/specweave/lib/hooks/reflection-config-loader.d.js.bak +0 -0
  56. package/plugins/specweave/lib/hooks/reflection-config-loader.d.ts.bak +0 -45
  57. package/plugins/specweave/lib/hooks/reflection-config-loader.js.bak +0 -92
  58. package/plugins/specweave/lib/hooks/reflection-config-loader.ts.bak +0 -156
  59. package/plugins/specweave/lib/hooks/reflection-parser.d.js.bak +0 -0
  60. package/plugins/specweave/lib/hooks/reflection-parser.d.ts.bak +0 -33
  61. package/plugins/specweave/lib/hooks/reflection-parser.js.bak +0 -301
  62. package/plugins/specweave/lib/hooks/reflection-parser.ts.bak +0 -484
  63. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.js.bak +0 -0
  64. package/plugins/specweave/lib/hooks/reflection-prompt-builder.d.ts.bak +0 -56
  65. package/plugins/specweave/lib/hooks/reflection-prompt-builder.js.bak +0 -182
  66. package/plugins/specweave/lib/hooks/reflection-prompt-builder.ts.bak +0 -306
  67. package/plugins/specweave/lib/hooks/reflection-storage.d.js.bak +0 -0
  68. package/plugins/specweave/lib/hooks/reflection-storage.d.ts.bak +0 -64
  69. package/plugins/specweave/lib/hooks/reflection-storage.js.bak +0 -231
  70. package/plugins/specweave/lib/hooks/reflection-storage.ts.bak +0 -369
  71. package/plugins/specweave/lib/hooks/run-self-reflection.d.js.bak +0 -0
  72. package/plugins/specweave/lib/hooks/run-self-reflection.d.ts.bak +0 -43
  73. package/plugins/specweave/lib/hooks/run-self-reflection.js.bak +0 -132
  74. package/plugins/specweave/lib/hooks/run-self-reflection.ts.bak +0 -258
  75. package/plugins/specweave/lib/hooks/sync-cache.js.bak +0 -294
  76. package/plugins/specweave/lib/hooks/sync-living-docs.d.js.bak +0 -1
  77. package/plugins/specweave/lib/hooks/sync-living-docs.d.ts.bak +0 -27
  78. package/plugins/specweave/lib/hooks/sync-living-docs.js.bak +0 -339
  79. package/plugins/specweave/lib/hooks/sync-us-tasks.js.bak +0 -476
  80. package/plugins/specweave/lib/hooks/translate-file.d.js.bak +0 -0
  81. package/plugins/specweave/lib/hooks/translate-file.d.ts.bak +0 -59
  82. package/plugins/specweave/lib/hooks/translate-file.js.bak +0 -289
  83. package/plugins/specweave/lib/hooks/translate-file.ts.bak +0 -428
  84. package/plugins/specweave/lib/hooks/translate-living-docs.d.js.bak +0 -0
  85. package/plugins/specweave/lib/hooks/translate-living-docs.d.ts.bak +0 -13
  86. package/plugins/specweave/lib/hooks/translate-living-docs.js.bak +0 -119
  87. package/plugins/specweave/lib/hooks/translate-living-docs.ts.bak +0 -224
  88. package/plugins/specweave/lib/hooks/update-ac-status.js.bak +0 -51
  89. package/plugins/specweave/lib/hooks/update-ac-status.ts.bak +0 -103
  90. package/plugins/specweave/lib/hooks/update-tasks-md.d.js.bak +0 -1
  91. package/plugins/specweave/lib/hooks/update-tasks-md.d.ts.bak +0 -29
  92. package/plugins/specweave/lib/hooks/update-tasks-md.js.bak +0 -296
  93. package/plugins/specweave/lib/hooks/update-tasks-md.ts.bak +0 -489
  94. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +0 -353
  95. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +0 -172
  96. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
  97. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -904
  98. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +0 -258
  99. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +0 -172
  100. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -738
  101. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +0 -110
@@ -1,289 +0,0 @@
1
- import fs from "fs-extra";
2
- import {
3
- detectLanguage,
4
- prepareTranslation,
5
- postProcessTranslation,
6
- validateTranslation,
7
- getLanguageName,
8
- formatCost
9
- } from "../../../../dist/src/utils/translation.js";
10
- async function translateFile(options) {
11
- const { filePath, targetLang, preview, verbose } = options;
12
- if (!await fs.pathExists(filePath)) {
13
- throw new Error(`File not found: ${filePath}`);
14
- }
15
- if (verbose) {
16
- console.log(`\u{1F4C4} Reading file: ${filePath}`);
17
- }
18
- const originalContent = await fs.readFile(filePath, "utf-8");
19
- const detectionResult = detectLanguage(originalContent);
20
- const sourceLanguage = detectionResult.language;
21
- if (verbose) {
22
- console.log(`\u{1F50D} Detected language: ${getLanguageName(sourceLanguage)} (confidence: ${(detectionResult.confidence * 100).toFixed(0)}%)`);
23
- }
24
- if (sourceLanguage === targetLang) {
25
- if (verbose) {
26
- console.log(`\u2705 File already in ${getLanguageName(targetLang)}, skipping translation`);
27
- }
28
- return {
29
- success: true,
30
- filePath,
31
- sourceLanguage,
32
- targetLanguage: targetLang,
33
- warnings: [`Already in ${getLanguageName(targetLang)}`],
34
- cost: 0,
35
- tokensUsed: 0
36
- };
37
- }
38
- if (sourceLanguage === "unknown") {
39
- if (verbose) {
40
- console.warn(`\u26A0\uFE0F Could not detect language, assuming English`);
41
- }
42
- return {
43
- success: false,
44
- filePath,
45
- sourceLanguage: "unknown",
46
- targetLanguage: targetLang,
47
- warnings: ["Language detection failed - file may already be in English or mixed language"],
48
- cost: 0,
49
- tokensUsed: 0
50
- };
51
- }
52
- if (verbose) {
53
- console.log(`\u{1F310} Translating from ${getLanguageName(sourceLanguage)} to ${getLanguageName(targetLang)}...`);
54
- console.log(`\u{1F4B0} Estimated cost: ${formatCost(3e-3)} (using Haiku)`);
55
- }
56
- const prepared = prepareTranslation(originalContent, sourceLanguage, targetLang);
57
- const translatedContent = await invokeLLMTranslation(prepared.prompt, verbose);
58
- const finalContent = postProcessTranslation(translatedContent, prepared.preserved);
59
- const warnings = validateTranslation(originalContent, finalContent);
60
- if (warnings.length > 0 && verbose) {
61
- console.warn(`\u26A0\uFE0F Translation warnings:`);
62
- warnings.forEach((w) => console.warn(` - ${w}`));
63
- }
64
- if (preview) {
65
- if (verbose) {
66
- console.log(`
67
- \u{1F4CB} PREVIEW (first 500 chars):
68
- `);
69
- console.log(finalContent.substring(0, 500));
70
- console.log(`
71
- ... (${finalContent.length} total characters)
72
- `);
73
- }
74
- return {
75
- success: true,
76
- filePath,
77
- sourceLanguage,
78
- targetLanguage: targetLang,
79
- warnings,
80
- cost: prepared.estimatedCost,
81
- tokensUsed: prepared.estimatedTokens,
82
- preview: finalContent
83
- };
84
- } else {
85
- await fs.writeFile(filePath, finalContent, "utf-8");
86
- if (verbose) {
87
- console.log(`\u2705 Translation complete: ${filePath}`);
88
- console.log(` Tokens used: ${prepared.estimatedTokens.toLocaleString()}`);
89
- console.log(` Cost: ${formatCost(prepared.estimatedCost)}`);
90
- }
91
- return {
92
- success: true,
93
- filePath,
94
- sourceLanguage,
95
- targetLanguage: targetLang,
96
- warnings,
97
- cost: prepared.estimatedCost,
98
- tokensUsed: prepared.estimatedTokens
99
- };
100
- }
101
- }
102
- async function invokeLLMTranslation(prompt, verbose) {
103
- const contentMatch = prompt.match(/SOURCE DOCUMENT[^\n]*:\n---\n([\s\S]*?)\n---/);
104
- const contentToTranslate = contentMatch ? contentMatch[1] : "";
105
- const apiKey = process.env.ANTHROPIC_API_KEY;
106
- if (apiKey) {
107
- if (verbose) {
108
- console.log(`
109
- \u{1F916} Translating via Anthropic API (Haiku model)...`);
110
- }
111
- try {
112
- const Anthropic = await import("@anthropic-ai/sdk").then((m) => m.default);
113
- const anthropic = new Anthropic({
114
- apiKey
115
- });
116
- const message = await anthropic.messages.create({
117
- model: "claude-3-haiku-20240307",
118
- max_tokens: 8e3,
119
- messages: [
120
- {
121
- role: "user",
122
- content: prompt
123
- }
124
- ]
125
- });
126
- const translatedContent = message.content[0].type === "text" ? message.content[0].text : contentToTranslate;
127
- if (verbose) {
128
- console.log(`\u2705 Translation complete via API`);
129
- console.log(` Model: claude-3-haiku-20240307`);
130
- console.log(` Input tokens: ${message.usage.input_tokens}`);
131
- console.log(` Output tokens: ${message.usage.output_tokens}`);
132
- console.log(` Cost: ~$${((message.usage.input_tokens * 0.25 + message.usage.output_tokens * 1.25) / 1e6).toFixed(4)}`);
133
- }
134
- return translatedContent;
135
- } catch (error) {
136
- console.error(`
137
- \u274C API translation failed: ${error.message}`);
138
- console.error(` Falling back to manual translation instructions
139
- `);
140
- }
141
- }
142
- const isInteractive = process.stdout.isTTY && process.env.CLAUDE_CODE_SESSION;
143
- if (isInteractive) {
144
- if (verbose) {
145
- console.log(`
146
- \u{1F916} Invoking Claude Code translator skill...`);
147
- console.log(` (Tip: Set ANTHROPIC_API_KEY for fully automatic translation)
148
- `);
149
- }
150
- console.log("\n" + "=".repeat(80));
151
- console.log("TRANSLATION REQUEST (translator skill will auto-activate):");
152
- console.log("=".repeat(80));
153
- console.log(prompt);
154
- console.log("=".repeat(80) + "\n");
155
- return `<!-- \u26A0\uFE0F TRANSLATION IN PROGRESS - Manual translation required via translator skill -->
156
-
157
- ${contentToTranslate}`;
158
- } else {
159
- if (verbose) {
160
- console.log(`
161
- \u{1F916} Generating translation (automated mode)...`);
162
- }
163
- console.error("\n\u26A0\uFE0F AUTO-TRANSLATION REQUIRES MANUAL STEP:");
164
- console.error(" Option A (Recommended): Set ANTHROPIC_API_KEY environment variable");
165
- console.error(" Option B: Run /specweave:translate <file-path>");
166
- console.error(" Option C: Manually translate the content\n");
167
- return `<!-- \u26A0\uFE0F AUTO-TRANSLATION PENDING -->
168
- <!-- Set ANTHROPIC_API_KEY for automatic translation -->
169
- <!-- Or run: /specweave:translate to complete -->
170
- <!-- Original content below -->
171
-
172
- ${contentToTranslate}`;
173
- }
174
- }
175
- async function batchTranslateFiles(filePaths, targetLang = "en", preview = false, verbose = false) {
176
- const results = [];
177
- if (verbose) {
178
- console.log(`
179
- \u{1F504} Batch translating ${filePaths.length} file(s) to ${getLanguageName(targetLang)}...
180
- `);
181
- }
182
- for (const filePath of filePaths) {
183
- try {
184
- const result = await translateFile({
185
- filePath,
186
- targetLang,
187
- preview,
188
- verbose
189
- });
190
- results.push(result);
191
- } catch (error) {
192
- if (verbose) {
193
- console.error(`\u274C Error translating ${filePath}: ${error.message}`);
194
- }
195
- results.push({
196
- success: false,
197
- filePath,
198
- sourceLanguage: "unknown",
199
- targetLanguage: targetLang,
200
- warnings: [error.message],
201
- cost: 0,
202
- tokensUsed: 0
203
- });
204
- }
205
- }
206
- if (verbose) {
207
- const successful = results.filter((r) => r.success).length;
208
- const totalCost = results.reduce((sum, r) => sum + r.cost, 0);
209
- const totalTokens = results.reduce((sum, r) => sum + r.tokensUsed, 0);
210
- console.log(`
211
- \u{1F4CA} Batch Translation Summary:`);
212
- console.log(` Successful: ${successful}/${filePaths.length}`);
213
- console.log(` Total tokens: ${totalTokens.toLocaleString()}`);
214
- console.log(` Total cost: ${formatCost(totalCost)}`);
215
- }
216
- return results;
217
- }
218
- function parseArgs() {
219
- const args = process.argv.slice(2);
220
- if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
221
- console.log(`
222
- Translation CLI Utility
223
-
224
- Usage:
225
- node translate-file.js <file-path> [options]
226
-
227
- Options:
228
- --target-lang <code> Target language (default: en)
229
- --preview Preview translation without writing to file
230
- --verbose, -v Show detailed output
231
- --help, -h Show this help message
232
-
233
- Supported Languages:
234
- en (English), ru (Russian), es (Spanish), zh (Chinese),
235
- de (German), fr (French), ja (Japanese), ko (Korean),
236
- pt (Portuguese), ar (Arabic), he (Hebrew)
237
-
238
- Examples:
239
- # Translate Russian file to English
240
- node translate-file.js .specweave/increments/0001/spec.md
241
-
242
- # Preview translation
243
- node translate-file.js spec.md --preview --verbose
244
-
245
- # Translate to Spanish
246
- node translate-file.js plan.md --target-lang es
247
- `.trim());
248
- process.exit(0);
249
- }
250
- const filePath = args[0];
251
- let targetLang = "en";
252
- let preview = false;
253
- let verbose = false;
254
- for (let i = 1; i < args.length; i++) {
255
- const arg = args[i];
256
- if (arg === "--target-lang" && args[i + 1]) {
257
- targetLang = args[i + 1];
258
- i++;
259
- } else if (arg === "--preview") {
260
- preview = true;
261
- } else if (arg === "--verbose" || arg === "-v") {
262
- verbose = true;
263
- }
264
- }
265
- return {
266
- filePath,
267
- targetLang,
268
- preview,
269
- verbose
270
- };
271
- }
272
- async function main() {
273
- try {
274
- const options = parseArgs();
275
- const result = await translateFile(options);
276
- process.exit(result.success ? 0 : 1);
277
- } catch (error) {
278
- console.error(`\u274C Translation failed: ${error.message}`);
279
- process.exit(1);
280
- }
281
- }
282
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
283
- if (isMainModule) {
284
- main();
285
- }
286
- export {
287
- batchTranslateFiles,
288
- translateFile
289
- };
@@ -1,428 +0,0 @@
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
- }