codemelt-core 0.1.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/analyzer/compressionAnalyzer.d.ts +4 -0
- package/dist/analyzer/compressionAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/compressionAnalyzer.js +21 -0
- package/dist/analyzer/index.d.ts +3 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +2 -0
- package/dist/analyzer/prompts/onboarding.d.ts +3 -0
- package/dist/analyzer/prompts/onboarding.d.ts.map +1 -0
- package/dist/analyzer/prompts/onboarding.js +14 -0
- package/dist/analyzer/prompts/performance.d.ts +3 -0
- package/dist/analyzer/prompts/performance.d.ts.map +1 -0
- package/dist/analyzer/prompts/performance.js +18 -0
- package/dist/analyzer/prompts/refactor.d.ts +3 -0
- package/dist/analyzer/prompts/refactor.d.ts.map +1 -0
- package/dist/analyzer/prompts/refactor.js +19 -0
- package/dist/analyzer/purposeDetector.d.ts +4 -0
- package/dist/analyzer/purposeDetector.d.ts.map +1 -0
- package/dist/analyzer/purposeDetector.js +141 -0
- package/dist/analyzer/readinessAnalyzer.d.ts +4 -0
- package/dist/analyzer/readinessAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/readinessAnalyzer.js +111 -0
- package/dist/analyzer/repositoryAnalyzer.d.ts +4 -0
- package/dist/analyzer/repositoryAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/repositoryAnalyzer.js +231 -0
- package/dist/analyzer/rules.d.ts +3 -0
- package/dist/analyzer/rules.d.ts.map +1 -0
- package/dist/analyzer/rules.js +156 -0
- package/dist/analyzer/types.d.ts +70 -0
- package/dist/analyzer/types.d.ts.map +1 -0
- package/dist/analyzer/types.js +1 -0
- package/dist/analyzer/workflowGenerator.d.ts +3 -0
- package/dist/analyzer/workflowGenerator.d.ts.map +1 -0
- package/dist/analyzer/workflowGenerator.js +22 -0
- package/dist/filters/shouldIgnore.d.ts +2 -0
- package/dist/filters/shouldIgnore.d.ts.map +1 -0
- package/dist/filters/shouldIgnore.js +10 -0
- package/dist/formatter/generateRepositoryContext.d.ts +10 -0
- package/dist/formatter/generateRepositoryContext.d.ts.map +1 -0
- package/dist/formatter/generateRepositoryContext.js +311 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/prioritizer/scoreFile.d.ts +3 -0
- package/dist/prioritizer/scoreFile.d.ts.map +1 -0
- package/dist/prioritizer/scoreFile.js +25 -0
- package/dist/prioritizer/sortFiles.d.ts +3 -0
- package/dist/prioritizer/sortFiles.d.ts.map +1 -0
- package/dist/prioritizer/sortFiles.js +9 -0
- package/dist/scanner/importanceDetector.d.ts +3 -0
- package/dist/scanner/importanceDetector.d.ts.map +1 -0
- package/dist/scanner/importanceDetector.js +70 -0
- package/dist/scanner/scanFiles.d.ts +3 -0
- package/dist/scanner/scanFiles.d.ts.map +1 -0
- package/dist/scanner/scanFiles.js +127 -0
- package/dist/summarizer/extractPatterns.d.ts +12 -0
- package/dist/summarizer/extractPatterns.d.ts.map +1 -0
- package/dist/summarizer/extractPatterns.js +83 -0
- package/dist/summarizer/flowDetector.d.ts +6 -0
- package/dist/summarizer/flowDetector.d.ts.map +1 -0
- package/dist/summarizer/flowDetector.js +29 -0
- package/dist/summarizer/index.d.ts +5 -0
- package/dist/summarizer/index.d.ts.map +1 -0
- package/dist/summarizer/index.js +25 -0
- package/dist/summarizer/inferPurpose.d.ts +10 -0
- package/dist/summarizer/inferPurpose.d.ts.map +1 -0
- package/dist/summarizer/inferPurpose.js +162 -0
- package/dist/summarizer/summarizeDirectory.d.ts +3 -0
- package/dist/summarizer/summarizeDirectory.d.ts.map +1 -0
- package/dist/summarizer/summarizeDirectory.js +87 -0
- package/dist/summarizer/summarizeFile.d.ts +4 -0
- package/dist/summarizer/summarizeFile.d.ts.map +1 -0
- package/dist/summarizer/summarizeFile.js +45 -0
- package/dist/summarizer/types.d.ts +35 -0
- package/dist/summarizer/types.d.ts.map +1 -0
- package/dist/summarizer/types.js +1 -0
- package/dist/tokenizer/estimateTokens.d.ts +2 -0
- package/dist/tokenizer/estimateTokens.d.ts.map +1 -0
- package/dist/tokenizer/estimateTokens.js +3 -0
- package/dist/tokenizer/estimateWords.d.ts +2 -0
- package/dist/tokenizer/estimateWords.d.ts.map +1 -0
- package/dist/tokenizer/estimateWords.js +6 -0
- package/package.json +28 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function scoreFile(file) {
|
|
2
|
+
const path = file.path.toLowerCase();
|
|
3
|
+
let score = 0;
|
|
4
|
+
if (path.includes("/src/"))
|
|
5
|
+
score += 30;
|
|
6
|
+
if (path.includes("/features/"))
|
|
7
|
+
score += 25;
|
|
8
|
+
if (path.includes("controller"))
|
|
9
|
+
score += 20;
|
|
10
|
+
if (path.includes("service"))
|
|
11
|
+
score += 20;
|
|
12
|
+
if (path.includes("routes"))
|
|
13
|
+
score += 15;
|
|
14
|
+
if (path.includes("model"))
|
|
15
|
+
score += 10;
|
|
16
|
+
if (path.includes("middleware"))
|
|
17
|
+
score += 10;
|
|
18
|
+
if (path.includes("config"))
|
|
19
|
+
score -= 10;
|
|
20
|
+
if (path.includes("package-lock"))
|
|
21
|
+
score -= 50;
|
|
22
|
+
if (path.includes("tsconfig"))
|
|
23
|
+
score -= 20;
|
|
24
|
+
return score;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortFiles.d.ts","sourceRoot":"","sources":["../../src/prioritizer/sortFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,wBAAgB,SAAS,CACrB,KAAK,EAAE,WAAW,EAAE,GACrB,WAAW,EAAE,CAMf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importanceDetector.d.ts","sourceRoot":"","sources":["../../src/scanner/importanceDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,kBAAkB,CAAC;AAEjE,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,cAAc,CA8EhB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { normalizePath } from "@codemelt/shared";
|
|
2
|
+
export function detectImportance(path, name) {
|
|
3
|
+
const normalized = normalizePath(path);
|
|
4
|
+
const lowerPath = normalized.toLowerCase();
|
|
5
|
+
const lowerName = name.toLowerCase();
|
|
6
|
+
// --- 1. CRITICAL: Entrypoints & Core Configs ---
|
|
7
|
+
const criticalConfigs = new Set([
|
|
8
|
+
"package.json",
|
|
9
|
+
"tsconfig.json",
|
|
10
|
+
"schema.prisma",
|
|
11
|
+
"drizzle.config.ts",
|
|
12
|
+
"drizzle.config.js",
|
|
13
|
+
"next.config.js",
|
|
14
|
+
"next.config.mjs",
|
|
15
|
+
"next.config.ts",
|
|
16
|
+
"vite.config.ts",
|
|
17
|
+
"vite.config.js",
|
|
18
|
+
"tailwind.config.js",
|
|
19
|
+
"tailwind.config.ts",
|
|
20
|
+
"tailwind.config.cjs",
|
|
21
|
+
".gitignore"
|
|
22
|
+
]);
|
|
23
|
+
if (criticalConfigs.has(lowerName)) {
|
|
24
|
+
return "critical";
|
|
25
|
+
}
|
|
26
|
+
const criticalEntrypoints = new Set([
|
|
27
|
+
"src/index.ts",
|
|
28
|
+
"src/index.tsx",
|
|
29
|
+
"src/main.ts",
|
|
30
|
+
"src/main.tsx",
|
|
31
|
+
"src/app.tsx",
|
|
32
|
+
"src/app.ts",
|
|
33
|
+
"app/layout.tsx",
|
|
34
|
+
"app/page.tsx",
|
|
35
|
+
"pages/_app.tsx",
|
|
36
|
+
"pages/index.tsx"
|
|
37
|
+
]);
|
|
38
|
+
if (criticalEntrypoints.has(lowerPath) || criticalEntrypoints.has(normalized)) {
|
|
39
|
+
return "critical";
|
|
40
|
+
}
|
|
41
|
+
// --- 2. HIGH: Central Logic & Systems Routing ---
|
|
42
|
+
if (lowerPath.includes("route") ||
|
|
43
|
+
lowerPath.includes("controller") ||
|
|
44
|
+
lowerPath.includes("schema") ||
|
|
45
|
+
lowerPath.includes("store") ||
|
|
46
|
+
lowerPath.includes("service") ||
|
|
47
|
+
lowerPath.includes("models/") ||
|
|
48
|
+
lowerPath.includes("db/") ||
|
|
49
|
+
lowerName.endsWith(".d.ts")) {
|
|
50
|
+
return "high";
|
|
51
|
+
}
|
|
52
|
+
// --- 3. LOW: Secondary Configurations & Noise ---
|
|
53
|
+
const lowConfigs = new Set([
|
|
54
|
+
"tsconfig.node.json",
|
|
55
|
+
"package-lock.json",
|
|
56
|
+
"yarn.lock",
|
|
57
|
+
"pnpm-lock.yaml",
|
|
58
|
+
".prettierrc",
|
|
59
|
+
".eslintrc",
|
|
60
|
+
".eslintrc.json",
|
|
61
|
+
"eslint.config.js",
|
|
62
|
+
".env.example",
|
|
63
|
+
".env.template"
|
|
64
|
+
]);
|
|
65
|
+
if (lowConfigs.has(lowerName) || lowerName.startsWith(".env")) {
|
|
66
|
+
return "low";
|
|
67
|
+
}
|
|
68
|
+
// --- 4. NORMAL: Standard logic files ---
|
|
69
|
+
return "normal";
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanFiles.d.ts","sourceRoot":"","sources":["../../src/scanner/scanFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,UAAU,EAAuF,MAAM,kBAAkB,CAAC;AAmBzJ,wBAAsB,SAAS,CAC3B,KAAK,EAAE,OAAO,EAAE,EAChB,WAAW,CAAC,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,UAAU,CAAC,CAuHrB"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { normalizePath, MAX_EXPORT_FILES, MAX_FILE_SIZE, MAX_CONTENT_CHARS, MAX_EXPORT_BYTES } from "@codemelt/shared";
|
|
2
|
+
import { shouldIgnore } from "../filters/shouldIgnore.js";
|
|
3
|
+
import { detectImportance } from "./importanceDetector.js";
|
|
4
|
+
const BINARY_EXTENSIONS = new Set([
|
|
5
|
+
"png", "jpg", "jpeg", "gif", "webp", "ico",
|
|
6
|
+
"pdf", "zip", "tar", "gz", "rar", "7z",
|
|
7
|
+
"mp4", "mp3", "wav", "avi", "mov", "flac",
|
|
8
|
+
"exe", "dll", "so", "dylib",
|
|
9
|
+
"woff", "woff2", "ttf", "eot",
|
|
10
|
+
"db", "sqlite", "sqlite3",
|
|
11
|
+
"bin", "dat", "pyc", "class", "o", "obj",
|
|
12
|
+
]);
|
|
13
|
+
function isBinary(filename) {
|
|
14
|
+
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
15
|
+
return BINARY_EXTENSIONS.has(ext);
|
|
16
|
+
}
|
|
17
|
+
export async function scanFiles(files, customRules) {
|
|
18
|
+
const scannedFiles = [];
|
|
19
|
+
let ignoredCount = 0;
|
|
20
|
+
let ignoredBytes = 0;
|
|
21
|
+
let totalBytesAccumulator = 0;
|
|
22
|
+
// Normalizing paths across the raw files array
|
|
23
|
+
const normalizedFiles = files.map(file => ({
|
|
24
|
+
...file,
|
|
25
|
+
path: normalizePath(file.path)
|
|
26
|
+
}));
|
|
27
|
+
// Capping maximum number of files to index to protect memory
|
|
28
|
+
let eligibleFilesList = normalizedFiles;
|
|
29
|
+
if (eligibleFilesList.length > MAX_EXPORT_FILES) {
|
|
30
|
+
console.warn(`[CodeMelt] Safety boundary cap hit: repository contains ${eligibleFilesList.length} files. Restricting active index depth to ${MAX_EXPORT_FILES} items.`);
|
|
31
|
+
eligibleFilesList = eligibleFilesList.slice(0, MAX_EXPORT_FILES);
|
|
32
|
+
}
|
|
33
|
+
// Single-pass deterministic ignore and size tracking
|
|
34
|
+
const eligibleFiles = eligibleFilesList.filter((file) => {
|
|
35
|
+
const ignored = shouldIgnore(file.path, customRules);
|
|
36
|
+
if (ignored) {
|
|
37
|
+
ignoredCount++;
|
|
38
|
+
ignoredBytes += file.size;
|
|
39
|
+
}
|
|
40
|
+
return !ignored;
|
|
41
|
+
});
|
|
42
|
+
if (files.length > 500) {
|
|
43
|
+
console.warn("[CodeMelt] Large repository detected. Export may take longer and use significant system resources.");
|
|
44
|
+
}
|
|
45
|
+
const BATCH_SIZE = 20;
|
|
46
|
+
for (let i = 0; i < eligibleFiles.length; i += BATCH_SIZE) {
|
|
47
|
+
const batch = eligibleFiles.slice(i, i + BATCH_SIZE);
|
|
48
|
+
const processedBatch = await Promise.all(batch.map(async (file) => {
|
|
49
|
+
const extension = file.name.split(".").pop()?.toLowerCase() || "";
|
|
50
|
+
const size = file.size;
|
|
51
|
+
let type = "text";
|
|
52
|
+
let content = "";
|
|
53
|
+
let isBin = isBinary(file.name);
|
|
54
|
+
// Safe order flow: type detection ➔ sizing limits ➔ only then read textual content
|
|
55
|
+
if (!isBin && file.readChunk && size > 0) {
|
|
56
|
+
try {
|
|
57
|
+
const chunk = await file.readChunk(Math.min(size, 4096));
|
|
58
|
+
let nullBytes = 0;
|
|
59
|
+
let nonPrintable = 0;
|
|
60
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
61
|
+
const byte = chunk[i];
|
|
62
|
+
if (byte === 0)
|
|
63
|
+
nullBytes++;
|
|
64
|
+
else if ((byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) || byte > 126)
|
|
65
|
+
nonPrintable++;
|
|
66
|
+
}
|
|
67
|
+
if (nullBytes > 0 || (chunk.length > 0 && nonPrintable / chunk.length > 0.3)) {
|
|
68
|
+
isBin = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
// fallback
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (isBin) {
|
|
76
|
+
type = "binary";
|
|
77
|
+
content = "[Binary file content omitted]";
|
|
78
|
+
}
|
|
79
|
+
else if (size > MAX_FILE_SIZE) {
|
|
80
|
+
type = "oversized";
|
|
81
|
+
content = `[Oversized file content omitted (${(size / 1024 / 1024).toFixed(1)}MB > 1MB limit)]`;
|
|
82
|
+
}
|
|
83
|
+
else if (totalBytesAccumulator > MAX_EXPORT_BYTES) {
|
|
84
|
+
type = "oversized";
|
|
85
|
+
content = "[Omitted by CodeMelt due to total memory ceiling limits exceeded]";
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
try {
|
|
89
|
+
const rawText = await file.text();
|
|
90
|
+
totalBytesAccumulator += size;
|
|
91
|
+
// Safe content length characters truncation cap
|
|
92
|
+
if (rawText.length > MAX_CONTENT_CHARS) {
|
|
93
|
+
content = rawText.slice(0, MAX_CONTENT_CHARS) + "\n\n[Content truncated by CodeMelt due to size safety thresholds]";
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
content = rawText;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
type = "binary";
|
|
101
|
+
content = "[Unable to read as text]";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Detect importance
|
|
105
|
+
const importance = detectImportance(file.path, file.name);
|
|
106
|
+
return {
|
|
107
|
+
name: file.name,
|
|
108
|
+
path: file.path,
|
|
109
|
+
extension,
|
|
110
|
+
size,
|
|
111
|
+
content,
|
|
112
|
+
included: true,
|
|
113
|
+
type,
|
|
114
|
+
importance,
|
|
115
|
+
};
|
|
116
|
+
}));
|
|
117
|
+
scannedFiles.push(...processedBatch);
|
|
118
|
+
}
|
|
119
|
+
const totalScannedBytes = files.reduce((acc, f) => acc + f.size, 0);
|
|
120
|
+
return {
|
|
121
|
+
scannedFiles,
|
|
122
|
+
ignoredCount,
|
|
123
|
+
ignoredBytes,
|
|
124
|
+
totalFiles: files.length,
|
|
125
|
+
totalBytes: totalScannedBytes,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ExtractedPatterns {
|
|
2
|
+
exports: string[];
|
|
3
|
+
imports: string[];
|
|
4
|
+
functions: string[];
|
|
5
|
+
routes: {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
}[];
|
|
9
|
+
keywords: Set<string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function extractPatterns(content: string, path: string, name: string): ExtractedPatterns;
|
|
12
|
+
//# sourceMappingURL=extractPatterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractPatterns.d.ts","sourceRoot":"","sources":["../../src/summarizer/extractPatterns.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAWD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAqF9F"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const KEYWORD_GROUPS = {
|
|
2
|
+
auth: ["auth", "login", "register", "signup", "signin", "jwt", "token", "password", "session", "cookie", "passport", "bcrypt", "oauth"],
|
|
3
|
+
db: ["db", "database", "query", "prisma", "drizzle", "mongo", "mongoose", "postgres", "sql", "schema", "model", "findmany", "insert", "select"],
|
|
4
|
+
state: ["state", "store", "dispatch", "reducer", "slice", "createslice", "selector", "atom", "zustand", "redux", "jotai"],
|
|
5
|
+
realtime: ["socket", "io", "ws", "websocket", "pusher", "realtime", "subscribe", "broadcast", "emit", "on("],
|
|
6
|
+
billing: ["stripe", "checkout", "payment", "charge", "cart", "order", "billing", "invoice", "sub"],
|
|
7
|
+
upload: ["upload", "multer", "s3", "storage", "file", "cloudinary", "aws-sdk"],
|
|
8
|
+
};
|
|
9
|
+
export function extractPatterns(content, path, name) {
|
|
10
|
+
const exports = [];
|
|
11
|
+
const imports = [];
|
|
12
|
+
const functions = [];
|
|
13
|
+
const routes = [];
|
|
14
|
+
const keywords = new Set();
|
|
15
|
+
// Skip binary, oversized or empty contents
|
|
16
|
+
if (!content || content.startsWith("[") && content.endsWith("]")) {
|
|
17
|
+
return { exports, imports, functions, routes, keywords };
|
|
18
|
+
}
|
|
19
|
+
const lowerContent = content.toLowerCase();
|
|
20
|
+
// 1. Keyword Scan
|
|
21
|
+
for (const [group, words] of Object.entries(KEYWORD_GROUPS)) {
|
|
22
|
+
for (const word of words) {
|
|
23
|
+
if (lowerContent.includes(word)) {
|
|
24
|
+
keywords.add(group);
|
|
25
|
+
break; // Once group is identified, move to next
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// 2. Regex Scan (Process line-by-line or moderate size chunks to protect browser CPU)
|
|
30
|
+
const lines = content.split("\n");
|
|
31
|
+
const exportRegex = /\bexport\s+(?:const|let|var|function|async\s+function|class|type|interface)\s+([a-zA-Z0-9_]+)/;
|
|
32
|
+
const importRegex = /\bimport\s+.*\s+from\s+['"]([^'"]+)['"]/;
|
|
33
|
+
const functionRegex = /\bfunction\s+([a-zA-Z0-9_]+)|\bconst\s+([a-zA-Z0-9_]+)\s*=\s*(?:async\s*)?\((?:[^)]*)\)\s*=>/;
|
|
34
|
+
const expressRouteRegex = /\b(?:router|app)\.(get|post|put|delete|patch)\(['"]([^'"]+)['"]/;
|
|
35
|
+
const nextApiRegex = /fetch\(['"]([^'"]+)['"]|axios\.(get|post|put|delete)\(['"]([^'"]+)['"]/;
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
// Limit processing to reasonable length lines
|
|
38
|
+
if (line.length > 500)
|
|
39
|
+
continue;
|
|
40
|
+
// A. Exports
|
|
41
|
+
const exportMatch = line.match(exportRegex);
|
|
42
|
+
if (exportMatch && exportMatch[1]) {
|
|
43
|
+
exports.push(exportMatch[1]);
|
|
44
|
+
}
|
|
45
|
+
// B. Imports
|
|
46
|
+
const importMatch = line.match(importRegex);
|
|
47
|
+
if (importMatch && importMatch[1]) {
|
|
48
|
+
imports.push(importMatch[1]);
|
|
49
|
+
}
|
|
50
|
+
// C. Local functions
|
|
51
|
+
const fnMatch = line.match(functionRegex);
|
|
52
|
+
const fnName = fnMatch ? (fnMatch[1] || fnMatch[2]) : null;
|
|
53
|
+
if (fnName && fnName !== "const" && fnName !== "let") {
|
|
54
|
+
functions.push(fnName);
|
|
55
|
+
}
|
|
56
|
+
// D. Express routes
|
|
57
|
+
const routeMatch = line.match(expressRouteRegex);
|
|
58
|
+
if (routeMatch && routeMatch[1] && routeMatch[2]) {
|
|
59
|
+
routes.push({
|
|
60
|
+
method: routeMatch[1].toUpperCase(),
|
|
61
|
+
path: routeMatch[2],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// E. Fetch/Axios network references (flow intelligence helper)
|
|
65
|
+
const netMatch = line.match(nextApiRegex);
|
|
66
|
+
if (netMatch) {
|
|
67
|
+
const endpoint = netMatch[1] || netMatch[2];
|
|
68
|
+
if (endpoint && endpoint.startsWith("/")) {
|
|
69
|
+
routes.push({
|
|
70
|
+
method: line.includes("post") || line.includes("POST") ? "POST" : "GET",
|
|
71
|
+
path: endpoint,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
exports: Array.from(new Set(exports)).slice(0, 10), // cap at 10 to keep it dense
|
|
78
|
+
imports: Array.from(new Set(imports)).slice(0, 10),
|
|
79
|
+
functions: Array.from(new Set(functions)).slice(0, 15),
|
|
80
|
+
routes,
|
|
81
|
+
keywords,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FileSemanticSummary, RouteDetail, RequestFlow } from "./types.js";
|
|
2
|
+
export declare function detectFlowsAndRoutes(fileSummaries: Record<string, FileSemanticSummary>): {
|
|
3
|
+
routes: RouteDetail[];
|
|
4
|
+
flows: RequestFlow[];
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=flowDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flowDetector.d.ts","sourceRoot":"","sources":["../../src/summarizer/flowDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3E,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GACjD;IAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,CAgCjD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function detectFlowsAndRoutes(fileSummaries) {
|
|
2
|
+
const routes = [];
|
|
3
|
+
const flows = [];
|
|
4
|
+
const fileKeys = Object.keys(fileSummaries);
|
|
5
|
+
// 1. Gather all registered routes across files
|
|
6
|
+
for (const [filePath, summary] of Object.entries(fileSummaries)) {
|
|
7
|
+
if (summary.routes && summary.routes.length > 0) {
|
|
8
|
+
for (const r of summary.routes) {
|
|
9
|
+
const parts = r.split(" ");
|
|
10
|
+
if (parts.length > 1) {
|
|
11
|
+
const method = parts[0].toUpperCase();
|
|
12
|
+
const path = parts[1];
|
|
13
|
+
// Avoid duplicate routes registration
|
|
14
|
+
const exists = routes.some(existing => existing.path === path && existing.method === method);
|
|
15
|
+
if (!exists) {
|
|
16
|
+
routes.push({
|
|
17
|
+
path,
|
|
18
|
+
method,
|
|
19
|
+
handlerFile: filePath,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// 2. Speculative pipelines are completely disabled to prevent hallucinations.
|
|
27
|
+
// We strictly return an empty array for flows.
|
|
28
|
+
return { routes, flows: [] };
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ScannedFile } from "@codemelt/shared";
|
|
2
|
+
import { SemanticRepositoryAnalysis } from "./types.js";
|
|
3
|
+
export declare function analyzeSemanticRepository(files: ScannedFile[]): SemanticRepositoryAnalysis;
|
|
4
|
+
export * from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/summarizer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,0BAA0B,EAAuB,MAAM,YAAY,CAAC;AAK7E,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,WAAW,EAAE,GACnB,0BAA0B,CA2B5B;AAED,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { summarizeFile } from "./summarizeFile.js";
|
|
2
|
+
import { summarizeDirectory } from "./summarizeDirectory.js";
|
|
3
|
+
import { detectFlowsAndRoutes } from "./flowDetector.js";
|
|
4
|
+
export function analyzeSemanticRepository(files) {
|
|
5
|
+
const fileSummaries = {};
|
|
6
|
+
// 1. Core file summaries loop
|
|
7
|
+
for (const file of files) {
|
|
8
|
+
const summary = summarizeFile(file);
|
|
9
|
+
fileSummaries[file.path] = summary;
|
|
10
|
+
}
|
|
11
|
+
// 2. Aggregate directory summaries
|
|
12
|
+
const directorySummaries = summarizeDirectory(fileSummaries);
|
|
13
|
+
// 3. Extract system entrypoints
|
|
14
|
+
const entrypoints = Object.values(fileSummaries).filter((summary) => summary.isEntrypoint);
|
|
15
|
+
// 4. Trace HTTP routes & flow tracks
|
|
16
|
+
const { routes, flows } = detectFlowsAndRoutes(fileSummaries);
|
|
17
|
+
return {
|
|
18
|
+
fileSummaries,
|
|
19
|
+
directorySummaries,
|
|
20
|
+
entrypoints,
|
|
21
|
+
routes,
|
|
22
|
+
flows,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ExtractedPatterns } from "./extractPatterns.js";
|
|
2
|
+
import { EntrypointType } from "./types.js";
|
|
3
|
+
interface InferredPurpose {
|
|
4
|
+
summary: string;
|
|
5
|
+
isEntrypoint: boolean;
|
|
6
|
+
entrypointType?: EntrypointType;
|
|
7
|
+
}
|
|
8
|
+
export declare function inferPurpose(path: string, name: string, patterns: ExtractedPatterns): InferredPurpose;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=inferPurpose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inferPurpose.d.ts","sourceRoot":"","sources":["../../src/summarizer/inferPurpose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,eAAe,CA0KrG"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export function inferPurpose(path, name, patterns) {
|
|
2
|
+
const lowerPath = path.toLowerCase();
|
|
3
|
+
const lowerName = name.toLowerCase();
|
|
4
|
+
const keywords = patterns.keywords;
|
|
5
|
+
// --- 1. ENTRYPOINT DETECTION ---
|
|
6
|
+
const isServerEntry = lowerName === "server.js" || lowerName === "server.ts" || lowerPath === "src/main.ts" || lowerPath === "src/index.ts" || lowerPath === "index.js";
|
|
7
|
+
const isFrontendBoot = lowerPath === "src/main.tsx" || lowerPath === "src/index.tsx" || lowerPath === "src/main.jsx" || lowerPath === "src/index.jsx";
|
|
8
|
+
const isAppShell = lowerName === "app.tsx" || lowerName === "app.jsx" || lowerName === "app.vue" || lowerName === "app.svelte";
|
|
9
|
+
const isLayoutRoot = lowerName === "layout.tsx" || lowerName === "layout.jsx" || lowerName === "layout.html";
|
|
10
|
+
const isRouterRoot = lowerName === "routes.ts" || lowerName === "routes.tsx" || lowerName === "routes.js" || lowerName === "page.tsx" && !lowerPath.includes("api");
|
|
11
|
+
if (isServerEntry) {
|
|
12
|
+
return {
|
|
13
|
+
summary: "Server entrypoint file.",
|
|
14
|
+
isEntrypoint: true,
|
|
15
|
+
entrypointType: "server",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (isFrontendBoot) {
|
|
19
|
+
return {
|
|
20
|
+
summary: "Frontend client entrypoint.",
|
|
21
|
+
isEntrypoint: true,
|
|
22
|
+
entrypointType: "frontend",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (isAppShell) {
|
|
26
|
+
return {
|
|
27
|
+
summary: "Main application router and root layout wrapper.",
|
|
28
|
+
isEntrypoint: true,
|
|
29
|
+
entrypointType: "app-shell",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (isLayoutRoot) {
|
|
33
|
+
return {
|
|
34
|
+
summary: "Root layout component structure.",
|
|
35
|
+
isEntrypoint: true,
|
|
36
|
+
entrypointType: "layout-root",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (isRouterRoot) {
|
|
40
|
+
return {
|
|
41
|
+
summary: "Router page component.",
|
|
42
|
+
isEntrypoint: true,
|
|
43
|
+
entrypointType: "router-root",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// --- 2. CONFIGURATION BOUNDARIES ---
|
|
47
|
+
const isConfig = lowerName.includes("config") || lowerName.startsWith(".") || lowerName.endsWith(".json");
|
|
48
|
+
if (isConfig) {
|
|
49
|
+
if (lowerName === "package.json")
|
|
50
|
+
return { summary: "Project dependency and build script definitions.", isEntrypoint: false };
|
|
51
|
+
if (lowerName === "tsconfig.json")
|
|
52
|
+
return { summary: "TypeScript compiler settings.", isEntrypoint: false };
|
|
53
|
+
if (lowerName === "tailwind.config.js" || lowerName === "tailwind.config.ts")
|
|
54
|
+
return { summary: "Tailwind CSS configuration rules.", isEntrypoint: false };
|
|
55
|
+
if (lowerName === "schema.prisma")
|
|
56
|
+
return { summary: "Prisma database schema and models.", isEntrypoint: false };
|
|
57
|
+
if (lowerName === ".gitignore")
|
|
58
|
+
return { summary: "Git ignore rules.", isEntrypoint: false };
|
|
59
|
+
return {
|
|
60
|
+
summary: "Project configuration file.",
|
|
61
|
+
isEntrypoint: false,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// --- 3. CONTROLLERS ---
|
|
65
|
+
const isController = lowerName.endsWith("controller.ts") || lowerName.endsWith("controller.js") || lowerPath.includes("/controllers/") || lowerPath.includes("/controller/");
|
|
66
|
+
if (isController) {
|
|
67
|
+
if (keywords.has("auth")) {
|
|
68
|
+
return { summary: "Controller handling user credentials and session management.", isEntrypoint: false };
|
|
69
|
+
}
|
|
70
|
+
if (keywords.has("db")) {
|
|
71
|
+
return { summary: "Controller handling database requests.", isEntrypoint: false };
|
|
72
|
+
}
|
|
73
|
+
if (keywords.has("billing")) {
|
|
74
|
+
return { summary: "Controller handling billing and webhook operations.", isEntrypoint: false };
|
|
75
|
+
}
|
|
76
|
+
if (keywords.has("upload")) {
|
|
77
|
+
return { summary: "Controller handling file upload operations.", isEntrypoint: false };
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
summary: "Controller mapping incoming requests to backend logic.",
|
|
81
|
+
isEntrypoint: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// --- 4. SERVICES ---
|
|
85
|
+
const isService = lowerName.endsWith("service.ts") || lowerName.endsWith("service.js") || lowerPath.includes("/services/") || lowerPath.includes("/service/");
|
|
86
|
+
if (isService) {
|
|
87
|
+
if (keywords.has("auth")) {
|
|
88
|
+
return { summary: "Service handling user authentication workflows.", isEntrypoint: false };
|
|
89
|
+
}
|
|
90
|
+
if (keywords.has("db")) {
|
|
91
|
+
return { summary: "Service handling database query logic.", isEntrypoint: false };
|
|
92
|
+
}
|
|
93
|
+
if (keywords.has("billing")) {
|
|
94
|
+
return { summary: "Service handling checkout processes.", isEntrypoint: false };
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
summary: "Service layer encapsulating backend operations.",
|
|
98
|
+
isEntrypoint: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// --- 5. MIDDLEWARE ---
|
|
102
|
+
const isMiddleware = lowerName.includes("middleware") || lowerPath.includes("/middleware/") || lowerPath.includes("/middlewares/");
|
|
103
|
+
if (isMiddleware) {
|
|
104
|
+
if (keywords.has("auth")) {
|
|
105
|
+
return { summary: "Middleware handling user token verification.", isEntrypoint: false };
|
|
106
|
+
}
|
|
107
|
+
if (keywords.has("upload")) {
|
|
108
|
+
return { summary: "Middleware handling file size and type filters.", isEntrypoint: false };
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
summary: "Middleware handling request routing filters.",
|
|
112
|
+
isEntrypoint: false,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// --- 6. REACT/UI VIEWS ---
|
|
116
|
+
const isUI = lowerName.endsWith(".tsx") || lowerName.endsWith(".jsx") || lowerName.endsWith(".vue") || lowerName.endsWith(".svelte") || lowerPath.includes("/components/") || lowerPath.includes("/views/");
|
|
117
|
+
if (isUI) {
|
|
118
|
+
if (lowerName.includes("navbar") || lowerName.includes("sidebar") || lowerName.includes("header")) {
|
|
119
|
+
return { summary: "Navigation component for headers or sidebars.", isEntrypoint: false };
|
|
120
|
+
}
|
|
121
|
+
if (lowerName.includes("modal") || lowerName.includes("dialog") || lowerName.includes("drawer")) {
|
|
122
|
+
return { summary: "Interactive modal dialog component.", isEntrypoint: false };
|
|
123
|
+
}
|
|
124
|
+
if (lowerName.includes("button") || lowerName.includes("input") || lowerName.includes("badge") || lowerName.includes("select")) {
|
|
125
|
+
return { summary: "Reusable form controls and UI elements.", isEntrypoint: false };
|
|
126
|
+
}
|
|
127
|
+
if (keywords.has("state")) {
|
|
128
|
+
return { summary: "Interactive component bound to state management.", isEntrypoint: false };
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
summary: "User interface view component.",
|
|
132
|
+
isEntrypoint: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// --- 7. ROUTE HANDLERS ---
|
|
136
|
+
const isRouteFile = lowerName.includes("route") || lowerPath.includes("/routes/") || lowerPath.includes("/api/");
|
|
137
|
+
if (isRouteFile) {
|
|
138
|
+
return {
|
|
139
|
+
summary: "API route endpoint handler configuration.",
|
|
140
|
+
isEntrypoint: false,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// --- 8. UTILITIES ---
|
|
144
|
+
const isUtil = lowerName.includes("util") || lowerName.includes("helper") || lowerPath.includes("/utils/") || lowerPath.includes("/helpers/") || lowerPath.includes("/lib/");
|
|
145
|
+
if (isUtil) {
|
|
146
|
+
if (keywords.has("auth")) {
|
|
147
|
+
return { summary: "Utility functions handling credentials.", isEntrypoint: false };
|
|
148
|
+
}
|
|
149
|
+
if (keywords.has("db")) {
|
|
150
|
+
return { summary: "Utility functions handling database pooling.", isEntrypoint: false };
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
summary: "General utility functions.",
|
|
154
|
+
isEntrypoint: false,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// --- 9. DEFAULT FALLBACK ---
|
|
158
|
+
return {
|
|
159
|
+
summary: "Logical repository code file.",
|
|
160
|
+
isEntrypoint: false,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeDirectory.d.ts","sourceRoot":"","sources":["../../src/summarizer/summarizeDirectory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3E,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GACjD,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAsF1C"}
|