@aiready/context-analyzer 0.5.3 → 0.7.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/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-test.log +12 -28
- package/README.md +5 -3
- package/SEMANTIC-VALIDATION.md +235 -0
- package/dist/chunk-AEK3MZC5.mjs +709 -0
- package/dist/chunk-DD7UVNE3.mjs +678 -0
- package/dist/chunk-DMRZMS2U.mjs +964 -0
- package/dist/chunk-HQNHM2X7.mjs +997 -0
- package/dist/chunk-I54HL4FZ.mjs +781 -0
- package/dist/chunk-IRWCPDWD.mjs +779 -0
- package/dist/chunk-PVVCCE6W.mjs +755 -0
- package/dist/chunk-RYIB5CWD.mjs +781 -0
- package/dist/cli.js +304 -33
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +90 -1
- package/dist/index.d.ts +90 -1
- package/dist/index.js +381 -35
- package/dist/index.mjs +17 -3
- package/package.json +2 -2
- package/src/__tests__/auto-detection.test.ts +156 -0
- package/src/__tests__/enhanced-cohesion.test.ts +126 -0
- package/src/analyzer.ts +313 -47
- package/src/index.ts +34 -2
- package/src/semantic-analysis.ts +287 -0
- package/src/types.ts +36 -1
package/dist/cli.js
CHANGED
|
@@ -31,12 +31,176 @@ var import_core2 = require("@aiready/core");
|
|
|
31
31
|
|
|
32
32
|
// src/analyzer.ts
|
|
33
33
|
var import_core = require("@aiready/core");
|
|
34
|
+
|
|
35
|
+
// src/semantic-analysis.ts
|
|
36
|
+
function buildCoUsageMatrix(graph) {
|
|
37
|
+
const coUsageMatrix = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const [sourceFile, node] of graph.nodes) {
|
|
39
|
+
const imports = node.imports;
|
|
40
|
+
for (let i = 0; i < imports.length; i++) {
|
|
41
|
+
const fileA = imports[i];
|
|
42
|
+
if (!coUsageMatrix.has(fileA)) {
|
|
43
|
+
coUsageMatrix.set(fileA, /* @__PURE__ */ new Map());
|
|
44
|
+
}
|
|
45
|
+
for (let j = i + 1; j < imports.length; j++) {
|
|
46
|
+
const fileB = imports[j];
|
|
47
|
+
const fileAUsage = coUsageMatrix.get(fileA);
|
|
48
|
+
fileAUsage.set(fileB, (fileAUsage.get(fileB) || 0) + 1);
|
|
49
|
+
if (!coUsageMatrix.has(fileB)) {
|
|
50
|
+
coUsageMatrix.set(fileB, /* @__PURE__ */ new Map());
|
|
51
|
+
}
|
|
52
|
+
const fileBUsage = coUsageMatrix.get(fileB);
|
|
53
|
+
fileBUsage.set(fileA, (fileBUsage.get(fileA) || 0) + 1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return coUsageMatrix;
|
|
58
|
+
}
|
|
59
|
+
function buildTypeGraph(graph) {
|
|
60
|
+
const typeGraph = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const [file, node] of graph.nodes) {
|
|
62
|
+
for (const exp of node.exports) {
|
|
63
|
+
if (exp.typeReferences) {
|
|
64
|
+
for (const typeRef of exp.typeReferences) {
|
|
65
|
+
if (!typeGraph.has(typeRef)) {
|
|
66
|
+
typeGraph.set(typeRef, /* @__PURE__ */ new Set());
|
|
67
|
+
}
|
|
68
|
+
typeGraph.get(typeRef).add(file);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return typeGraph;
|
|
74
|
+
}
|
|
75
|
+
function calculateDomainConfidence(signals) {
|
|
76
|
+
const weights = {
|
|
77
|
+
coUsage: 0.35,
|
|
78
|
+
// Strongest signal: actual usage patterns
|
|
79
|
+
typeReference: 0.3,
|
|
80
|
+
// Strong signal: shared types
|
|
81
|
+
exportName: 0.15,
|
|
82
|
+
// Medium signal: identifier semantics
|
|
83
|
+
importPath: 0.1,
|
|
84
|
+
// Weaker signal: path structure
|
|
85
|
+
folderStructure: 0.1
|
|
86
|
+
// Weakest signal: organization convention
|
|
87
|
+
};
|
|
88
|
+
let confidence = 0;
|
|
89
|
+
if (signals.coUsage) confidence += weights.coUsage;
|
|
90
|
+
if (signals.typeReference) confidence += weights.typeReference;
|
|
91
|
+
if (signals.exportName) confidence += weights.exportName;
|
|
92
|
+
if (signals.importPath) confidence += weights.importPath;
|
|
93
|
+
if (signals.folderStructure) confidence += weights.folderStructure;
|
|
94
|
+
return confidence;
|
|
95
|
+
}
|
|
96
|
+
function inferDomainFromSemantics(file, exportName, graph, coUsageMatrix, typeGraph, exportTypeRefs) {
|
|
97
|
+
const assignments = [];
|
|
98
|
+
const domainSignals = /* @__PURE__ */ new Map();
|
|
99
|
+
const coUsages = coUsageMatrix.get(file) || /* @__PURE__ */ new Map();
|
|
100
|
+
const strongCoUsages = Array.from(coUsages.entries()).filter(([_, count]) => count >= 3).map(([coFile]) => coFile);
|
|
101
|
+
for (const coFile of strongCoUsages) {
|
|
102
|
+
const coNode = graph.nodes.get(coFile);
|
|
103
|
+
if (coNode) {
|
|
104
|
+
for (const exp of coNode.exports) {
|
|
105
|
+
if (exp.inferredDomain && exp.inferredDomain !== "unknown") {
|
|
106
|
+
const domain = exp.inferredDomain;
|
|
107
|
+
if (!domainSignals.has(domain)) {
|
|
108
|
+
domainSignals.set(domain, {
|
|
109
|
+
coUsage: false,
|
|
110
|
+
typeReference: false,
|
|
111
|
+
exportName: false,
|
|
112
|
+
importPath: false,
|
|
113
|
+
folderStructure: false
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
domainSignals.get(domain).coUsage = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (exportTypeRefs) {
|
|
122
|
+
for (const typeRef of exportTypeRefs) {
|
|
123
|
+
const filesWithType = typeGraph.get(typeRef);
|
|
124
|
+
if (filesWithType) {
|
|
125
|
+
for (const typeFile of filesWithType) {
|
|
126
|
+
if (typeFile !== file) {
|
|
127
|
+
const typeNode = graph.nodes.get(typeFile);
|
|
128
|
+
if (typeNode) {
|
|
129
|
+
for (const exp of typeNode.exports) {
|
|
130
|
+
if (exp.inferredDomain && exp.inferredDomain !== "unknown") {
|
|
131
|
+
const domain = exp.inferredDomain;
|
|
132
|
+
if (!domainSignals.has(domain)) {
|
|
133
|
+
domainSignals.set(domain, {
|
|
134
|
+
coUsage: false,
|
|
135
|
+
typeReference: false,
|
|
136
|
+
exportName: false,
|
|
137
|
+
importPath: false,
|
|
138
|
+
folderStructure: false
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
domainSignals.get(domain).typeReference = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const [domain, signals] of domainSignals) {
|
|
151
|
+
const confidence = calculateDomainConfidence(signals);
|
|
152
|
+
if (confidence >= 0.3) {
|
|
153
|
+
assignments.push({ domain, confidence, signals });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
assignments.sort((a, b) => b.confidence - a.confidence);
|
|
157
|
+
return assignments;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/analyzer.ts
|
|
161
|
+
function extractDomainKeywordsFromPaths(files) {
|
|
162
|
+
const folderNames = /* @__PURE__ */ new Set();
|
|
163
|
+
for (const { file } of files) {
|
|
164
|
+
const segments = file.split("/");
|
|
165
|
+
const skipFolders = /* @__PURE__ */ new Set(["src", "lib", "dist", "build", "node_modules", "test", "tests", "__tests__", "spec", "e2e", "scripts", "components", "utils", "helpers", "util", "helper", "api", "apis"]);
|
|
166
|
+
for (const segment of segments) {
|
|
167
|
+
const normalized = segment.toLowerCase();
|
|
168
|
+
if (normalized && !skipFolders.has(normalized) && !normalized.includes(".")) {
|
|
169
|
+
const singular = singularize(normalized);
|
|
170
|
+
folderNames.add(singular);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return Array.from(folderNames);
|
|
175
|
+
}
|
|
176
|
+
function singularize(word) {
|
|
177
|
+
const irregulars = {
|
|
178
|
+
people: "person",
|
|
179
|
+
children: "child",
|
|
180
|
+
men: "man",
|
|
181
|
+
women: "woman"
|
|
182
|
+
};
|
|
183
|
+
if (irregulars[word]) {
|
|
184
|
+
return irregulars[word];
|
|
185
|
+
}
|
|
186
|
+
if (word.endsWith("ies")) {
|
|
187
|
+
return word.slice(0, -3) + "y";
|
|
188
|
+
}
|
|
189
|
+
if (word.endsWith("ses")) {
|
|
190
|
+
return word.slice(0, -2);
|
|
191
|
+
}
|
|
192
|
+
if (word.endsWith("s") && word.length > 3) {
|
|
193
|
+
return word.slice(0, -1);
|
|
194
|
+
}
|
|
195
|
+
return word;
|
|
196
|
+
}
|
|
34
197
|
function buildDependencyGraph(files) {
|
|
35
198
|
const nodes = /* @__PURE__ */ new Map();
|
|
36
199
|
const edges = /* @__PURE__ */ new Map();
|
|
200
|
+
const autoDetectedKeywords = extractDomainKeywordsFromPaths(files);
|
|
37
201
|
for (const { file, content } of files) {
|
|
38
202
|
const imports = extractImportsFromContent(content);
|
|
39
|
-
const exports2 =
|
|
203
|
+
const exports2 = extractExportsWithAST(content, file, { domainKeywords: autoDetectedKeywords }, imports);
|
|
40
204
|
const tokenCost = (0, import_core.estimateTokens)(content);
|
|
41
205
|
const linesOfCode = content.split("\n").length;
|
|
42
206
|
nodes.set(file, {
|
|
@@ -48,7 +212,28 @@ function buildDependencyGraph(files) {
|
|
|
48
212
|
});
|
|
49
213
|
edges.set(file, new Set(imports));
|
|
50
214
|
}
|
|
51
|
-
|
|
215
|
+
const graph = { nodes, edges };
|
|
216
|
+
const coUsageMatrix = buildCoUsageMatrix(graph);
|
|
217
|
+
const typeGraph = buildTypeGraph(graph);
|
|
218
|
+
graph.coUsageMatrix = coUsageMatrix;
|
|
219
|
+
graph.typeGraph = typeGraph;
|
|
220
|
+
for (const [file, node] of nodes) {
|
|
221
|
+
for (const exp of node.exports) {
|
|
222
|
+
const semanticAssignments = inferDomainFromSemantics(
|
|
223
|
+
file,
|
|
224
|
+
exp.name,
|
|
225
|
+
graph,
|
|
226
|
+
coUsageMatrix,
|
|
227
|
+
typeGraph,
|
|
228
|
+
exp.typeReferences
|
|
229
|
+
);
|
|
230
|
+
exp.domains = semanticAssignments;
|
|
231
|
+
if (semanticAssignments.length > 0) {
|
|
232
|
+
exp.inferredDomain = semanticAssignments[0].domain;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return graph;
|
|
52
237
|
}
|
|
53
238
|
function extractImportsFromContent(content) {
|
|
54
239
|
const imports = [];
|
|
@@ -64,7 +249,7 @@ function extractImportsFromContent(content) {
|
|
|
64
249
|
let match;
|
|
65
250
|
while ((match = pattern.exec(content)) !== null) {
|
|
66
251
|
const importPath = match[1];
|
|
67
|
-
if (importPath && !importPath.startsWith("
|
|
252
|
+
if (importPath && !importPath.startsWith("node:")) {
|
|
68
253
|
imports.push(importPath);
|
|
69
254
|
}
|
|
70
255
|
}
|
|
@@ -151,26 +336,7 @@ function detectCircularDependencies(graph) {
|
|
|
151
336
|
return cycles;
|
|
152
337
|
}
|
|
153
338
|
function calculateCohesion(exports2, filePath) {
|
|
154
|
-
|
|
155
|
-
if (exports2.length === 1) return 1;
|
|
156
|
-
if (filePath && isTestFile(filePath)) {
|
|
157
|
-
return 1;
|
|
158
|
-
}
|
|
159
|
-
const domains = exports2.map((e) => e.inferredDomain || "unknown");
|
|
160
|
-
const domainCounts = /* @__PURE__ */ new Map();
|
|
161
|
-
for (const domain of domains) {
|
|
162
|
-
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
163
|
-
}
|
|
164
|
-
const total = domains.length;
|
|
165
|
-
let entropy = 0;
|
|
166
|
-
for (const count of domainCounts.values()) {
|
|
167
|
-
const p = count / total;
|
|
168
|
-
if (p > 0) {
|
|
169
|
-
entropy -= p * Math.log2(p);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const maxEntropy = Math.log2(total);
|
|
173
|
-
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
339
|
+
return calculateEnhancedCohesion(exports2, filePath);
|
|
174
340
|
}
|
|
175
341
|
function isTestFile(filePath) {
|
|
176
342
|
const lower = filePath.toLowerCase();
|
|
@@ -223,7 +389,7 @@ function detectModuleClusters(graph) {
|
|
|
223
389
|
}
|
|
224
390
|
return clusters.sort((a, b) => b.fragmentationScore - a.fragmentationScore);
|
|
225
391
|
}
|
|
226
|
-
function extractExports(content) {
|
|
392
|
+
function extractExports(content, filePath, domainOptions, fileImports) {
|
|
227
393
|
const exports2 = [];
|
|
228
394
|
const patterns = [
|
|
229
395
|
/export\s+function\s+(\w+)/g,
|
|
@@ -246,15 +412,20 @@ function extractExports(content) {
|
|
|
246
412
|
while ((match = pattern.exec(content)) !== null) {
|
|
247
413
|
const name = match[1] || "default";
|
|
248
414
|
const type = types[index];
|
|
249
|
-
const inferredDomain = inferDomain(name);
|
|
415
|
+
const inferredDomain = inferDomain(name, filePath, domainOptions, fileImports);
|
|
250
416
|
exports2.push({ name, type, inferredDomain });
|
|
251
417
|
}
|
|
252
418
|
});
|
|
253
419
|
return exports2;
|
|
254
420
|
}
|
|
255
|
-
function inferDomain(name) {
|
|
421
|
+
function inferDomain(name, filePath, domainOptions, fileImports) {
|
|
256
422
|
const lower = name.toLowerCase();
|
|
257
|
-
const
|
|
423
|
+
const tokens = Array.from(
|
|
424
|
+
new Set(
|
|
425
|
+
lower.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-z0-9]+/gi, " ").split(" ").filter(Boolean)
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
const defaultKeywords = [
|
|
258
429
|
"authentication",
|
|
259
430
|
"authorization",
|
|
260
431
|
"payment",
|
|
@@ -271,14 +442,11 @@ function inferDomain(name) {
|
|
|
271
442
|
"config",
|
|
272
443
|
"model",
|
|
273
444
|
"view",
|
|
274
|
-
"auth"
|
|
275
|
-
"api",
|
|
276
|
-
"helper",
|
|
277
|
-
"util"
|
|
445
|
+
"auth"
|
|
278
446
|
];
|
|
447
|
+
const domainKeywords = domainOptions?.domainKeywords && domainOptions.domainKeywords.length ? [...domainOptions.domainKeywords, ...defaultKeywords] : defaultKeywords;
|
|
279
448
|
for (const keyword of domainKeywords) {
|
|
280
|
-
|
|
281
|
-
if (wordBoundaryPattern.test(name)) {
|
|
449
|
+
if (tokens.includes(keyword)) {
|
|
282
450
|
return keyword;
|
|
283
451
|
}
|
|
284
452
|
}
|
|
@@ -287,6 +455,37 @@ function inferDomain(name) {
|
|
|
287
455
|
return keyword;
|
|
288
456
|
}
|
|
289
457
|
}
|
|
458
|
+
if (fileImports && fileImports.length > 0) {
|
|
459
|
+
for (const importPath of fileImports) {
|
|
460
|
+
const allSegments = importPath.split("/");
|
|
461
|
+
const relevantSegments = allSegments.filter((s) => {
|
|
462
|
+
if (!s) return false;
|
|
463
|
+
if (s === "." || s === "..") return false;
|
|
464
|
+
if (s.startsWith("@") && s.length === 1) return false;
|
|
465
|
+
return true;
|
|
466
|
+
}).map((s) => s.startsWith("@") ? s.slice(1) : s);
|
|
467
|
+
for (const segment of relevantSegments) {
|
|
468
|
+
const segLower = segment.toLowerCase();
|
|
469
|
+
const singularSegment = singularize(segLower);
|
|
470
|
+
for (const keyword of domainKeywords) {
|
|
471
|
+
if (singularSegment === keyword || segLower === keyword || segLower.includes(keyword)) {
|
|
472
|
+
return keyword;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (filePath) {
|
|
479
|
+
const pathSegments = filePath.toLowerCase().split("/");
|
|
480
|
+
for (const segment of pathSegments) {
|
|
481
|
+
const singularSegment = singularize(segment);
|
|
482
|
+
for (const keyword of domainKeywords) {
|
|
483
|
+
if (singularSegment === keyword || segment === keyword || segment.includes(keyword)) {
|
|
484
|
+
return keyword;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
290
489
|
return "unknown";
|
|
291
490
|
}
|
|
292
491
|
function generateConsolidationPlan(domain, files, targetFiles) {
|
|
@@ -315,6 +514,78 @@ function generateConsolidationPlan(domain, files, targetFiles) {
|
|
|
315
514
|
);
|
|
316
515
|
return plan;
|
|
317
516
|
}
|
|
517
|
+
function extractExportsWithAST(content, filePath, domainOptions, fileImports) {
|
|
518
|
+
try {
|
|
519
|
+
const { exports: astExports } = (0, import_core.parseFileExports)(content, filePath);
|
|
520
|
+
return astExports.map((exp) => ({
|
|
521
|
+
name: exp.name,
|
|
522
|
+
type: exp.type,
|
|
523
|
+
inferredDomain: inferDomain(exp.name, filePath, domainOptions, fileImports),
|
|
524
|
+
imports: exp.imports,
|
|
525
|
+
dependencies: exp.dependencies
|
|
526
|
+
}));
|
|
527
|
+
} catch (error) {
|
|
528
|
+
return extractExports(content, filePath, domainOptions, fileImports);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function calculateEnhancedCohesion(exports2, filePath) {
|
|
532
|
+
if (exports2.length === 0) return 1;
|
|
533
|
+
if (exports2.length === 1) return 1;
|
|
534
|
+
if (filePath && isTestFile(filePath)) {
|
|
535
|
+
return 1;
|
|
536
|
+
}
|
|
537
|
+
const domainCohesion = calculateDomainCohesion(exports2);
|
|
538
|
+
const hasImportData = exports2.some((e) => e.imports && e.imports.length > 0);
|
|
539
|
+
if (!hasImportData) {
|
|
540
|
+
return domainCohesion;
|
|
541
|
+
}
|
|
542
|
+
const importCohesion = calculateImportBasedCohesion(exports2);
|
|
543
|
+
return importCohesion * 0.6 + domainCohesion * 0.4;
|
|
544
|
+
}
|
|
545
|
+
function calculateImportBasedCohesion(exports2) {
|
|
546
|
+
const exportsWithImports = exports2.filter((e) => e.imports && e.imports.length > 0);
|
|
547
|
+
if (exportsWithImports.length < 2) {
|
|
548
|
+
return 1;
|
|
549
|
+
}
|
|
550
|
+
let totalSimilarity = 0;
|
|
551
|
+
let comparisons = 0;
|
|
552
|
+
for (let i = 0; i < exportsWithImports.length; i++) {
|
|
553
|
+
for (let j = i + 1; j < exportsWithImports.length; j++) {
|
|
554
|
+
const exp1 = exportsWithImports[i];
|
|
555
|
+
const exp2 = exportsWithImports[j];
|
|
556
|
+
const similarity = calculateJaccardSimilarity(exp1.imports, exp2.imports);
|
|
557
|
+
totalSimilarity += similarity;
|
|
558
|
+
comparisons++;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return comparisons > 0 ? totalSimilarity / comparisons : 1;
|
|
562
|
+
}
|
|
563
|
+
function calculateJaccardSimilarity(arr1, arr2) {
|
|
564
|
+
if (arr1.length === 0 && arr2.length === 0) return 1;
|
|
565
|
+
if (arr1.length === 0 || arr2.length === 0) return 0;
|
|
566
|
+
const set1 = new Set(arr1);
|
|
567
|
+
const set2 = new Set(arr2);
|
|
568
|
+
const intersection = new Set([...set1].filter((x) => set2.has(x)));
|
|
569
|
+
const union = /* @__PURE__ */ new Set([...set1, ...set2]);
|
|
570
|
+
return intersection.size / union.size;
|
|
571
|
+
}
|
|
572
|
+
function calculateDomainCohesion(exports2) {
|
|
573
|
+
const domains = exports2.map((e) => e.inferredDomain || "unknown");
|
|
574
|
+
const domainCounts = /* @__PURE__ */ new Map();
|
|
575
|
+
for (const domain of domains) {
|
|
576
|
+
domainCounts.set(domain, (domainCounts.get(domain) || 0) + 1);
|
|
577
|
+
}
|
|
578
|
+
const total = domains.length;
|
|
579
|
+
let entropy = 0;
|
|
580
|
+
for (const count of domainCounts.values()) {
|
|
581
|
+
const p = count / total;
|
|
582
|
+
if (p > 0) {
|
|
583
|
+
entropy -= p * Math.log2(p);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const maxEntropy = Math.log2(total);
|
|
587
|
+
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
588
|
+
}
|
|
318
589
|
|
|
319
590
|
// src/index.ts
|
|
320
591
|
async function analyzeContext(options) {
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -66,6 +66,95 @@ interface ContextSummary {
|
|
|
66
66
|
severity: string;
|
|
67
67
|
}>;
|
|
68
68
|
}
|
|
69
|
+
interface DependencyGraph {
|
|
70
|
+
nodes: Map<string, DependencyNode>;
|
|
71
|
+
edges: Map<string, Set<string>>;
|
|
72
|
+
coUsageMatrix?: Map<string, Map<string, number>>;
|
|
73
|
+
typeGraph?: Map<string, Set<string>>;
|
|
74
|
+
}
|
|
75
|
+
interface DependencyNode {
|
|
76
|
+
file: string;
|
|
77
|
+
imports: string[];
|
|
78
|
+
exports: ExportInfo[];
|
|
79
|
+
tokenCost: number;
|
|
80
|
+
linesOfCode: number;
|
|
81
|
+
exportedBy?: string[];
|
|
82
|
+
sharedTypes?: string[];
|
|
83
|
+
}
|
|
84
|
+
interface ExportInfo {
|
|
85
|
+
name: string;
|
|
86
|
+
type: 'function' | 'class' | 'const' | 'type' | 'interface' | 'default';
|
|
87
|
+
inferredDomain?: string;
|
|
88
|
+
domains?: DomainAssignment[];
|
|
89
|
+
imports?: string[];
|
|
90
|
+
dependencies?: string[];
|
|
91
|
+
typeReferences?: string[];
|
|
92
|
+
}
|
|
93
|
+
interface DomainAssignment {
|
|
94
|
+
domain: string;
|
|
95
|
+
confidence: number;
|
|
96
|
+
signals: DomainSignals;
|
|
97
|
+
}
|
|
98
|
+
interface DomainSignals {
|
|
99
|
+
folderStructure: boolean;
|
|
100
|
+
importPath: boolean;
|
|
101
|
+
typeReference: boolean;
|
|
102
|
+
coUsage: boolean;
|
|
103
|
+
exportName: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface CoUsageData {
|
|
106
|
+
file: string;
|
|
107
|
+
coImportedWith: Map<string, number>;
|
|
108
|
+
sharedImporters: string[];
|
|
109
|
+
}
|
|
110
|
+
interface TypeDependency {
|
|
111
|
+
typeName: string;
|
|
112
|
+
definedIn: string;
|
|
113
|
+
usedBy: string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Build co-usage matrix: track which files are imported together
|
|
118
|
+
*
|
|
119
|
+
* Files frequently imported together likely belong to the same semantic domain
|
|
120
|
+
*/
|
|
121
|
+
declare function buildCoUsageMatrix(graph: DependencyGraph): Map<string, Map<string, number>>;
|
|
122
|
+
/**
|
|
123
|
+
* Extract type dependencies from AST exports
|
|
124
|
+
*
|
|
125
|
+
* Files that share types are semantically related
|
|
126
|
+
*/
|
|
127
|
+
declare function buildTypeGraph(graph: DependencyGraph): Map<string, Set<string>>;
|
|
128
|
+
/**
|
|
129
|
+
* Find semantic clusters using co-usage patterns
|
|
130
|
+
*
|
|
131
|
+
* Files with high co-usage counts belong in the same cluster
|
|
132
|
+
*/
|
|
133
|
+
declare function findSemanticClusters(coUsageMatrix: Map<string, Map<string, number>>, minCoUsage?: number): Map<string, string[]>;
|
|
134
|
+
/**
|
|
135
|
+
* Calculate confidence score for domain assignment based on multiple signals
|
|
136
|
+
*/
|
|
137
|
+
declare function calculateDomainConfidence(signals: DomainSignals): number;
|
|
138
|
+
/**
|
|
139
|
+
* Infer domain from semantic analysis (co-usage + types)
|
|
140
|
+
*
|
|
141
|
+
* This replaces the folder-based heuristic with actual code relationships
|
|
142
|
+
*/
|
|
143
|
+
declare function inferDomainFromSemantics(file: string, exportName: string, graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, exportTypeRefs?: string[]): DomainAssignment[];
|
|
144
|
+
/**
|
|
145
|
+
* Get co-usage data for a specific file
|
|
146
|
+
*/
|
|
147
|
+
declare function getCoUsageData(file: string, coUsageMatrix: Map<string, Map<string, number>>): CoUsageData;
|
|
148
|
+
/**
|
|
149
|
+
* Find files that should be consolidated based on semantic similarity
|
|
150
|
+
*
|
|
151
|
+
* High co-usage + shared types = strong consolidation candidate
|
|
152
|
+
*/
|
|
153
|
+
declare function findConsolidationCandidates(graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, minCoUsage?: number, minSharedTypes?: number): Array<{
|
|
154
|
+
files: string[];
|
|
155
|
+
reason: string;
|
|
156
|
+
strength: number;
|
|
157
|
+
}>;
|
|
69
158
|
|
|
70
159
|
/**
|
|
71
160
|
* Generate smart defaults for context analysis based on repository size
|
|
@@ -81,4 +170,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
|
|
|
81
170
|
*/
|
|
82
171
|
declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
|
|
83
172
|
|
|
84
|
-
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary, getSmartDefaults };
|
|
173
|
+
export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type ModuleCluster, type TypeDependency, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateDomainConfidence, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
|
package/dist/index.d.ts
CHANGED
|
@@ -66,6 +66,95 @@ interface ContextSummary {
|
|
|
66
66
|
severity: string;
|
|
67
67
|
}>;
|
|
68
68
|
}
|
|
69
|
+
interface DependencyGraph {
|
|
70
|
+
nodes: Map<string, DependencyNode>;
|
|
71
|
+
edges: Map<string, Set<string>>;
|
|
72
|
+
coUsageMatrix?: Map<string, Map<string, number>>;
|
|
73
|
+
typeGraph?: Map<string, Set<string>>;
|
|
74
|
+
}
|
|
75
|
+
interface DependencyNode {
|
|
76
|
+
file: string;
|
|
77
|
+
imports: string[];
|
|
78
|
+
exports: ExportInfo[];
|
|
79
|
+
tokenCost: number;
|
|
80
|
+
linesOfCode: number;
|
|
81
|
+
exportedBy?: string[];
|
|
82
|
+
sharedTypes?: string[];
|
|
83
|
+
}
|
|
84
|
+
interface ExportInfo {
|
|
85
|
+
name: string;
|
|
86
|
+
type: 'function' | 'class' | 'const' | 'type' | 'interface' | 'default';
|
|
87
|
+
inferredDomain?: string;
|
|
88
|
+
domains?: DomainAssignment[];
|
|
89
|
+
imports?: string[];
|
|
90
|
+
dependencies?: string[];
|
|
91
|
+
typeReferences?: string[];
|
|
92
|
+
}
|
|
93
|
+
interface DomainAssignment {
|
|
94
|
+
domain: string;
|
|
95
|
+
confidence: number;
|
|
96
|
+
signals: DomainSignals;
|
|
97
|
+
}
|
|
98
|
+
interface DomainSignals {
|
|
99
|
+
folderStructure: boolean;
|
|
100
|
+
importPath: boolean;
|
|
101
|
+
typeReference: boolean;
|
|
102
|
+
coUsage: boolean;
|
|
103
|
+
exportName: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface CoUsageData {
|
|
106
|
+
file: string;
|
|
107
|
+
coImportedWith: Map<string, number>;
|
|
108
|
+
sharedImporters: string[];
|
|
109
|
+
}
|
|
110
|
+
interface TypeDependency {
|
|
111
|
+
typeName: string;
|
|
112
|
+
definedIn: string;
|
|
113
|
+
usedBy: string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Build co-usage matrix: track which files are imported together
|
|
118
|
+
*
|
|
119
|
+
* Files frequently imported together likely belong to the same semantic domain
|
|
120
|
+
*/
|
|
121
|
+
declare function buildCoUsageMatrix(graph: DependencyGraph): Map<string, Map<string, number>>;
|
|
122
|
+
/**
|
|
123
|
+
* Extract type dependencies from AST exports
|
|
124
|
+
*
|
|
125
|
+
* Files that share types are semantically related
|
|
126
|
+
*/
|
|
127
|
+
declare function buildTypeGraph(graph: DependencyGraph): Map<string, Set<string>>;
|
|
128
|
+
/**
|
|
129
|
+
* Find semantic clusters using co-usage patterns
|
|
130
|
+
*
|
|
131
|
+
* Files with high co-usage counts belong in the same cluster
|
|
132
|
+
*/
|
|
133
|
+
declare function findSemanticClusters(coUsageMatrix: Map<string, Map<string, number>>, minCoUsage?: number): Map<string, string[]>;
|
|
134
|
+
/**
|
|
135
|
+
* Calculate confidence score for domain assignment based on multiple signals
|
|
136
|
+
*/
|
|
137
|
+
declare function calculateDomainConfidence(signals: DomainSignals): number;
|
|
138
|
+
/**
|
|
139
|
+
* Infer domain from semantic analysis (co-usage + types)
|
|
140
|
+
*
|
|
141
|
+
* This replaces the folder-based heuristic with actual code relationships
|
|
142
|
+
*/
|
|
143
|
+
declare function inferDomainFromSemantics(file: string, exportName: string, graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, exportTypeRefs?: string[]): DomainAssignment[];
|
|
144
|
+
/**
|
|
145
|
+
* Get co-usage data for a specific file
|
|
146
|
+
*/
|
|
147
|
+
declare function getCoUsageData(file: string, coUsageMatrix: Map<string, Map<string, number>>): CoUsageData;
|
|
148
|
+
/**
|
|
149
|
+
* Find files that should be consolidated based on semantic similarity
|
|
150
|
+
*
|
|
151
|
+
* High co-usage + shared types = strong consolidation candidate
|
|
152
|
+
*/
|
|
153
|
+
declare function findConsolidationCandidates(graph: DependencyGraph, coUsageMatrix: Map<string, Map<string, number>>, typeGraph: Map<string, Set<string>>, minCoUsage?: number, minSharedTypes?: number): Array<{
|
|
154
|
+
files: string[];
|
|
155
|
+
reason: string;
|
|
156
|
+
strength: number;
|
|
157
|
+
}>;
|
|
69
158
|
|
|
70
159
|
/**
|
|
71
160
|
* Generate smart defaults for context analysis based on repository size
|
|
@@ -81,4 +170,4 @@ declare function analyzeContext(options: ContextAnalyzerOptions): Promise<Contex
|
|
|
81
170
|
*/
|
|
82
171
|
declare function generateSummary(results: ContextAnalysisResult[]): ContextSummary;
|
|
83
172
|
|
|
84
|
-
export { type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type ModuleCluster, analyzeContext, generateSummary, getSmartDefaults };
|
|
173
|
+
export { type CoUsageData, type ContextAnalysisResult, type ContextAnalyzerOptions, type ContextSummary, type DomainAssignment, type DomainSignals, type ModuleCluster, type TypeDependency, analyzeContext, buildCoUsageMatrix, buildTypeGraph, calculateDomainConfidence, findConsolidationCandidates, findSemanticClusters, generateSummary, getCoUsageData, getSmartDefaults, inferDomainFromSemantics };
|