@theihtisham/agent-shadow-brain 2.1.0 → 3.0.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/dist/brain/accessibility-checker.d.ts +10 -0
- package/dist/brain/accessibility-checker.d.ts.map +1 -0
- package/dist/brain/accessibility-checker.js +379 -0
- package/dist/brain/accessibility-checker.js.map +1 -0
- package/dist/brain/api-contract-analyzer.d.ts +19 -0
- package/dist/brain/api-contract-analyzer.d.ts.map +1 -0
- package/dist/brain/api-contract-analyzer.js +251 -0
- package/dist/brain/api-contract-analyzer.js.map +1 -0
- package/dist/brain/ast-analyzer.d.ts +23 -0
- package/dist/brain/ast-analyzer.d.ts.map +1 -0
- package/dist/brain/ast-analyzer.js +462 -0
- package/dist/brain/ast-analyzer.js.map +1 -0
- package/dist/brain/code-age-analyzer.d.ts +11 -0
- package/dist/brain/code-age-analyzer.d.ts.map +1 -0
- package/dist/brain/code-age-analyzer.js +152 -0
- package/dist/brain/code-age-analyzer.js.map +1 -0
- package/dist/brain/config-drift-detector.d.ts +13 -0
- package/dist/brain/config-drift-detector.d.ts.map +1 -0
- package/dist/brain/config-drift-detector.js +198 -0
- package/dist/brain/config-drift-detector.js.map +1 -0
- package/dist/brain/dead-code-eliminator.d.ts +16 -0
- package/dist/brain/dead-code-eliminator.d.ts.map +1 -0
- package/dist/brain/dead-code-eliminator.js +359 -0
- package/dist/brain/dead-code-eliminator.js.map +1 -0
- package/dist/brain/env-analyzer.d.ts +13 -0
- package/dist/brain/env-analyzer.d.ts.map +1 -0
- package/dist/brain/env-analyzer.js +277 -0
- package/dist/brain/env-analyzer.js.map +1 -0
- package/dist/brain/i18n-detector.d.ts +12 -0
- package/dist/brain/i18n-detector.d.ts.map +1 -0
- package/dist/brain/i18n-detector.js +242 -0
- package/dist/brain/i18n-detector.js.map +1 -0
- package/dist/brain/license-compliance.d.ts +13 -0
- package/dist/brain/license-compliance.d.ts.map +1 -0
- package/dist/brain/license-compliance.js +213 -0
- package/dist/brain/license-compliance.js.map +1 -0
- package/dist/brain/llm-client.d.ts.map +1 -1
- package/dist/brain/llm-client.js +3 -0
- package/dist/brain/llm-client.js.map +1 -1
- package/dist/brain/mutation-advisor.d.ts +11 -0
- package/dist/brain/mutation-advisor.d.ts.map +1 -0
- package/dist/brain/mutation-advisor.js +154 -0
- package/dist/brain/mutation-advisor.js.map +1 -0
- package/dist/brain/orchestrator.d.ts +48 -1
- package/dist/brain/orchestrator.d.ts.map +1 -1
- package/dist/brain/orchestrator.js +178 -2
- package/dist/brain/orchestrator.js.map +1 -1
- package/dist/cli.js +408 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +151 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BrainInsight } from '../types.js';
|
|
2
|
+
export declare class EnvAnalyzer {
|
|
3
|
+
private projectDir;
|
|
4
|
+
constructor(projectDir: string);
|
|
5
|
+
analyzeProject(maxFiles?: number): Promise<BrainInsight[]>;
|
|
6
|
+
private analyzeEnvFiles;
|
|
7
|
+
private analyzeCodeFile;
|
|
8
|
+
private crossReferenceEnv;
|
|
9
|
+
private looksLikeSecret;
|
|
10
|
+
private issueToInsight;
|
|
11
|
+
private collectFiles;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=env-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-analyzer.d.ts","sourceRoot":"","sources":["../../src/brain/env-analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAY,MAAM,aAAa,CAAC;AAQrD,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,cAAc,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwBrE,OAAO,CAAC,eAAe;IAiFvB,OAAO,CAAC,eAAe;IAiEvB,OAAO,CAAC,iBAAiB;IAkDzB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,YAAY;CAqBrB"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
// src/brain/env-analyzer.ts — Environment variable analysis
|
|
2
|
+
// v3.0.0 — Detects hardcoded secrets, missing validation, inconsistent naming, unused vars
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
const IGNORE_DIRS = new Set([
|
|
6
|
+
'node_modules', '.git', 'dist', 'build', 'out', 'coverage', '.next', '.nuxt', '.cache',
|
|
7
|
+
]);
|
|
8
|
+
export class EnvAnalyzer {
|
|
9
|
+
constructor(projectDir) {
|
|
10
|
+
this.projectDir = projectDir;
|
|
11
|
+
}
|
|
12
|
+
async analyzeProject(maxFiles = 200) {
|
|
13
|
+
const issues = [];
|
|
14
|
+
// Phase 1: Check .env files
|
|
15
|
+
issues.push(...this.analyzeEnvFiles());
|
|
16
|
+
// Phase 2: Check source code for env usage
|
|
17
|
+
const codeFiles = this.collectFiles(this.projectDir, maxFiles);
|
|
18
|
+
const envVars = new Set();
|
|
19
|
+
const codeIssues = [];
|
|
20
|
+
for (const filePath of codeFiles) {
|
|
21
|
+
const { vars, issues: fileIssues } = this.analyzeCodeFile(filePath);
|
|
22
|
+
for (const v of vars)
|
|
23
|
+
envVars.add(v);
|
|
24
|
+
codeIssues.push(...fileIssues);
|
|
25
|
+
}
|
|
26
|
+
issues.push(...codeIssues);
|
|
27
|
+
// Phase 3: Cross-reference .env.example with .env usage
|
|
28
|
+
issues.push(...this.crossReferenceEnv(envVars));
|
|
29
|
+
return issues.map(issue => this.issueToInsight(issue));
|
|
30
|
+
}
|
|
31
|
+
analyzeEnvFiles() {
|
|
32
|
+
const issues = [];
|
|
33
|
+
const envFiles = ['.env', '.env.local', '.env.development', '.env.production', '.env.test', '.env.example'];
|
|
34
|
+
const envVars = new Map();
|
|
35
|
+
for (const envFile of envFiles) {
|
|
36
|
+
const fullPath = path.join(this.projectDir, envFile);
|
|
37
|
+
let content;
|
|
38
|
+
try {
|
|
39
|
+
content = fs.readFileSync(fullPath, 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const lines = content.split('\n');
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
const line = lines[i].trim();
|
|
47
|
+
if (!line || line.startsWith('#'))
|
|
48
|
+
continue;
|
|
49
|
+
const match = line.match(/^(\w+)=(.*)$/);
|
|
50
|
+
if (!match)
|
|
51
|
+
continue;
|
|
52
|
+
const name = match[1];
|
|
53
|
+
const value = match[2].replace(/^["']|["']$/g, '');
|
|
54
|
+
envVars.set(name, { file: envFile, line: i + 1, value });
|
|
55
|
+
// Check for hardcoded secrets
|
|
56
|
+
if (this.looksLikeSecret(name, value) && envFile !== '.env.example') {
|
|
57
|
+
issues.push({
|
|
58
|
+
variable: name,
|
|
59
|
+
file: envFile,
|
|
60
|
+
line: i + 1,
|
|
61
|
+
type: 'hardcoded-secret',
|
|
62
|
+
severity: 'critical',
|
|
63
|
+
suggestion: `Move ${name} to a secret manager (Vault, AWS Secrets Manager) or use CI/CD secrets. Never commit real secrets.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Check for missing docs
|
|
67
|
+
const prevLine = i > 0 ? lines[i - 1].trim() : '';
|
|
68
|
+
if (!prevLine.startsWith('#') && envFile === '.env.example') {
|
|
69
|
+
issues.push({
|
|
70
|
+
variable: name,
|
|
71
|
+
file: envFile,
|
|
72
|
+
line: i + 1,
|
|
73
|
+
type: 'missing-docs',
|
|
74
|
+
severity: 'low',
|
|
75
|
+
suggestion: `Add a comment above ${name} explaining its purpose, format, and default value.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check for inconsistent naming
|
|
81
|
+
const varNames = [...envVars.keys()];
|
|
82
|
+
const styles = new Map();
|
|
83
|
+
for (const name of varNames) {
|
|
84
|
+
let style;
|
|
85
|
+
if (/^[A-Z][A-Z0-9_]*$/.test(name))
|
|
86
|
+
style = 'UPPER_SNAKE';
|
|
87
|
+
else if (/^[a-z][a-z0-9_]*$/.test(name))
|
|
88
|
+
style = 'lower_snake';
|
|
89
|
+
else if (/^[a-z][a-zA-Z0-9]*$/.test(name))
|
|
90
|
+
style = 'camelCase';
|
|
91
|
+
else
|
|
92
|
+
style = 'mixed';
|
|
93
|
+
if (!styles.has(style))
|
|
94
|
+
styles.set(style, []);
|
|
95
|
+
styles.get(style).push(name);
|
|
96
|
+
}
|
|
97
|
+
if (styles.size > 1) {
|
|
98
|
+
const hasUpper = styles.has('UPPER_SNAKE');
|
|
99
|
+
const nonUpper = [...styles.entries()].filter(([s]) => s !== 'UPPER_SNAKE');
|
|
100
|
+
if (hasUpper && nonUpper.length > 0) {
|
|
101
|
+
issues.push({
|
|
102
|
+
variable: nonUpper[0][1][0],
|
|
103
|
+
file: '.env',
|
|
104
|
+
line: 0,
|
|
105
|
+
type: 'inconsistent-naming',
|
|
106
|
+
severity: 'medium',
|
|
107
|
+
suggestion: `Use UPPER_SNAKE_CASE consistently for env vars. Inconsistent: ${nonUpper.flatMap(([, v]) => v).slice(0, 5).join(', ')}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return issues;
|
|
112
|
+
}
|
|
113
|
+
analyzeCodeFile(filePath) {
|
|
114
|
+
let content;
|
|
115
|
+
try {
|
|
116
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { vars: [], issues: [] };
|
|
120
|
+
}
|
|
121
|
+
const vars = [];
|
|
122
|
+
const issues = [];
|
|
123
|
+
const relPath = path.relative(this.projectDir, filePath);
|
|
124
|
+
const lines = content.split('\n');
|
|
125
|
+
for (let i = 0; i < lines.length; i++) {
|
|
126
|
+
const line = lines[i];
|
|
127
|
+
// Detect process.env.X usage
|
|
128
|
+
const envMatch = line.match(/process\.env\.(\w+)/g);
|
|
129
|
+
if (envMatch) {
|
|
130
|
+
for (const m of envMatch) {
|
|
131
|
+
const varName = m.replace('process.env.', '');
|
|
132
|
+
vars.push(varName);
|
|
133
|
+
// Check for missing default/fallback
|
|
134
|
+
const hasFallback = line.includes('??') || line.includes('||') || line.includes('=') && !line.includes('process.env.');
|
|
135
|
+
if (!hasFallback) {
|
|
136
|
+
// Check if there's a validation line nearby
|
|
137
|
+
const nearbyLines = lines.slice(Math.max(0, i - 3), Math.min(lines.length, i + 3)).join('\n');
|
|
138
|
+
const hasValidation = /z\.object|joi|validate|required|assert/.test(nearbyLines);
|
|
139
|
+
if (!hasValidation && !line.includes('typeof')) {
|
|
140
|
+
issues.push({
|
|
141
|
+
variable: varName,
|
|
142
|
+
file: relPath,
|
|
143
|
+
line: i + 1,
|
|
144
|
+
type: 'missing-default',
|
|
145
|
+
severity: 'high',
|
|
146
|
+
suggestion: `Provide a default value or runtime validation for ${varName}: process.env.${varName} ?? 'default'`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Detect hardcoded secrets in code (not process.env)
|
|
153
|
+
const secretPatterns = [
|
|
154
|
+
{ regex: /(?:api[_-]?key|apikey|secret|token|password|passwd|pwd)\s*[:=]\s*["'][^"']{8,}["']/gi, type: 'hardcoded-secret' },
|
|
155
|
+
{ regex: /(?:mongodb|postgres|mysql|redis):\/\/[^\s"']+/gi, type: 'hardcoded-secret' },
|
|
156
|
+
{ regex: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, type: 'hardcoded-secret' },
|
|
157
|
+
];
|
|
158
|
+
for (const { regex, type } of secretPatterns) {
|
|
159
|
+
regex.lastIndex = 0;
|
|
160
|
+
if (regex.test(line) && !line.includes('process.env') && !line.includes('getenv') && !line.trim().startsWith('//')) {
|
|
161
|
+
issues.push({
|
|
162
|
+
variable: 'HARDCODED_SECRET',
|
|
163
|
+
file: relPath,
|
|
164
|
+
line: i + 1,
|
|
165
|
+
type,
|
|
166
|
+
severity: 'critical',
|
|
167
|
+
suggestion: 'Never hardcode secrets in source code. Use environment variables or a secret manager.',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { vars, issues };
|
|
173
|
+
}
|
|
174
|
+
crossReferenceEnv(usedVars) {
|
|
175
|
+
const issues = [];
|
|
176
|
+
// Check .env.example
|
|
177
|
+
const examplePath = path.join(this.projectDir, '.env.example');
|
|
178
|
+
let exampleVars;
|
|
179
|
+
try {
|
|
180
|
+
const content = fs.readFileSync(examplePath, 'utf-8');
|
|
181
|
+
exampleVars = new Set(content.split('\n')
|
|
182
|
+
.map(l => l.trim())
|
|
183
|
+
.filter(l => l && !l.startsWith('#'))
|
|
184
|
+
.map(l => l.split('=')[0])
|
|
185
|
+
.filter(Boolean));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
exampleVars = new Set();
|
|
189
|
+
}
|
|
190
|
+
// Vars used in code but not in .env.example
|
|
191
|
+
for (const v of usedVars) {
|
|
192
|
+
if (!exampleVars.has(v)) {
|
|
193
|
+
issues.push({
|
|
194
|
+
variable: v,
|
|
195
|
+
file: '.env.example',
|
|
196
|
+
line: 0,
|
|
197
|
+
type: 'missing-default',
|
|
198
|
+
severity: 'medium',
|
|
199
|
+
suggestion: `Add ${v} to .env.example so developers know it's required.`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Vars in .env.example but not used in code
|
|
204
|
+
for (const v of exampleVars) {
|
|
205
|
+
if (!usedVars.has(v)) {
|
|
206
|
+
issues.push({
|
|
207
|
+
variable: v,
|
|
208
|
+
file: '.env.example',
|
|
209
|
+
line: 0,
|
|
210
|
+
type: 'unused',
|
|
211
|
+
severity: 'low',
|
|
212
|
+
suggestion: `${v} is in .env.example but not referenced in code. Consider removing it.`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return issues;
|
|
217
|
+
}
|
|
218
|
+
looksLikeSecret(name, value) {
|
|
219
|
+
const secretNames = /secret|password|passwd|pwd|token|api[_-]?key|private[_-]?key|auth|credential/i;
|
|
220
|
+
if (!secretNames.test(name))
|
|
221
|
+
return false;
|
|
222
|
+
// Non-empty, non-placeholder values
|
|
223
|
+
if (!value || value === '' || value === 'changeme' || value === 'xxx' || value === 'your-key-here')
|
|
224
|
+
return false;
|
|
225
|
+
if (value.length < 8)
|
|
226
|
+
return false;
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
issueToInsight(issue) {
|
|
230
|
+
return {
|
|
231
|
+
type: 'env',
|
|
232
|
+
priority: issue.severity === 'critical' ? 'critical' : issue.severity === 'high' ? 'high' : issue.severity === 'medium' ? 'medium' : 'low',
|
|
233
|
+
title: `[env] ${issue.type}: ${issue.variable} in ${issue.file}${issue.line ? ':' + issue.line : ''}`,
|
|
234
|
+
content: `Environment variable issue in ${issue.file}${issue.line ? ':' + issue.line : ''}\n` +
|
|
235
|
+
` Variable: ${issue.variable}\n` +
|
|
236
|
+
` Type: ${issue.type}\n` +
|
|
237
|
+
` Severity: ${issue.severity}\n` +
|
|
238
|
+
` Fix: ${issue.suggestion}`,
|
|
239
|
+
files: [issue.file],
|
|
240
|
+
timestamp: new Date(),
|
|
241
|
+
confidence: issue.type === 'hardcoded-secret' ? 0.95 : 0.8,
|
|
242
|
+
metadata: { variable: issue.variable, envIssueType: issue.type },
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
collectFiles(dir, maxFiles) {
|
|
246
|
+
const results = [];
|
|
247
|
+
const walk = (currentDir, depth) => {
|
|
248
|
+
if (results.length >= maxFiles || depth > 10)
|
|
249
|
+
return;
|
|
250
|
+
let entries;
|
|
251
|
+
try {
|
|
252
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
if (results.length >= maxFiles)
|
|
259
|
+
return;
|
|
260
|
+
if (entry.name.startsWith('.'))
|
|
261
|
+
continue;
|
|
262
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
263
|
+
if (entry.isDirectory()) {
|
|
264
|
+
if (IGNORE_DIRS.has(entry.name))
|
|
265
|
+
continue;
|
|
266
|
+
walk(fullPath, depth + 1);
|
|
267
|
+
}
|
|
268
|
+
else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
|
|
269
|
+
results.push(fullPath);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
walk(dir, 0);
|
|
274
|
+
return results;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=env-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-analyzer.js","sourceRoot":"","sources":["../../src/brain/env-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BrainInsight, I18nIssue } from '../types.js';
|
|
2
|
+
export declare class I18nDetector {
|
|
3
|
+
private projectDir;
|
|
4
|
+
constructor(projectDir: string);
|
|
5
|
+
analyzeProject(maxFiles?: number): Promise<BrainInsight[]>;
|
|
6
|
+
private detectI18nLibrary;
|
|
7
|
+
analyzeFile(filePath: string, hasI18n: boolean): I18nIssue[];
|
|
8
|
+
private isCodeNotText;
|
|
9
|
+
private issueToInsight;
|
|
10
|
+
private collectFiles;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=i18n-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-detector.d.ts","sourceRoot":"","sources":["../../src/brain/i18n-detector.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAoDtD,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,cAAc,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAerE,OAAO,CAAC,iBAAiB;IAgBzB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE;IAoH5D,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,YAAY;CAqBrB"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// src/brain/i18n-detector.ts — Internationalization (i18n) readiness checker
|
|
2
|
+
// v3.0.0 — Detects hardcoded strings, date/number format issues, missing RTL support
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
const IGNORE_DIRS = new Set([
|
|
6
|
+
'node_modules', '.git', 'dist', 'build', 'out', 'coverage', '.next', '.nuxt', '.cache',
|
|
7
|
+
'__tests__', '__test__', 'test', 'tests', 'spec',
|
|
8
|
+
]);
|
|
9
|
+
const CODE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.vue', '.svelte']);
|
|
10
|
+
// Common English words that suggest hardcoded UI text
|
|
11
|
+
const UI_PATTERNS = [
|
|
12
|
+
// String literals in JSX/HTML attributes that look like user-facing text
|
|
13
|
+
/placeholder\s*=\s*["']([A-Z][^"']{3,})["']/g,
|
|
14
|
+
/title\s*=\s*["']([A-Z][^"']{3,})["']/g,
|
|
15
|
+
/label\s*=\s*["']([A-Z][^"']{3,})["']/g,
|
|
16
|
+
/aria-label\s*=\s*["']([A-Z][^"']{3,})["']/g,
|
|
17
|
+
/alt\s*=\s*["']([A-Z][^"']{3,})["']/g,
|
|
18
|
+
// Button text in JSX
|
|
19
|
+
/>\s*([A-Z][a-z]+(\s+[A-Z]?[a-z]+){1,5})\s*</g,
|
|
20
|
+
// Error messages as string literals
|
|
21
|
+
/throw\s+new\s+\w+Error\s*\(\s*["']([A-Z][^"']{10,})["']/g,
|
|
22
|
+
// Alert/confirm messages
|
|
23
|
+
/\balert\s*\(\s*["']([A-Z][^"']{5,})["']/g,
|
|
24
|
+
/\bconfirm\s*\(\s*["']([A-Z][^"']{5,})["']/g,
|
|
25
|
+
// console.log with descriptive text (might be user-facing in some contexts)
|
|
26
|
+
/toast\s*\(\s*["']([A-Z][^"']{5,})["']/g,
|
|
27
|
+
/notification\s*\.\s*(success|error|warning|info)\s*\(\s*["']([A-Z][^"']{5,})["']/g,
|
|
28
|
+
];
|
|
29
|
+
// Date format patterns that aren't i18n-aware
|
|
30
|
+
const DATE_PATTERNS = [
|
|
31
|
+
{ regex: /\bnew\s+Date\(\s*\)\.to(Locale)?DateString\s*\(\s*\)/g, type: 'date-format' },
|
|
32
|
+
{ regex: /\btoLocaleDateString\s*\(\s*["']en["']/g, type: 'date-format' },
|
|
33
|
+
{ regex: /\bgetMonth\(\)\s*\+\s*1/g, type: 'date-format' },
|
|
34
|
+
{ regex: /\bgetFullYear\(\)\s*-\s*\d{2}/g, type: 'date-format' },
|
|
35
|
+
];
|
|
36
|
+
// Number format patterns
|
|
37
|
+
const NUMBER_PATTERNS = [
|
|
38
|
+
{ regex: /\btoFixed\s*\(\s*\d+\s*\)/g, type: 'number-format' },
|
|
39
|
+
{ regex: /\b\.toLocaleString\s*\(\s*\)/g, type: 'number-format' },
|
|
40
|
+
];
|
|
41
|
+
// String concatenation with variables (likely needs interpolation)
|
|
42
|
+
const CONCAT_PATTERNS = [
|
|
43
|
+
/["'][A-Za-z\s]+\s*["']\s*\+\s*\w+/g,
|
|
44
|
+
/\w+\s*\+\s*["']\s+[A-Za-z\s]+["']/g,
|
|
45
|
+
/`[^`]*\$\{[^}]+\}[^`]*[A-Za-z]{3,}[^`]*`/g, // template literals with text
|
|
46
|
+
];
|
|
47
|
+
export class I18nDetector {
|
|
48
|
+
constructor(projectDir) {
|
|
49
|
+
this.projectDir = projectDir;
|
|
50
|
+
}
|
|
51
|
+
async analyzeProject(maxFiles = 150) {
|
|
52
|
+
const files = this.collectFiles(this.projectDir, maxFiles);
|
|
53
|
+
const allIssues = [];
|
|
54
|
+
// Check if i18n library is already present
|
|
55
|
+
const hasI18n = this.detectI18nLibrary();
|
|
56
|
+
for (const filePath of files) {
|
|
57
|
+
const issues = this.analyzeFile(filePath, hasI18n);
|
|
58
|
+
allIssues.push(...issues);
|
|
59
|
+
}
|
|
60
|
+
return allIssues.map(issue => this.issueToInsight(issue, hasI18n));
|
|
61
|
+
}
|
|
62
|
+
detectI18nLibrary() {
|
|
63
|
+
const pkgPath = path.join(this.projectDir, 'package.json');
|
|
64
|
+
try {
|
|
65
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
66
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
67
|
+
return !!(allDeps['i18next'] || allDeps['react-i18next'] || allDeps['vue-i18n'] ||
|
|
68
|
+
allDeps['@angular/localize'] || allDeps['svelte-i18n'] || allDeps['next-intl'] ||
|
|
69
|
+
allDeps['next-i18next'] || allDeps['react-intl'] || allDeps['formatjs'] ||
|
|
70
|
+
allDeps['lingui'] || allDeps['@lingui/core']);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
analyzeFile(filePath, hasI18n) {
|
|
77
|
+
let content;
|
|
78
|
+
try {
|
|
79
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const issues = [];
|
|
85
|
+
const lines = content.split('\n');
|
|
86
|
+
const relPath = path.relative(this.projectDir, filePath);
|
|
87
|
+
// Skip test files
|
|
88
|
+
if (relPath.includes('.test.') || relPath.includes('.spec.') || relPath.includes('__tests__')) {
|
|
89
|
+
return issues;
|
|
90
|
+
}
|
|
91
|
+
// Detect hardcoded UI strings
|
|
92
|
+
for (let i = 0; i < lines.length; i++) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
const lineNum = i + 1;
|
|
95
|
+
// Check for UI text patterns
|
|
96
|
+
for (const pattern of UI_PATTERNS) {
|
|
97
|
+
pattern.lastIndex = 0;
|
|
98
|
+
const match = pattern.exec(line);
|
|
99
|
+
if (match) {
|
|
100
|
+
const text = match[1] || match[0];
|
|
101
|
+
// Skip if it looks like code, not text
|
|
102
|
+
if (this.isCodeNotText(text))
|
|
103
|
+
continue;
|
|
104
|
+
issues.push({
|
|
105
|
+
type: 'hardcoded-string',
|
|
106
|
+
file: relPath,
|
|
107
|
+
line: lineNum,
|
|
108
|
+
content: text.slice(0, 100),
|
|
109
|
+
suggestion: hasI18n
|
|
110
|
+
? `Replace with i18n translation key: t('key')`
|
|
111
|
+
: `Extract to a translations file or use an i18n library`,
|
|
112
|
+
severity: 'medium',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check for string concatenation
|
|
117
|
+
for (const concatPattern of CONCAT_PATTERNS) {
|
|
118
|
+
concatPattern.lastIndex = 0;
|
|
119
|
+
if (concatPattern.test(line)) {
|
|
120
|
+
// Avoid duplicates
|
|
121
|
+
const existing = issues.find(iss => iss.line === lineNum && iss.type === 'concatenation');
|
|
122
|
+
if (!existing) {
|
|
123
|
+
issues.push({
|
|
124
|
+
type: 'concatenation',
|
|
125
|
+
file: relPath,
|
|
126
|
+
line: lineNum,
|
|
127
|
+
content: line.trim().slice(0, 100),
|
|
128
|
+
suggestion: 'Use ICU message format or interpolation for proper i18n: `{variable}` instead of string concatenation',
|
|
129
|
+
severity: 'high',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Check date format issues
|
|
136
|
+
for (const { regex, type } of DATE_PATTERNS) {
|
|
137
|
+
regex.lastIndex = 0;
|
|
138
|
+
let match;
|
|
139
|
+
while ((match = regex.exec(content)) !== null) {
|
|
140
|
+
const line = content.substring(0, match.index).split('\n').length;
|
|
141
|
+
issues.push({
|
|
142
|
+
type,
|
|
143
|
+
file: relPath,
|
|
144
|
+
line,
|
|
145
|
+
content: match[0],
|
|
146
|
+
suggestion: 'Use Intl.DateTimeFormat with explicit locale or i18n library date formatting',
|
|
147
|
+
severity: 'low',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Check number format issues
|
|
152
|
+
for (const { regex, type } of NUMBER_PATTERNS) {
|
|
153
|
+
regex.lastIndex = 0;
|
|
154
|
+
let match;
|
|
155
|
+
while ((match = regex.exec(content)) !== null) {
|
|
156
|
+
const line = content.substring(0, match.index).split('\n').length;
|
|
157
|
+
issues.push({
|
|
158
|
+
type,
|
|
159
|
+
file: relPath,
|
|
160
|
+
line,
|
|
161
|
+
content: match[0],
|
|
162
|
+
suggestion: 'Use Intl.NumberFormat with locale for proper number formatting (decimals, separators, currency)',
|
|
163
|
+
severity: 'low',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Check for pluralization issues (hardcoded "s" for plural)
|
|
168
|
+
const pluralRegex = /\+\s*["']s["']\s*|\?\s*["']s["']\s*:\s*["']["']/g;
|
|
169
|
+
let pluralMatch;
|
|
170
|
+
while ((pluralMatch = pluralRegex.exec(content)) !== null) {
|
|
171
|
+
const line = content.substring(0, pluralMatch.index).split('\n').length;
|
|
172
|
+
issues.push({
|
|
173
|
+
type: 'pluralization',
|
|
174
|
+
file: relPath,
|
|
175
|
+
line,
|
|
176
|
+
content: pluralMatch[0],
|
|
177
|
+
suggestion: 'Use ICU plural format: `{count, plural, one {item} other {items}}` for proper pluralization across languages',
|
|
178
|
+
severity: 'medium',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return issues;
|
|
182
|
+
}
|
|
183
|
+
isCodeNotText(text) {
|
|
184
|
+
// Skip if it looks like code identifiers
|
|
185
|
+
if (/^[a-z_]+(\.[a-z_]+)*$/.test(text))
|
|
186
|
+
return true;
|
|
187
|
+
if (/^\d+$/.test(text))
|
|
188
|
+
return true;
|
|
189
|
+
if (/^(true|false|null|undefined|void|async|await|return|const|let|var)$/.test(text))
|
|
190
|
+
return true;
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
issueToInsight(issue, hasI18n) {
|
|
194
|
+
return {
|
|
195
|
+
type: 'i18n',
|
|
196
|
+
priority: issue.severity === 'high' ? 'high' : issue.severity === 'medium' ? 'medium' : 'low',
|
|
197
|
+
title: `[i18n] ${issue.type}: ${issue.content.slice(0, 60)}`,
|
|
198
|
+
content: `i18n issue in ${issue.file}:${issue.line}\n` +
|
|
199
|
+
` Type: ${issue.type}\n` +
|
|
200
|
+
` Content: ${issue.content}\n` +
|
|
201
|
+
` Severity: ${issue.severity}\n` +
|
|
202
|
+
` Fix: ${issue.suggestion}\n` +
|
|
203
|
+
(hasI18n ? '' : ' Note: No i18n library detected. Consider adding i18next, react-intl, or next-intl.'),
|
|
204
|
+
files: [issue.file],
|
|
205
|
+
timestamp: new Date(),
|
|
206
|
+
confidence: 0.85,
|
|
207
|
+
metadata: { i18nType: issue.type, hasI18nLibrary: hasI18n },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
collectFiles(dir, maxFiles) {
|
|
211
|
+
const results = [];
|
|
212
|
+
const walk = (currentDir, depth) => {
|
|
213
|
+
if (results.length >= maxFiles || depth > 10)
|
|
214
|
+
return;
|
|
215
|
+
let entries;
|
|
216
|
+
try {
|
|
217
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
for (const entry of entries) {
|
|
223
|
+
if (results.length >= maxFiles)
|
|
224
|
+
return;
|
|
225
|
+
if (entry.name.startsWith('.'))
|
|
226
|
+
continue;
|
|
227
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
228
|
+
if (entry.isDirectory()) {
|
|
229
|
+
if (IGNORE_DIRS.has(entry.name))
|
|
230
|
+
continue;
|
|
231
|
+
walk(fullPath, depth + 1);
|
|
232
|
+
}
|
|
233
|
+
else if (entry.isFile() && CODE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
234
|
+
results.push(fullPath);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
walk(dir, 0);
|
|
239
|
+
return results;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=i18n-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-detector.js","sourceRoot":"","sources":["../../src/brain/i18n-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BrainInsight } from '../types.js';
|
|
2
|
+
export declare class LicenseCompliance {
|
|
3
|
+
private projectDir;
|
|
4
|
+
constructor(projectDir: string);
|
|
5
|
+
analyzeProject(): Promise<BrainInsight[]>;
|
|
6
|
+
private readPackageJson;
|
|
7
|
+
private getPackageLicense;
|
|
8
|
+
private detectConflicts;
|
|
9
|
+
private getProjectLicense;
|
|
10
|
+
private getRestrictedRecommendation;
|
|
11
|
+
private issueToInsight;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=license-compliance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license-compliance.d.ts","sourceRoot":"","sources":["../../src/brain/license-compliance.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAgB,MAAM,aAAa,CAAC;AA+BzD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAIxB,cAAc,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAuE/C,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,cAAc;CAkBvB"}
|