locai 2.4.0 → 2.5.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.
- package/README.md +5 -1
- package/dist/cli.js +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/core/orchestrator.d.ts +3 -0
- package/dist/core/orchestrator.js +27 -0
- package/dist/core/pipeline/steps/ContextEnrichmentStep.d.ts +9 -0
- package/dist/core/pipeline/steps/ContextEnrichmentStep.js +43 -0
- package/dist/services/source-analyzer.d.ts +14 -0
- package/dist/services/source-analyzer.js +164 -0
- package/dist/services/translation-service.js +6 -0
- package/dist/types/context.d.ts +13 -0
- package/dist/types/context.js +1 -0
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ Translate your project in seconds without installing anything:
|
|
|
13
13
|
npx locai translate --source en --targets tr,es,de
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
> **Need a permanent setup?** Check out our [Configuration Guide](./docs/configuration.md) to create a `localize.config.js` file.
|
|
17
|
+
|
|
16
18
|
## 📖 Documentation
|
|
17
19
|
|
|
18
20
|
Everything you need to know to master Locai.
|
|
@@ -37,7 +39,9 @@ Choose the right brain for your localization. Compare OpenAI, Gemini, DeepSeek,
|
|
|
37
39
|
|
|
38
40
|
Deep dive into Locai's intelligent features. We didn't just wrap an API; we built a localization engine.
|
|
39
41
|
|
|
40
|
-
-
|
|
42
|
+
- **🧠 Smart Context Awareness**: Uses AI to understand the context of your strings for better translations.
|
|
43
|
+
- **🔬 Deep Context Analysis**: Scans your source code (AST) to extract component names, comments, and props for context-aware translations.
|
|
44
|
+
- **📚 Infinite Memory**: Uses vector embeddings to remember previous translations and maintain consistency. If you translate the same (or similar) sentence again, we fetch it from local cache instantly. Zero cost, 100% consistency.
|
|
41
45
|
- **Smart Sync**: We hash every key. Locai only translates **new** or **modified** keys. If you have 1000 keys and change 5, we only send 5 to the AI.
|
|
42
46
|
- **Context Awareness**: Locai analyzes your content. It knows if a string is for a **Button** (keep it short), **Marketing** (make it punchy), or **Legal** (be precise).
|
|
43
47
|
- **Glossary & Brand Safety**: Define immutable terms (like "Locai", "API", "SaaS"). We guarantee they are never translated or corrupted.
|
package/dist/cli.js
CHANGED
|
@@ -26,6 +26,7 @@ const main = async () => {
|
|
|
26
26
|
.option("-s, --source <lang>", "Source language", defaultConfig.source)
|
|
27
27
|
.option("-t, --targets <langs>", "Target languages (comma separated)", (val) => val.split(","), defaultConfig.targets)
|
|
28
28
|
.option("--localesDir <dir>", "Localization files directory", defaultConfig.localesDir)
|
|
29
|
+
.option("--no-deep-context", "Disable deep context analysis (source code scanning)")
|
|
29
30
|
.option("--debug", "Enable debug mode with verbose logging", false)
|
|
30
31
|
.option("--verbose", "Enable detailed diagnostic output", false);
|
|
31
32
|
program.on("option:debug", function () {
|
package/dist/config/index.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export interface LocalizeConfig extends Omit<Partial<OrchestratorOptions>, "cont
|
|
|
9
9
|
* Target language codes (e.g. ['tr', 'es'])
|
|
10
10
|
*/
|
|
11
11
|
targets?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Deep Context (Source Code Analysis)
|
|
14
|
+
* Scans source code for context (component name, comments, props)
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
deepContext?: boolean;
|
|
12
18
|
/**
|
|
13
19
|
* Filter by file extensions (e.g. ['.json', '.arb'])
|
|
14
20
|
*/
|
|
@@ -6,8 +6,10 @@ import GlossaryManager from "../utils/glossary-manager.js";
|
|
|
6
6
|
import { TranslationResult } from "./pipeline/context.js";
|
|
7
7
|
import { CacheStats } from "./pipeline/steps/CacheReadStep.js";
|
|
8
8
|
import { ConfidenceSettings } from "./pipeline/steps/TranslationStep.js";
|
|
9
|
+
import { SourceCodeAnalyzer } from "../services/source-analyzer.js";
|
|
9
10
|
export interface OrchestratorOptions {
|
|
10
11
|
context: ContextConfig;
|
|
12
|
+
deepContext?: boolean;
|
|
11
13
|
progressOptions?: any;
|
|
12
14
|
styleGuide?: any;
|
|
13
15
|
lengthControl?: any;
|
|
@@ -78,6 +80,7 @@ declare class Orchestrator {
|
|
|
78
80
|
vectorStore: VectorStore;
|
|
79
81
|
embeddingProvider: EmbeddingProvider;
|
|
80
82
|
vectorStats: VectorMemoryStats;
|
|
83
|
+
sourceAnalyzer: SourceCodeAnalyzer;
|
|
81
84
|
confidenceSettings: ConfidenceSettings;
|
|
82
85
|
private concurrencyLimit;
|
|
83
86
|
private pipeline;
|
|
@@ -22,6 +22,8 @@ import QualityCheckStep from "./pipeline/steps/QualityCheckStep.js";
|
|
|
22
22
|
import ToneCheckStep from "./pipeline/steps/ToneCheckStep.js";
|
|
23
23
|
import ConfidenceCheckStep from "./pipeline/steps/ConfidenceCheckStep.js";
|
|
24
24
|
import CacheWriteStep from "./pipeline/steps/CacheWriteStep.js";
|
|
25
|
+
import ContextEnrichmentStep from "./pipeline/steps/ContextEnrichmentStep.js";
|
|
26
|
+
import { SourceCodeAnalyzer } from "../services/source-analyzer.js";
|
|
25
27
|
// Vector Memory Imports
|
|
26
28
|
import { VectorStore } from "../services/vector-store.js";
|
|
27
29
|
import { EmbeddingProvider } from "../services/embedding-provider.js";
|
|
@@ -41,6 +43,8 @@ class Orchestrator {
|
|
|
41
43
|
vectorStore;
|
|
42
44
|
embeddingProvider;
|
|
43
45
|
vectorStats;
|
|
46
|
+
// Source Code Analysis
|
|
47
|
+
sourceAnalyzer;
|
|
44
48
|
confidenceSettings;
|
|
45
49
|
concurrencyLimit;
|
|
46
50
|
pipeline;
|
|
@@ -150,6 +154,26 @@ class Orchestrator {
|
|
|
150
154
|
}
|
|
151
155
|
});
|
|
152
156
|
}
|
|
157
|
+
// Initialize Source Analyzer
|
|
158
|
+
if (options.sourceCodeAnalyzer) {
|
|
159
|
+
this.sourceAnalyzer = options.sourceCodeAnalyzer;
|
|
160
|
+
// Assumed initialized or self-initializing
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.sourceAnalyzer = new SourceCodeAnalyzer();
|
|
164
|
+
// Fire and forget initialization (or await if strict)
|
|
165
|
+
// We'll trust it catches up or is fast enough for the first few items,
|
|
166
|
+
// or we can await it if we want to block startup.
|
|
167
|
+
// For CLI responsiveness, we might want to await it only if deep context is enabled.
|
|
168
|
+
const deepContextEnabled = options.deepContext !== false; // Enabled by default
|
|
169
|
+
if (deepContextEnabled) {
|
|
170
|
+
this.sourceAnalyzer.initialize().catch((err) => {
|
|
171
|
+
if (this.advanced.debug) {
|
|
172
|
+
console.warn("Source analyzer init failed:", err.message);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
153
177
|
// Initialize Pipeline
|
|
154
178
|
this.pipeline = new Pipeline();
|
|
155
179
|
this._buildPipeline();
|
|
@@ -182,6 +206,9 @@ class Orchestrator {
|
|
|
182
206
|
// 3. Glossary Pre-processing
|
|
183
207
|
this.pipeline.use(new GlossaryPreStep(this.glossaryManager));
|
|
184
208
|
// 4. Translation
|
|
209
|
+
// 3.5 Context Enrichment (Deep Context)
|
|
210
|
+
const deepContextEnabled = this.options.deepContext !== false;
|
|
211
|
+
this.pipeline.use(new ContextEnrichmentStep(this.sourceAnalyzer, deepContextEnabled));
|
|
185
212
|
this.pipeline.use(new TranslationStep(this.options, this.confidenceSettings));
|
|
186
213
|
// 4.5 Vector Cache Write (save new translations)
|
|
187
214
|
if (this.options.vectorMemory?.enabled) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import PipelineStep from "../PipelineStep.js";
|
|
2
|
+
import { TranslationContext } from "../context.js";
|
|
3
|
+
import { SourceCodeAnalyzer } from "../../../services/source-analyzer.js";
|
|
4
|
+
export default class ContextEnrichmentStep extends PipelineStep {
|
|
5
|
+
private analyzer;
|
|
6
|
+
private enabled;
|
|
7
|
+
constructor(analyzer: SourceCodeAnalyzer, enabled?: boolean);
|
|
8
|
+
execute(context: TranslationContext, next: () => Promise<void>): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import PipelineStep from "../PipelineStep.js";
|
|
2
|
+
export default class ContextEnrichmentStep extends PipelineStep {
|
|
3
|
+
analyzer;
|
|
4
|
+
enabled;
|
|
5
|
+
constructor(analyzer, enabled = true) {
|
|
6
|
+
super();
|
|
7
|
+
this.analyzer = analyzer;
|
|
8
|
+
this.enabled = enabled;
|
|
9
|
+
}
|
|
10
|
+
async execute(context, next) {
|
|
11
|
+
if (this.enabled) {
|
|
12
|
+
try {
|
|
13
|
+
// Ensure analysis is complete before checking for context
|
|
14
|
+
await this.analyzer.ensureInitialized();
|
|
15
|
+
const codeContext = this.analyzer.getContext(context.key);
|
|
16
|
+
if (codeContext) {
|
|
17
|
+
// Enrich the context meta
|
|
18
|
+
context.meta = {
|
|
19
|
+
...context.meta,
|
|
20
|
+
code: codeContext,
|
|
21
|
+
};
|
|
22
|
+
// Also append to the AI prompt context if it exists
|
|
23
|
+
// (Assuming existing translation prompt uses contextData)
|
|
24
|
+
context.contextData = {
|
|
25
|
+
...(context.contextData || {}),
|
|
26
|
+
visualContext: {
|
|
27
|
+
component: codeContext.component,
|
|
28
|
+
file: codeContext.filePath,
|
|
29
|
+
comments: codeContext.comments,
|
|
30
|
+
props: codeContext.props,
|
|
31
|
+
snippet: codeContext.usageSnippet,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// Non-blocking error
|
|
38
|
+
// console.warn(`Context enrichment failed for ${context.key}`, error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await next();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CodeContext } from "../types/context.js";
|
|
2
|
+
export declare class SourceCodeAnalyzer {
|
|
3
|
+
private project;
|
|
4
|
+
private keyUsageMap;
|
|
5
|
+
private rootDir;
|
|
6
|
+
private initPromise;
|
|
7
|
+
constructor(rootDir?: string);
|
|
8
|
+
initialize(globPatterns?: string[]): Promise<void>;
|
|
9
|
+
ensureInitialized(): Promise<void>;
|
|
10
|
+
private processFile;
|
|
11
|
+
private processCallExpression;
|
|
12
|
+
private extractContext;
|
|
13
|
+
getContext(key: string): CodeContext | undefined;
|
|
14
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Project, Node } from "ts-morph";
|
|
2
|
+
import { log } from "../utils/logger.js";
|
|
3
|
+
import path from "path";
|
|
4
|
+
export class SourceCodeAnalyzer {
|
|
5
|
+
project;
|
|
6
|
+
keyUsageMap = new Map();
|
|
7
|
+
rootDir;
|
|
8
|
+
initPromise = null;
|
|
9
|
+
constructor(rootDir = process.cwd()) {
|
|
10
|
+
this.rootDir = rootDir;
|
|
11
|
+
// Initialize with default settings, but don't add files yet for performance
|
|
12
|
+
this.project = new Project({
|
|
13
|
+
skipAddingFilesFromTsConfig: true,
|
|
14
|
+
compilerOptions: {
|
|
15
|
+
allowJs: true,
|
|
16
|
+
jsx: 1, // Preserve
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
initialize(globPatterns = [
|
|
21
|
+
"src/**/*.{ts,tsx}",
|
|
22
|
+
"app/**/*.{ts,tsx}",
|
|
23
|
+
"components/**/*.{ts,tsx}",
|
|
24
|
+
]) {
|
|
25
|
+
if (this.initPromise)
|
|
26
|
+
return this.initPromise;
|
|
27
|
+
this.initPromise = (async () => {
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 0)); // Force async execution for race condition safety
|
|
29
|
+
try {
|
|
30
|
+
// Add source files
|
|
31
|
+
log(`Analyzing source code in ${this.rootDir}...`);
|
|
32
|
+
// Exclude node_modules explicitly just in case
|
|
33
|
+
this.project.addSourceFilesAtPaths(globPatterns);
|
|
34
|
+
const sourceFiles = this.project.getSourceFiles();
|
|
35
|
+
log(`Found ${sourceFiles.length} source files for context analysis.`);
|
|
36
|
+
for (const sourceFile of sourceFiles) {
|
|
37
|
+
this.processFile(sourceFile);
|
|
38
|
+
}
|
|
39
|
+
log(`Source analysis complete. Context extracted for ${this.keyUsageMap.size} keys.`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.warn(`Error during source analysis: ${error.message}`);
|
|
43
|
+
// Don't throw to prevent blocking the whole app, just log
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
return this.initPromise;
|
|
47
|
+
}
|
|
48
|
+
async ensureInitialized() {
|
|
49
|
+
if (this.initPromise) {
|
|
50
|
+
await this.initPromise;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
processFile(sourceFile) {
|
|
54
|
+
sourceFile.forEachDescendant((node) => {
|
|
55
|
+
if (Node.isCallExpression(node)) {
|
|
56
|
+
this.processCallExpression(node, sourceFile.getFilePath());
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
processCallExpression(node, filePath) {
|
|
61
|
+
// Match t('key') or useTranslations().t('key') pattern
|
|
62
|
+
// This is a heuristic match
|
|
63
|
+
const expression = node.getExpression();
|
|
64
|
+
// Check if expression is accessible
|
|
65
|
+
if (!expression)
|
|
66
|
+
return;
|
|
67
|
+
const expressionText = expression.getText();
|
|
68
|
+
// Simple check for 't' function calls
|
|
69
|
+
if (expressionText.endsWith(".t") || expressionText === "t") {
|
|
70
|
+
const args = node.getArguments();
|
|
71
|
+
if (args.length > 0 && Node.isStringLiteral(args[0])) {
|
|
72
|
+
const key = args[0].getLiteralText();
|
|
73
|
+
this.extractContext(key, node, filePath);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
extractContext(key, node, fullFilePath) {
|
|
78
|
+
const relativePath = path.relative(this.rootDir, fullFilePath);
|
|
79
|
+
const line = node.getStartLineNumber();
|
|
80
|
+
// Extract component name
|
|
81
|
+
let componentName = undefined;
|
|
82
|
+
// Use getAncestors() to find the parent safely
|
|
83
|
+
const componentNode = node
|
|
84
|
+
.getAncestors()
|
|
85
|
+
.find((n) => Node.isFunctionDeclaration(n) ||
|
|
86
|
+
Node.isClassDeclaration(n) ||
|
|
87
|
+
Node.isVariableDeclaration(n));
|
|
88
|
+
if (componentNode) {
|
|
89
|
+
if (Node.isFunctionDeclaration(componentNode) ||
|
|
90
|
+
Node.isClassDeclaration(componentNode)) {
|
|
91
|
+
componentName = componentNode.getName();
|
|
92
|
+
}
|
|
93
|
+
else if (Node.isVariableDeclaration(componentNode)) {
|
|
94
|
+
componentName = componentNode.getName();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Extract comments
|
|
98
|
+
// Look for leading comments on the statement
|
|
99
|
+
const statement = node
|
|
100
|
+
.getAncestors()
|
|
101
|
+
.find((n) => Node.isExpressionStatement(n) ||
|
|
102
|
+
Node.isVariableStatement(n) ||
|
|
103
|
+
Node.isJsxElement(n) ||
|
|
104
|
+
Node.isJsxExpression(n));
|
|
105
|
+
const comments = [];
|
|
106
|
+
const targetNode = statement || node;
|
|
107
|
+
const ranges = targetNode.getLeadingCommentRanges();
|
|
108
|
+
for (const range of ranges) {
|
|
109
|
+
comments.push(range
|
|
110
|
+
.getText()
|
|
111
|
+
.replace(/^\/\/\s*/, "")
|
|
112
|
+
.replace(/^\/\*\s*/, "")
|
|
113
|
+
.replace(/\s*\*\/$/, ""));
|
|
114
|
+
}
|
|
115
|
+
// Get props (simple heuristic for JSX)
|
|
116
|
+
const props = {};
|
|
117
|
+
const jsxElement = node
|
|
118
|
+
.getAncestors()
|
|
119
|
+
.find((n) => Node.isJsxOpeningElement(n) || Node.isJsxSelfClosingElement(n));
|
|
120
|
+
if (jsxElement &&
|
|
121
|
+
(Node.isJsxOpeningElement(jsxElement) || Node.isJsxSelfClosingElement(jsxElement))) {
|
|
122
|
+
jsxElement.getAttributes().forEach((attr) => {
|
|
123
|
+
if (Node.isJsxAttribute(attr)) {
|
|
124
|
+
// Safe name extraction
|
|
125
|
+
const nameNode = attr.getNameNode();
|
|
126
|
+
const name = nameNode ? nameNode.getText() : "unknown";
|
|
127
|
+
const initializer = attr.getInitializer();
|
|
128
|
+
let value = initializer ? initializer.getText() : "true";
|
|
129
|
+
// Strip quotes if string literal
|
|
130
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
131
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
132
|
+
value = value.substring(1, value.length - 1);
|
|
133
|
+
}
|
|
134
|
+
props[name] = value;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Code Snippet (surrounding lines)
|
|
139
|
+
// Check for node.getParent() to avoid error if node is root (unlikely for call expression)
|
|
140
|
+
const parent = node.getParent();
|
|
141
|
+
const snippet = parent ? parent.getText() : node.getText();
|
|
142
|
+
const context = {
|
|
143
|
+
filePath: relativePath,
|
|
144
|
+
line,
|
|
145
|
+
component: componentName,
|
|
146
|
+
usageSnippet: snippet.length > 200 ? snippet.substring(0, 200) + "..." : snippet,
|
|
147
|
+
comments: comments.length > 0 ? comments : undefined,
|
|
148
|
+
props: Object.keys(props).length > 0 ? props : undefined,
|
|
149
|
+
};
|
|
150
|
+
if (!this.keyUsageMap.has(key)) {
|
|
151
|
+
this.keyUsageMap.set(key, []);
|
|
152
|
+
}
|
|
153
|
+
this.keyUsageMap.get(key)?.push(context);
|
|
154
|
+
}
|
|
155
|
+
getContext(key) {
|
|
156
|
+
// Return duplicate functionality: prefer the first usage found or merge?
|
|
157
|
+
// For now return the first usage that has comments, or just the first usage.
|
|
158
|
+
const usages = this.keyUsageMap.get(key);
|
|
159
|
+
if (!usages || usages.length === 0)
|
|
160
|
+
return undefined;
|
|
161
|
+
// Prioritize usage with comments
|
|
162
|
+
return usages.find((u) => u.comments && u.comments.length > 0) || usages[0];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -9,6 +9,7 @@ import gracefulShutdown from "../utils/graceful-shutdown.js";
|
|
|
9
9
|
import uiManager from "../utils/ui-manager.js";
|
|
10
10
|
import statisticsManager from "../utils/statistics-manager.js";
|
|
11
11
|
import { LanguageProcessor } from "./language-processor.js";
|
|
12
|
+
import { SourceCodeAnalyzer } from "./source-analyzer.js";
|
|
12
13
|
/**
|
|
13
14
|
* Service for handling file translation operations.
|
|
14
15
|
* Orchestrates the translation process including state management,
|
|
@@ -151,6 +152,10 @@ class TranslationService {
|
|
|
151
152
|
const languageConcurrency = options.concurrencyLimit || 3;
|
|
152
153
|
const targetLanguages = [...(options.targets || [])];
|
|
153
154
|
await uiManager.log(`Processing ${targetLanguages.length} languages with concurrency of ${languageConcurrency}`);
|
|
155
|
+
// Initialize shared SourceCodeAnalyzer
|
|
156
|
+
console.log("Initializing Locai Engine...");
|
|
157
|
+
const sharedAnalyzer = new SourceCodeAnalyzer();
|
|
158
|
+
sharedAnalyzer.initialize();
|
|
154
159
|
// Create shared orchestrators array to collect review queues
|
|
155
160
|
const orchestrators = [];
|
|
156
161
|
for (let i = 0; i < targetLanguages.length; i += languageConcurrency) {
|
|
@@ -167,6 +172,7 @@ class TranslationService {
|
|
|
167
172
|
...options,
|
|
168
173
|
concurrencyLimit: 1,
|
|
169
174
|
progressOptions,
|
|
175
|
+
sourceCodeAnalyzer: sharedAnalyzer,
|
|
170
176
|
});
|
|
171
177
|
orchestrators.push(orchestrator);
|
|
172
178
|
return this.processLanguage(targetLang, resolvedFile, flattenedSource, orchestrator, { ...options, progressOptions }, globalStats, comparison);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface CodeContext {
|
|
2
|
+
filePath: string;
|
|
3
|
+
line: number;
|
|
4
|
+
component?: string;
|
|
5
|
+
usageSnippet?: string;
|
|
6
|
+
comments?: string[];
|
|
7
|
+
props?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export interface SourceAnalyzerConfig {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
include?: string[];
|
|
12
|
+
exclude?: string[];
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "locai",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/core/orchestrator.js",
|
|
6
6
|
"types": "dist/core/orchestrator.d.ts",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"start": "node dist/core/orchestrator.js",
|
|
17
17
|
"dev": "tsx src/core/orchestrator.js",
|
|
18
18
|
"build": "tsc",
|
|
19
|
-
"
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
20
|
"lint": "eslint .",
|
|
21
21
|
"lint:fix": "eslint . --fix",
|
|
22
22
|
"format": "prettier --write \"**/*.{js,json,md,jsx,ts,tsx,css}\"",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"./translator": "./dist/commands/translator.js"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"axios": "1.13.
|
|
76
|
-
"c12": "^3.3.
|
|
75
|
+
"axios": "1.13.2",
|
|
76
|
+
"c12": "^3.3.3",
|
|
77
77
|
"commander": "^14.0.2",
|
|
78
78
|
"dot-properties": "^1.1.0",
|
|
79
79
|
"dotenv": "^17.2.3",
|
|
@@ -81,25 +81,26 @@
|
|
|
81
81
|
"gettext-parser": "^8.0.0",
|
|
82
82
|
"jiti": "^2.6.1",
|
|
83
83
|
"js-yaml": "^4.1.1",
|
|
84
|
-
"lru-cache": "^11.2.
|
|
84
|
+
"lru-cache": "^11.2.4",
|
|
85
85
|
"p-limit": "^7.2.0",
|
|
86
86
|
"prompts": "^2.4.2",
|
|
87
|
+
"ts-morph": "^27.0.2",
|
|
87
88
|
"vectra": "^0.11.1"
|
|
88
89
|
},
|
|
89
90
|
"devDependencies": {
|
|
90
|
-
"@eslint/js": "^9.39.
|
|
91
|
+
"@eslint/js": "^9.39.2",
|
|
91
92
|
"@types/js-yaml": "^4.0.9",
|
|
92
|
-
"@types/node": "^25.0.
|
|
93
|
+
"@types/node": "^25.0.3",
|
|
93
94
|
"@types/prompts": "^2.4.9",
|
|
94
|
-
"@vitest/coverage-v8": "^4.0.
|
|
95
|
-
"eslint": "^9.39.
|
|
95
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
96
|
+
"eslint": "^9.39.2",
|
|
96
97
|
"eslint-config-prettier": "^10.1.8",
|
|
97
98
|
"globals": "^16.5.0",
|
|
98
|
-
"prettier": "^3.
|
|
99
|
+
"prettier": "^3.7.4",
|
|
99
100
|
"std-mocks": "^2.0.0",
|
|
100
101
|
"tsx": "^4.21.0",
|
|
101
102
|
"typescript": "^5.9.3",
|
|
102
|
-
"vitest": "^4.0.
|
|
103
|
+
"vitest": "^4.0.16"
|
|
103
104
|
},
|
|
104
105
|
"files": [
|
|
105
106
|
"dist/"
|