0xray 4.0.35 → 4.0.37
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/.grok-plugin/plugin.json +1 -1
- package/.xray/features.json +1 -1
- package/dist/CHANGELOG.md +25 -0
- package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
- package/dist/analytics/emerging-pattern-detector.js +37 -12
- package/dist/analytics/pattern-learning-engine.js +1 -9
- package/dist/analytics/predictive-analytics.js +51 -33
- package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
- package/dist/analytics/prompt-pattern-analyzer.js +39 -20
- package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
- package/dist/analytics/routing-performance-analyzer.js +89 -37
- package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
- package/dist/analytics/simple-pattern-analyzer.js +66 -28
- package/dist/architect/architect-tools.d.ts +3 -0
- package/dist/architect/architect-tools.js +22 -0
- package/dist/cli/commands/grok-install.js +1 -1
- package/dist/cli/commands/hermes-install.js +11 -5
- package/dist/cli/commands/openclaw-install.js +13 -7
- package/dist/cli/commands/opencode-install.js +7 -4
- package/dist/cli/commands/suit-monitor.d.ts +30 -0
- package/dist/cli/commands/suit-monitor.js +128 -0
- package/dist/cli/index.js +52 -15
- package/dist/cli/suit-pulse.d.ts +29 -0
- package/dist/cli/suit-pulse.js +270 -0
- package/dist/delegation/agent-delegator.d.ts +4 -2
- package/dist/delegation/agent-delegator.js +46 -27
- package/dist/delegation/agent-expertise.js +2 -1
- package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
- package/dist/delegation/analytics/outcome-tracker.js +17 -3
- package/dist/delegation/ast-code-parser.d.ts +5 -0
- package/dist/delegation/ast-code-parser.js +53 -48
- package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
- package/dist/delegation/codebase-context-analyzer.js +188 -159
- package/dist/delegation/session-coordinator.js +3 -2
- package/dist/enforcement/loaders/codex-loader.js +18 -14
- package/dist/enforcement/validators/architecture-validators.js +115 -9
- package/dist/governance/codex-policy.service.d.ts +12 -2
- package/dist/governance/codex-policy.service.js +91 -16
- package/dist/integrations/grok/hooks/session-start.js +6 -6
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
- package/dist/integrations/hooks/goggles-pipeline.mjs +221 -44
- package/dist/integrations/hooks/goggles-planes.json +172 -8
- package/dist/integrations/hooks/plates.cjs +138 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
- package/dist/integrations/hooks/write-home.cjs +59 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/architect-tools.server.d.ts +6 -0
- package/dist/mcps/architect-tools.server.js +19 -18
- package/dist/mcps/orchestrator/server.d.ts +4 -3
- package/dist/mcps/orchestrator/server.js +29 -17
- package/dist/memory-routing/lookup-cache.d.ts +8 -0
- package/dist/memory-routing/lookup-cache.js +72 -0
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +26 -0
- package/dist/memory-routing/provider-loader.js +3 -0
- package/dist/metrics/agent-metrics.d.ts +14 -5
- package/dist/metrics/agent-metrics.js +362 -224
- package/dist/nucleus/synthesis-consult-receipt.js +38 -16
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
- package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
- package/dist/orchestrator/orchestrator.js +1 -2
- package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
- package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
- package/dist/processors/implementations/console-log-guard-processor.js +39 -50
- package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
- package/dist/processors/implementations/performance-budget-processor.js +29 -38
- package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
- package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
- package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
- package/dist/reporting/framework-reporting-system.d.ts +3 -0
- package/dist/reporting/framework-reporting-system.js +61 -8
- package/dist/reporting/log-parser.js +188 -105
- package/dist/reporting/metrics.js +42 -18
- package/dist/security/prompt-security-validator.js +8 -7
- package/dist/security/security-auditor.d.ts +4 -0
- package/dist/security/security-auditor.js +132 -57
- package/dist/security/security-hardener.d.ts +2 -0
- package/dist/security/security-hardener.js +31 -6
- package/dist/security/security-headers.d.ts +2 -0
- package/dist/security/security-headers.js +23 -21
- package/dist/security/security-scanner.js +18 -4
- package/dist/session/session-cleanup-manager.d.ts +2 -0
- package/dist/session/session-cleanup-manager.js +29 -8
- package/dist/session/session-monitor.d.ts +14 -4
- package/dist/session/session-monitor.js +208 -106
- package/dist/session/session-state-manager.d.ts +11 -0
- package/dist/session/session-state-manager.js +90 -8
- package/docs-site/docs/plates/activity-log.md +38 -0
- package/docs-site/docs/plates/cursor-compact.md +35 -0
- package/docs-site/docs/plates/grok-compact.md +35 -0
- package/docs-site/docs/plates/index.md +25 -3
- package/docs-site/docs/plates/inference-files.md +35 -0
- package/docs-site/docs/plates/kept-line.md +35 -0
- package/docs-site/docs/plates/lens-gate.md +35 -0
- package/docs-site/docs/plates/lens-page.md +35 -0
- package/docs-site/docs/plates/notes-page.md +35 -0
- package/docs-site/docs/plates/package-face.md +35 -0
- package/docs-site/docs/plates/payload-heat.md +35 -0
- package/docs-site/docs/plates/pickup-stamp.md +35 -0
- package/docs-site/docs/plates/pre-tool.md +35 -0
- package/docs-site/docs/plates/record-map.md +41 -0
- package/docs-site/docs/plates/reflection-page.md +35 -0
- package/docs-site/docs/plates/review.md +57 -0
- package/docs-site/docs/plates/session-capture.md +38 -0
- package/docs-site/docs/plates/site-manual.md +35 -0
- package/docs-site/docs/plates/stamp-plate.md +35 -0
- package/docs-site/docs/plates/station-card.md +35 -0
- package/docs-site/docs/plates/station-heat.md +35 -0
- package/docs-site/docs/plates/suit-organs.md +38 -0
- package/docs-site/docs/plates/suit-settings.md +35 -0
- package/docs-site/docs/plates/suit-wear.md +37 -0
- package/docs-site/docs/plates/trail-state.md +35 -0
- package/docs-site/docs/plates/work-fresh.md +35 -0
- package/docs-site/docs/plates/write-home.md +38 -0
- package/package.json +4 -2
- package/scripts/mjs/run-grok-hook.mjs +50 -0
- package/scripts/node/install-bridges.cjs +305 -81
- package/scripts/node/postinstall.cjs +14 -3
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/architect-tools.server.ts +25 -20
- package/src/mcps/orchestrator/server.ts +33 -19
- package/xray/features.json +1 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
|
@@ -1,6 +1,60 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
1
2
|
import * as fs from "fs";
|
|
2
3
|
import * as path from "path";
|
|
3
4
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
5
|
+
function bytesHash(bytes) {
|
|
6
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
7
|
+
}
|
|
8
|
+
const JS_IMPORT_PATTERNS = [
|
|
9
|
+
/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g,
|
|
10
|
+
/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
11
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
12
|
+
];
|
|
13
|
+
const JS_EXPORT_PATTERNS = [
|
|
14
|
+
/export\s+(?:const|let|var|function|class|interface|type)\s+(\w+)/g,
|
|
15
|
+
/export\s+{\s*([^}]+)\s*}/g,
|
|
16
|
+
/export\s+default/g,
|
|
17
|
+
];
|
|
18
|
+
const PY_IMPORT_PATTERNS = [/from\s+([^\s]+)\s+import/g, /import\s+([^\s]+)/g];
|
|
19
|
+
const JAVA_IMPORT_PATTERN = /^import\s+([^;]+);/gm;
|
|
20
|
+
const JAVA_EXPORT_PATTERN = /(?:public\s+)?(?:class|interface|enum)\s+(\w+)/g;
|
|
21
|
+
const CONFIG_FILE_PATTERNS = [
|
|
22
|
+
/package\.json$/,
|
|
23
|
+
/tsconfig\.json$/,
|
|
24
|
+
/webpack\.config\./,
|
|
25
|
+
/\.eslintrc/,
|
|
26
|
+
/\.prettierrc/,
|
|
27
|
+
/Dockerfile/,
|
|
28
|
+
/docker-compose\.yml/,
|
|
29
|
+
/\.env/,
|
|
30
|
+
];
|
|
31
|
+
const IMPORT_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".py", ".java"];
|
|
32
|
+
const FRAMEWORK_NEEDLES = [
|
|
33
|
+
["react", "React"],
|
|
34
|
+
["vue", "Vue.js"],
|
|
35
|
+
["angular", "Angular"],
|
|
36
|
+
["svelte", "Svelte"],
|
|
37
|
+
["express", "Express.js"],
|
|
38
|
+
["nestjs", "NestJS"],
|
|
39
|
+
["nextjs", "Next.js"],
|
|
40
|
+
["nuxt", "Nuxt.js"],
|
|
41
|
+
];
|
|
42
|
+
function countLines(content) {
|
|
43
|
+
let lines = 1;
|
|
44
|
+
for (let i = 0; i < content.length; i++) {
|
|
45
|
+
if (content.charCodeAt(i) === 10)
|
|
46
|
+
lines++;
|
|
47
|
+
}
|
|
48
|
+
return lines;
|
|
49
|
+
}
|
|
50
|
+
function resetAndCollect(pattern, content, into) {
|
|
51
|
+
pattern.lastIndex = 0;
|
|
52
|
+
let match;
|
|
53
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
54
|
+
if (match[1])
|
|
55
|
+
into.push(match[1]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
4
58
|
export class CodebaseContextAnalyzer {
|
|
5
59
|
projectRoot;
|
|
6
60
|
memoryConfig;
|
|
@@ -135,9 +189,18 @@ export class CodebaseContextAnalyzer {
|
|
|
135
189
|
if (filePath) {
|
|
136
190
|
try {
|
|
137
191
|
const currentStats = fs.statSync(filePath);
|
|
138
|
-
const cachedMtime = cached.lastModified?.getTime()
|
|
139
|
-
|
|
140
|
-
|
|
192
|
+
const cachedMtime = cached.lastModified?.getTime();
|
|
193
|
+
const sameMeta = cachedMtime !== undefined &&
|
|
194
|
+
currentStats.mtime.getTime() === cachedMtime &&
|
|
195
|
+
cached.size !== undefined &&
|
|
196
|
+
currentStats.size === cached.size;
|
|
197
|
+
if (!sameMeta) {
|
|
198
|
+
this.analysisCache.delete(cacheKey);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
// mtime and size collided. Identity includes the bytes.
|
|
202
|
+
const hash = bytesHash(fs.readFileSync(filePath, "utf8"));
|
|
203
|
+
if (cached.contentHash !== hash) {
|
|
141
204
|
this.analysisCache.delete(cacheKey);
|
|
142
205
|
return null;
|
|
143
206
|
}
|
|
@@ -172,29 +235,37 @@ export class CodebaseContextAnalyzer {
|
|
|
172
235
|
* Extract file path from cache key for validation
|
|
173
236
|
*/
|
|
174
237
|
extractFilePathFromCacheKey(cacheKey) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
238
|
+
if (!cacheKey.startsWith("file:"))
|
|
239
|
+
return null;
|
|
240
|
+
const rest = cacheKey.slice("file:".length);
|
|
241
|
+
const colon = rest.lastIndexOf(":");
|
|
242
|
+
if (colon <= 0)
|
|
243
|
+
return null;
|
|
244
|
+
const mtime = rest.slice(colon + 1);
|
|
245
|
+
if (!/^\d+$/.test(mtime))
|
|
246
|
+
return null;
|
|
247
|
+
return rest.slice(0, colon);
|
|
181
248
|
}
|
|
182
249
|
/**
|
|
183
250
|
* Stream file content for large files to reduce memory usage
|
|
184
251
|
*/
|
|
185
252
|
async streamFileContent(filePath) {
|
|
186
253
|
return new Promise((resolve, reject) => {
|
|
187
|
-
|
|
254
|
+
const chunks = [];
|
|
255
|
+
let size = 0;
|
|
188
256
|
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
|
|
189
257
|
stream.on("data", (chunk) => {
|
|
190
|
-
|
|
258
|
+
const text = typeof chunk === "string" ? chunk : String(chunk);
|
|
259
|
+
size += text.length;
|
|
191
260
|
// Prevent excessive memory usage during streaming
|
|
192
|
-
if (
|
|
261
|
+
if (size > this.memoryConfig.maxFileSizeBytes) {
|
|
193
262
|
stream.destroy();
|
|
194
263
|
reject(new Error("File too large for streaming"));
|
|
264
|
+
return;
|
|
195
265
|
}
|
|
266
|
+
chunks.push(text);
|
|
196
267
|
});
|
|
197
|
-
stream.on("end", () => resolve(
|
|
268
|
+
stream.on("end", () => resolve(chunks.join("")));
|
|
198
269
|
stream.on("error", reject);
|
|
199
270
|
});
|
|
200
271
|
}
|
|
@@ -206,9 +277,15 @@ export class CodebaseContextAnalyzer {
|
|
|
206
277
|
const modules = new Map();
|
|
207
278
|
const dependencyGraph = new Map();
|
|
208
279
|
const languages = new Map();
|
|
209
|
-
const scanDirectory = async (dirPath, relativePath = "") => {
|
|
280
|
+
const scanDirectory = async (dirPath, relativePath = "", checkModule = false) => {
|
|
210
281
|
try {
|
|
211
282
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
283
|
+
// Names decide module vs tree. Do not walk this snapshot.
|
|
284
|
+
if (checkModule && this.isModuleNames(entries.map((entry) => entry.name))) {
|
|
285
|
+
const moduleInfo = await this.analyzeModule(dirPath, relativePath, jobId);
|
|
286
|
+
modules.set(relativePath, moduleInfo);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
212
289
|
// Process files in batches to control memory usage
|
|
213
290
|
const fileEntries = [];
|
|
214
291
|
const dirEntries = [];
|
|
@@ -221,11 +298,9 @@ export class CodebaseContextAnalyzer {
|
|
|
221
298
|
continue;
|
|
222
299
|
}
|
|
223
300
|
if (entry.isDirectory()) {
|
|
224
|
-
const isModule = this.isModuleDirectory(entryPath);
|
|
225
301
|
dirEntries.push({
|
|
226
302
|
path: entryPath,
|
|
227
303
|
relativePath: entryRelativePath,
|
|
228
|
-
isModule,
|
|
229
304
|
});
|
|
230
305
|
}
|
|
231
306
|
else if (entry.isFile()) {
|
|
@@ -237,13 +312,7 @@ export class CodebaseContextAnalyzer {
|
|
|
237
312
|
}
|
|
238
313
|
// Process directories first (may contain modules)
|
|
239
314
|
for (const dirEntry of dirEntries) {
|
|
240
|
-
|
|
241
|
-
const moduleInfo = await this.analyzeModule(dirEntry.path, dirEntry.relativePath, jobId);
|
|
242
|
-
modules.set(dirEntry.relativePath, moduleInfo);
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
await scanDirectory(dirEntry.path, dirEntry.relativePath);
|
|
246
|
-
}
|
|
315
|
+
await scanDirectory(dirEntry.path, dirEntry.relativePath, true);
|
|
247
316
|
}
|
|
248
317
|
// Process files with concurrent processing for better performance
|
|
249
318
|
const concurrencyLimit = this.memoryConfig.enableConcurrentProcessing
|
|
@@ -341,8 +410,8 @@ export class CodebaseContextAnalyzer {
|
|
|
341
410
|
let imports = [];
|
|
342
411
|
let exports = [];
|
|
343
412
|
try {
|
|
344
|
-
//
|
|
345
|
-
const cacheKey = `file:${
|
|
413
|
+
// Same path and mtime still miss when size or bytes change.
|
|
414
|
+
const cacheKey = `file:${filePath}:${stats.mtime.getTime()}`;
|
|
346
415
|
const cached = this.getCachedAnalysis(cacheKey);
|
|
347
416
|
if (cached) {
|
|
348
417
|
return {
|
|
@@ -365,7 +434,7 @@ export class CodebaseContextAnalyzer {
|
|
|
365
434
|
this.memoryConfig.enableStreaming && stats.size > 100 * 1024
|
|
366
435
|
? await this.streamFileContent(filePath)
|
|
367
436
|
: fs.readFileSync(filePath, "utf8");
|
|
368
|
-
linesOfCode = content
|
|
437
|
+
linesOfCode = countLines(content);
|
|
369
438
|
const importExportData = await this.extractImportsExports(content, language, jobId);
|
|
370
439
|
imports = importExportData.imports;
|
|
371
440
|
exports = importExportData.exports;
|
|
@@ -376,6 +445,9 @@ export class CodebaseContextAnalyzer {
|
|
|
376
445
|
imports,
|
|
377
446
|
exports,
|
|
378
447
|
content,
|
|
448
|
+
lastModified: stats.mtime,
|
|
449
|
+
size: stats.size,
|
|
450
|
+
contentHash: bytesHash(content),
|
|
379
451
|
timestamp: Date.now(),
|
|
380
452
|
});
|
|
381
453
|
}
|
|
@@ -427,6 +499,7 @@ export class CodebaseContextAnalyzer {
|
|
|
427
499
|
const dependents = new Set();
|
|
428
500
|
const scanModuleFiles = async (modulePath, moduleRelativePath) => {
|
|
429
501
|
try {
|
|
502
|
+
// List at the walk so a file created after classification is included.
|
|
430
503
|
const entries = fs.readdirSync(modulePath, { withFileTypes: true });
|
|
431
504
|
for (const entry of entries) {
|
|
432
505
|
const entryPath = path.join(modulePath, entry.name);
|
|
@@ -499,27 +572,13 @@ export class CodebaseContextAnalyzer {
|
|
|
499
572
|
extractJsTsImportsExports(content) {
|
|
500
573
|
const imports = [];
|
|
501
574
|
const exports = [];
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
506
|
-
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
507
|
-
];
|
|
508
|
-
importPatterns.forEach((pattern) => {
|
|
509
|
-
let match;
|
|
510
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
511
|
-
if (match[1]) {
|
|
512
|
-
imports.push(match[1]);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
});
|
|
575
|
+
for (const pattern of JS_IMPORT_PATTERNS) {
|
|
576
|
+
resetAndCollect(pattern, content, imports);
|
|
577
|
+
}
|
|
516
578
|
// Export patterns
|
|
517
|
-
const exportPatterns =
|
|
518
|
-
/export\s+(?:const|let|var|function|class|interface|type)\s+(\w+)/g,
|
|
519
|
-
/export\s+{\s*([^}]+)\s*}/g,
|
|
520
|
-
/export\s+default/g,
|
|
521
|
-
];
|
|
579
|
+
const exportPatterns = JS_EXPORT_PATTERNS;
|
|
522
580
|
exportPatterns.forEach((pattern) => {
|
|
581
|
+
pattern.lastIndex = 0;
|
|
523
582
|
let match;
|
|
524
583
|
while ((match = pattern.exec(content)) !== null) {
|
|
525
584
|
if (match[1]) {
|
|
@@ -546,16 +605,9 @@ export class CodebaseContextAnalyzer {
|
|
|
546
605
|
extractPythonImportsExports(content) {
|
|
547
606
|
const imports = [];
|
|
548
607
|
const exports = [];
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
let match;
|
|
553
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
554
|
-
if (match[1]) {
|
|
555
|
-
imports.push(match[1]);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
});
|
|
608
|
+
for (const pattern of PY_IMPORT_PATTERNS) {
|
|
609
|
+
resetAndCollect(pattern, content, imports);
|
|
610
|
+
}
|
|
559
611
|
// Export patterns (Python doesn't have explicit exports, but __all__ can indicate public API)
|
|
560
612
|
const allMatch = content.match(/__all__\s*=\s*\[([^\]]+)\]/);
|
|
561
613
|
if (allMatch && allMatch[1]) {
|
|
@@ -573,21 +625,8 @@ export class CodebaseContextAnalyzer {
|
|
|
573
625
|
extractJavaImportsExports(content) {
|
|
574
626
|
const imports = [];
|
|
575
627
|
const exports = [];
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
let match;
|
|
579
|
-
while ((match = importPattern.exec(content)) !== null) {
|
|
580
|
-
if (match[1]) {
|
|
581
|
-
imports.push(match[1]);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
// Export patterns (public classes/interfaces)
|
|
585
|
-
const exportPattern = /(?:public\s+)?(?:class|interface|enum)\s+(\w+)/g;
|
|
586
|
-
while ((match = exportPattern.exec(content)) !== null) {
|
|
587
|
-
if (match[1]) {
|
|
588
|
-
exports.push(match[1]);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
628
|
+
resetAndCollect(JAVA_IMPORT_PATTERN, content, imports);
|
|
629
|
+
resetAndCollect(JAVA_EXPORT_PATTERN, content, exports);
|
|
591
630
|
return {
|
|
592
631
|
imports: Array.from(new Set(imports)),
|
|
593
632
|
exports: Array.from(new Set(exports)),
|
|
@@ -597,7 +636,16 @@ export class CodebaseContextAnalyzer {
|
|
|
597
636
|
* Build dependency graph between files
|
|
598
637
|
*/
|
|
599
638
|
async buildDependencyGraph(fileGraph, dependencyGraph) {
|
|
600
|
-
|
|
639
|
+
const existsCache = new Map();
|
|
640
|
+
const fileExists = (absPath) => {
|
|
641
|
+
const cached = existsCache.get(absPath);
|
|
642
|
+
if (cached !== undefined)
|
|
643
|
+
return cached;
|
|
644
|
+
const found = fs.existsSync(absPath);
|
|
645
|
+
existsCache.set(absPath, found);
|
|
646
|
+
return found;
|
|
647
|
+
};
|
|
648
|
+
for (const [filePath, fileInfo] of fileGraph) {
|
|
601
649
|
const dependencies = new Set();
|
|
602
650
|
for (const importPath of fileInfo.imports) {
|
|
603
651
|
// Resolve relative imports
|
|
@@ -605,21 +653,11 @@ export class CodebaseContextAnalyzer {
|
|
|
605
653
|
try {
|
|
606
654
|
const resolvedPath = path.resolve(path.dirname(fileInfo.path), importPath);
|
|
607
655
|
const resolvedRelative = path.relative(this.projectRoot, resolvedPath);
|
|
608
|
-
// Check for file extensions
|
|
609
|
-
const possibleExtensions = [
|
|
610
|
-
"",
|
|
611
|
-
".ts",
|
|
612
|
-
".tsx",
|
|
613
|
-
".js",
|
|
614
|
-
".jsx",
|
|
615
|
-
".py",
|
|
616
|
-
".java",
|
|
617
|
-
];
|
|
618
656
|
let foundFile = null;
|
|
619
|
-
for (const ext of
|
|
657
|
+
for (const ext of IMPORT_EXTENSIONS) {
|
|
620
658
|
const testPath = resolvedRelative + ext;
|
|
621
659
|
if (fileGraph.has(testPath) ||
|
|
622
|
-
|
|
660
|
+
fileExists(path.join(this.projectRoot, testPath))) {
|
|
623
661
|
foundFile = testPath;
|
|
624
662
|
break;
|
|
625
663
|
}
|
|
@@ -648,34 +686,61 @@ export class CodebaseContextAnalyzer {
|
|
|
648
686
|
const patterns = [];
|
|
649
687
|
let structure = "monolithic";
|
|
650
688
|
const entryPoints = [];
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
689
|
+
const foundFrameworks = new Set();
|
|
690
|
+
let hasControllers = false;
|
|
691
|
+
let hasModels = false;
|
|
692
|
+
let hasViews = false;
|
|
693
|
+
let hasRepository = false;
|
|
694
|
+
let hasFactory = false;
|
|
695
|
+
let hasObserver = false;
|
|
696
|
+
let hasMicroservices = false;
|
|
697
|
+
// One pass over paths and file text. Same booleans as the per-needle scans.
|
|
698
|
+
for (const [filePath, fileInfo] of fileGraph) {
|
|
699
|
+
if (!hasControllers && filePath.includes("controller"))
|
|
700
|
+
hasControllers = true;
|
|
701
|
+
if (!hasModels && filePath.includes("model"))
|
|
702
|
+
hasModels = true;
|
|
703
|
+
if (!hasViews && filePath.includes("view"))
|
|
704
|
+
hasViews = true;
|
|
705
|
+
if (!hasRepository && filePath.includes("repository"))
|
|
706
|
+
hasRepository = true;
|
|
707
|
+
if (!hasFactory && filePath.includes("factory"))
|
|
708
|
+
hasFactory = true;
|
|
709
|
+
if (!hasMicroservices &&
|
|
710
|
+
(filePath.includes("docker") ||
|
|
711
|
+
filePath.includes("kubernetes") ||
|
|
712
|
+
filePath.includes("service"))) {
|
|
713
|
+
hasMicroservices = true;
|
|
714
|
+
}
|
|
715
|
+
const content = fileInfo.content;
|
|
716
|
+
if (!content)
|
|
717
|
+
continue;
|
|
718
|
+
if (!hasObserver && (content.includes("subscribe") || content.includes("observe"))) {
|
|
719
|
+
hasObserver = true;
|
|
720
|
+
}
|
|
721
|
+
if (foundFrameworks.size === FRAMEWORK_NEEDLES.length)
|
|
722
|
+
continue;
|
|
723
|
+
const lower = content.toLowerCase();
|
|
724
|
+
for (const [needle, label] of FRAMEWORK_NEEDLES) {
|
|
725
|
+
if (!foundFrameworks.has(label) && lower.includes(needle)) {
|
|
726
|
+
foundFrameworks.add(label);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
for (const [, label] of FRAMEWORK_NEEDLES) {
|
|
731
|
+
if (foundFrameworks.has(label))
|
|
732
|
+
frameworks.push(label);
|
|
733
|
+
}
|
|
734
|
+
if (hasControllers && hasModels && hasViews)
|
|
670
735
|
patterns.push("MVC");
|
|
671
|
-
if (
|
|
736
|
+
if (hasRepository)
|
|
672
737
|
patterns.push("Repository");
|
|
673
|
-
if (
|
|
738
|
+
if (hasObserver)
|
|
674
739
|
patterns.push("Observer");
|
|
675
|
-
if (
|
|
740
|
+
if (hasFactory)
|
|
676
741
|
patterns.push("Factory");
|
|
677
742
|
// Structure detection
|
|
678
|
-
if (modules.size > 5 &&
|
|
743
|
+
if (modules.size > 5 && hasMicroservices) {
|
|
679
744
|
structure = "microservices";
|
|
680
745
|
}
|
|
681
746
|
else if (modules.size > 0) {
|
|
@@ -895,33 +960,26 @@ export class CodebaseContextAnalyzer {
|
|
|
895
960
|
return risks;
|
|
896
961
|
}
|
|
897
962
|
// Helper methods
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
963
|
+
isModuleNames(names) {
|
|
964
|
+
let hasSource = false;
|
|
965
|
+
let hasMarker = false;
|
|
966
|
+
for (const entry of names) {
|
|
967
|
+
const ext = path.extname(entry);
|
|
968
|
+
if (this.supportedLanguages[ext]) {
|
|
969
|
+
hasSource = true;
|
|
970
|
+
}
|
|
971
|
+
if (entry === "package.json" ||
|
|
972
|
+
entry === "index.ts" ||
|
|
973
|
+
entry === "index.js") {
|
|
974
|
+
hasMarker = true;
|
|
975
|
+
}
|
|
976
|
+
if (hasSource && hasMarker)
|
|
977
|
+
return true;
|
|
911
978
|
}
|
|
979
|
+
return false;
|
|
912
980
|
}
|
|
913
981
|
isConfigFile(filePath) {
|
|
914
|
-
|
|
915
|
-
/package\.json$/,
|
|
916
|
-
/tsconfig\.json$/,
|
|
917
|
-
/webpack\.config\./,
|
|
918
|
-
/\.eslintrc/,
|
|
919
|
-
/\.prettierrc/,
|
|
920
|
-
/Dockerfile/,
|
|
921
|
-
/docker-compose\.yml/,
|
|
922
|
-
/\.env/,
|
|
923
|
-
];
|
|
924
|
-
return configPatterns.some((pattern) => pattern.test(filePath));
|
|
982
|
+
return CONFIG_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
|
|
925
983
|
}
|
|
926
984
|
classifyModule(relativePath, files) {
|
|
927
985
|
if (relativePath.includes("test") || relativePath.includes("spec")) {
|
|
@@ -957,35 +1015,6 @@ export class CodebaseContextAnalyzer {
|
|
|
957
1015
|
const sourceFile = files.find((f) => f.isSourceCode);
|
|
958
1016
|
return sourceFile?.relativePath;
|
|
959
1017
|
}
|
|
960
|
-
hasFramework(fileGraph, framework) {
|
|
961
|
-
for (const file of Array.from(fileGraph.values())) {
|
|
962
|
-
if (file.content?.toLowerCase().includes(framework.toLowerCase())) {
|
|
963
|
-
return true;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
return false;
|
|
967
|
-
}
|
|
968
|
-
detectMVCPattern(fileGraph) {
|
|
969
|
-
const hasControllers = Array.from(fileGraph.keys()).some((path) => path.includes("controller"));
|
|
970
|
-
const hasModels = Array.from(fileGraph.keys()).some((path) => path.includes("model"));
|
|
971
|
-
const hasViews = Array.from(fileGraph.keys()).some((path) => path.includes("view"));
|
|
972
|
-
return hasControllers && hasModels && hasViews;
|
|
973
|
-
}
|
|
974
|
-
detectRepositoryPattern(fileGraph) {
|
|
975
|
-
return Array.from(fileGraph.keys()).some((path) => path.includes("repository"));
|
|
976
|
-
}
|
|
977
|
-
detectObserverPattern(fileGraph) {
|
|
978
|
-
return Array.from(fileGraph.values()).some((file) => file.content?.includes("subscribe") ||
|
|
979
|
-
file.content?.includes("observe"));
|
|
980
|
-
}
|
|
981
|
-
detectFactoryPattern(fileGraph) {
|
|
982
|
-
return Array.from(fileGraph.keys()).some((path) => path.includes("factory"));
|
|
983
|
-
}
|
|
984
|
-
hasMicroservicesIndicators(fileGraph) {
|
|
985
|
-
return Array.from(fileGraph.keys()).some((path) => path.includes("docker") ||
|
|
986
|
-
path.includes("kubernetes") ||
|
|
987
|
-
path.includes("service"));
|
|
988
|
-
}
|
|
989
1018
|
isEntryPoint(filePath, fileInfo) {
|
|
990
1019
|
const entryIndicators = [
|
|
991
1020
|
"index.ts",
|
|
@@ -296,9 +296,10 @@ export class SessionCoordinator {
|
|
|
296
296
|
resolved = expertItem ? expertItem.value : sharedData[0]?.value;
|
|
297
297
|
break;
|
|
298
298
|
case "consensus":
|
|
299
|
-
// All values must be the same
|
|
299
|
+
// All values must be the same. Stringify the anchor once.
|
|
300
300
|
const firstValue = sharedData[0]?.value;
|
|
301
|
-
const
|
|
301
|
+
const firstKey = JSON.stringify(firstValue);
|
|
302
|
+
const allSame = sharedData.every((item) => JSON.stringify(item.value) === firstKey);
|
|
302
303
|
resolved = allSame ? firstValue : undefined;
|
|
303
304
|
break;
|
|
304
305
|
}
|
|
@@ -323,21 +323,25 @@ export class CodexLoader extends BaseLoader {
|
|
|
323
323
|
}
|
|
324
324
|
// Term 19: Small, Focused Functions - check function size
|
|
325
325
|
if (termNum === 19) {
|
|
326
|
-
// Match actual function declarations and arrow functions (not variable declarations)
|
|
326
|
+
// Match actual function declarations and arrow functions (not variable declarations).
|
|
327
|
+
// exec indexes the first hit of each match text; indexOf(text) is that same span,
|
|
328
|
+
// and a repeated text rechecks it. Do not copy the tail on every hit.
|
|
327
329
|
const functionRegex = /(?:^|\n)(?:function\s+\w+|async\s+function\s+\w+|(?:\([^)]*\)|[^=])=>\s*\{)/g;
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
330
|
+
const seen = new Set();
|
|
331
|
+
let match;
|
|
332
|
+
while ((match = functionRegex.exec(newCode)) !== null) {
|
|
333
|
+
const text = match[0];
|
|
334
|
+
if (seen.has(text))
|
|
335
|
+
continue;
|
|
336
|
+
seen.add(text);
|
|
337
|
+
const bodyStart = match.index + text.length;
|
|
338
|
+
const closingIdx = newCode.indexOf('}', bodyStart);
|
|
339
|
+
if (closingIdx > bodyStart) {
|
|
340
|
+
const functionBody = newCode.substring(bodyStart, closingIdx);
|
|
341
|
+
const lines = functionBody.split('\n').filter(l => l.trim().length > 0);
|
|
342
|
+
if (lines.length > 30) {
|
|
343
|
+
violations.push(`Function with ${lines.length} lines - consider splitting into smaller functions (max 30)`);
|
|
344
|
+
break;
|
|
341
345
|
}
|
|
342
346
|
}
|
|
343
347
|
}
|