aibridge-context 1.5.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * fileSnapshot.js
5
+ * ---------------
6
+ * Maintains an in-memory snapshot of every tracked file's content.
7
+ * The watcher calls captureSnapshot(file) BEFORE a change is processed,
8
+ * so diffTexts() can compare old vs new.
9
+ *
10
+ * Also owns the "code_files" section of state: a catalogue of every
11
+ * tracked source file with its exports, imports, functions and line count.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const CODE_EXTENSIONS = new Set(['.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx', '.py', '.go', '.rs', '.java', '.rb', '.php', '.cs', '.swift']);
18
+ const MAX_READ_BYTES = 200_000; // 200 KB per file cap
19
+
20
+ // In-process store: absolute path → content string
21
+ const _snapshots = new Map();
22
+
23
+ // ─────────────────────────────────────────────────────────────────
24
+ // Snapshot capture / retrieval
25
+ // ─────────────────────────────────────────────────────────────────
26
+
27
+ function captureSnapshot(absolutePath) {
28
+ try {
29
+ const content = readFileSafe(absolutePath);
30
+ _snapshots.set(absolutePath, content);
31
+ return content;
32
+ } catch (_) {
33
+ _snapshots.set(absolutePath, '');
34
+ return '';
35
+ }
36
+ }
37
+
38
+ function getSnapshot(absolutePath) {
39
+ return _snapshots.has(absolutePath) ? _snapshots.get(absolutePath) : null;
40
+ }
41
+
42
+ function readCurrentContent(absolutePath) {
43
+ try {
44
+ return readFileSafe(absolutePath);
45
+ } catch (_) {
46
+ return '';
47
+ }
48
+ }
49
+
50
+ function readFileSafe(absolutePath) {
51
+ const stat = fs.statSync(absolutePath);
52
+ if (stat.size > MAX_READ_BYTES) {
53
+ // Read only the first MAX_READ_BYTES for large files
54
+ const buf = Buffer.alloc(MAX_READ_BYTES);
55
+ const fd = fs.openSync(absolutePath, 'r');
56
+ fs.readSync(fd, buf, 0, MAX_READ_BYTES, 0);
57
+ fs.closeSync(fd);
58
+ return buf.toString('utf8') + '\n[file truncated]';
59
+ }
60
+ return fs.readFileSync(absolutePath, 'utf8');
61
+ }
62
+
63
+ function deleteSnapshot(absolutePath) {
64
+ _snapshots.delete(absolutePath);
65
+ }
66
+
67
+ // ─────────────────────────────────────────────────────────────────
68
+ // Code file catalogue builder
69
+ // ─────────────────────────────────────────────────────────────────
70
+
71
+ /**
72
+ * For each tracked source file, extract:
73
+ * - line count
74
+ * - exported names (JS/TS)
75
+ * - imported modules
76
+ * - top-level function names
77
+ * - class names
78
+ *
79
+ * Returns an object keyed by relative file path.
80
+ */
81
+ function buildCodeFileCatalogue(projectRoot, fileList) {
82
+ const resolvedRoot = path.resolve(projectRoot);
83
+ const catalogue = {};
84
+
85
+ for (const relPath of (fileList || [])) {
86
+ const ext = path.extname(relPath).toLowerCase();
87
+ if (!CODE_EXTENSIONS.has(ext)) continue;
88
+
89
+ const absPath = path.join(resolvedRoot, relPath);
90
+ let content = '';
91
+ try { content = readCurrentContent(absPath); } catch (_) { continue; }
92
+
93
+ catalogue[relPath] = analyseSourceFile(content, ext);
94
+ }
95
+
96
+ return catalogue;
97
+ }
98
+
99
+ function analyseSourceFile(content, ext) {
100
+ const lines = content.split('\n');
101
+ const result = {
102
+ lines: lines.length,
103
+ functions: [],
104
+ classes: [],
105
+ exports: [],
106
+ imports: []
107
+ };
108
+
109
+ if (['.js', '.ts', '.mjs', '.cjs', '.jsx', '.tsx'].includes(ext)) {
110
+ analyseJs(content, result);
111
+ } else if (ext === '.py') {
112
+ analysePython(content, result);
113
+ }
114
+
115
+ return result;
116
+ }
117
+
118
+ function analyseJs(content, result) {
119
+ // Named functions
120
+ const fnPattern = /^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][A-Za-z0-9_$]*)/gm;
121
+ let m;
122
+ while ((m = fnPattern.exec(content)) !== null) result.functions.push(m[1]);
123
+
124
+ // Arrow / const functions
125
+ const arrowPattern = /^(?:export\s+)?(?:const|let)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*(?:async\s*)?\(/gm;
126
+ while ((m = arrowPattern.exec(content)) !== null) result.functions.push(m[1]);
127
+
128
+ // Classes
129
+ const classPattern = /^(?:export\s+)?class\s+([A-Za-z_$][A-Za-z0-9_$]*)/gm;
130
+ while ((m = classPattern.exec(content)) !== null) result.classes.push(m[1]);
131
+
132
+ // CommonJS exports
133
+ const cjsExportPattern = /module\.exports\s*=\s*\{([^}]+)\}/s;
134
+ const cjsMatch = cjsExportPattern.exec(content);
135
+ if (cjsMatch) {
136
+ const names = cjsMatch[1].match(/([A-Za-z_$][A-Za-z0-9_$]*)/g) || [];
137
+ result.exports.push(...names);
138
+ }
139
+
140
+ // ES named exports
141
+ const esExportPattern = /^export\s+(?:const|let|function|class|async)\s+([A-Za-z_$][A-Za-z0-9_$]*)/gm;
142
+ while ((m = esExportPattern.exec(content)) !== null) result.exports.push(m[1]);
143
+
144
+ // require() imports
145
+ const requirePattern = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
146
+ while ((m = requirePattern.exec(content)) !== null) result.imports.push(m[1]);
147
+
148
+ // ES import
149
+ const importPattern = /^import\s+.*\s+from\s+['"]([^'"]+)['"]/gm;
150
+ while ((m = importPattern.exec(content)) !== null) result.imports.push(m[1]);
151
+
152
+ // De-dup
153
+ result.functions = [...new Set(result.functions)].slice(0, 40);
154
+ result.classes = [...new Set(result.classes)].slice(0, 20);
155
+ result.exports = [...new Set(result.exports)].slice(0, 40);
156
+ result.imports = [...new Set(result.imports)].slice(0, 30);
157
+ }
158
+
159
+ function analysePython(content, result) {
160
+ const defPattern = /^def\s+([A-Za-z_][A-Za-z0-9_]*)/gm;
161
+ const classPattern = /^class\s+([A-Za-z_][A-Za-z0-9_]*)/gm;
162
+ const importPattern = /^(?:import|from)\s+([A-Za-z_][A-Za-z0-9_.]*)/gm;
163
+ let m;
164
+ while ((m = defPattern.exec(content)) !== null) result.functions.push(m[1]);
165
+ while ((m = classPattern.exec(content)) !== null) result.classes.push(m[1]);
166
+ while ((m = importPattern.exec(content)) !== null) result.imports.push(m[1]);
167
+ result.functions = [...new Set(result.functions)].slice(0, 40);
168
+ result.classes = [...new Set(result.classes)].slice(0, 20);
169
+ result.imports = [...new Set(result.imports)].slice(0, 30);
170
+ }
171
+
172
+ module.exports = {
173
+ captureSnapshot,
174
+ deleteSnapshot,
175
+ getSnapshot,
176
+ readCurrentContent,
177
+ buildCodeFileCatalogue
178
+ };