50c 4.0.0 → 4.1.1
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/README.md +27 -175
- package/bin/50c.js +62 -2224
- package/package.json +17 -31
- package/LICENSE +0 -31
- package/lib/caz-context.js +0 -461
- package/lib/file-memory.js +0 -301
- package/lib/invent-ui.js +0 -717
- package/lib/mcp-tv.js +0 -1015
- package/lib/pre-publish.js +0 -831
- package/lib/subagent.js +0 -369
- package/lib/team.js +0 -691
- package/lib/tools-registry.js +0 -184
package/lib/file-memory.js
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FILE MEMORY - Local file indexing for LLM assistance
|
|
3
|
-
* No dependencies, pure Node.js
|
|
4
|
-
*
|
|
5
|
-
* Helps LLM not "phone it in" on 4000-8000 line files by providing:
|
|
6
|
-
* - Function/class locations
|
|
7
|
-
* - Line range retrieval
|
|
8
|
-
* - Keyword search
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const os = require('os');
|
|
14
|
-
|
|
15
|
-
const INDEX_DIR = path.join(os.homedir(), '.50c', 'file_index');
|
|
16
|
-
|
|
17
|
-
// Ensure index directory exists
|
|
18
|
-
if (!fs.existsSync(INDEX_DIR)) {
|
|
19
|
-
fs.mkdirSync(INDEX_DIR, { recursive: true });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getIndexPath(filepath) {
|
|
23
|
-
const hash = require('crypto').createHash('md5')
|
|
24
|
-
.update(filepath).digest('hex').slice(0, 12);
|
|
25
|
-
return path.join(INDEX_DIR, `${hash}.json`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function extractSymbols(lines) {
|
|
29
|
-
const symbols = [];
|
|
30
|
-
lines.forEach((line, i) => {
|
|
31
|
-
const trimmed = line.trim();
|
|
32
|
-
const lineNum = i + 1;
|
|
33
|
-
|
|
34
|
-
// Python
|
|
35
|
-
if (trimmed.startsWith('def ')) {
|
|
36
|
-
const name = trimmed.slice(4).split('(')[0].trim();
|
|
37
|
-
symbols.push({ name, type: 'function', line: lineNum, sig: trimmed.split(':')[0] });
|
|
38
|
-
}
|
|
39
|
-
else if (trimmed.startsWith('async def ')) {
|
|
40
|
-
const name = trimmed.slice(10).split('(')[0].trim();
|
|
41
|
-
symbols.push({ name, type: 'async_function', line: lineNum, sig: trimmed.split(':')[0] });
|
|
42
|
-
}
|
|
43
|
-
else if (trimmed.startsWith('class ')) {
|
|
44
|
-
const name = trimmed.slice(6).split('(')[0].split(':')[0].trim();
|
|
45
|
-
symbols.push({ name, type: 'class', line: lineNum, sig: trimmed.split(':')[0] });
|
|
46
|
-
}
|
|
47
|
-
// JavaScript/TypeScript
|
|
48
|
-
else if (trimmed.match(/^(export\s+)?(async\s+)?function\s+\w+/)) {
|
|
49
|
-
const match = trimmed.match(/function\s+(\w+)/);
|
|
50
|
-
if (match) symbols.push({ name: match[1], type: 'function', line: lineNum, sig: trimmed.split('{')[0].trim() });
|
|
51
|
-
}
|
|
52
|
-
else if (trimmed.match(/^(export\s+)?class\s+\w+/)) {
|
|
53
|
-
const match = trimmed.match(/class\s+(\w+)/);
|
|
54
|
-
if (match) symbols.push({ name: match[1], type: 'class', line: lineNum, sig: trimmed.split('{')[0].trim() });
|
|
55
|
-
}
|
|
56
|
-
else if (trimmed.match(/^(const|let|var)\s+\w+\s*=\s*(async\s+)?\(/)) {
|
|
57
|
-
const match = trimmed.match(/^(const|let|var)\s+(\w+)/);
|
|
58
|
-
if (match) symbols.push({ name: match[2], type: 'arrow_function', line: lineNum, sig: trimmed.slice(0, 60) });
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
return symbols;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function indexFile(filepath) {
|
|
65
|
-
filepath = path.resolve(filepath);
|
|
66
|
-
|
|
67
|
-
if (!fs.existsSync(filepath)) {
|
|
68
|
-
return { error: `File not found: ${filepath}` };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const content = fs.readFileSync(filepath, 'utf8');
|
|
72
|
-
const lines = content.split('\n');
|
|
73
|
-
const symbols = extractSymbols(lines);
|
|
74
|
-
|
|
75
|
-
// Create chunks (100 lines each)
|
|
76
|
-
const chunkSize = 100;
|
|
77
|
-
const chunks = [];
|
|
78
|
-
for (let i = 0; i < lines.length; i += chunkSize) {
|
|
79
|
-
const chunkLines = lines.slice(i, i + chunkSize);
|
|
80
|
-
const chunkSymbols = symbols.filter(s => s.line > i && s.line <= i + chunkSize);
|
|
81
|
-
chunks.push({
|
|
82
|
-
start: i + 1,
|
|
83
|
-
end: Math.min(i + chunkSize, lines.length),
|
|
84
|
-
preview: chunkLines.slice(0, 3).join(' ').slice(0, 100),
|
|
85
|
-
functions: chunkSymbols.filter(s => s.type !== 'class').map(s => s.name),
|
|
86
|
-
classes: chunkSymbols.filter(s => s.type === 'class').map(s => s.name)
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const index = {
|
|
91
|
-
filepath,
|
|
92
|
-
totalLines: lines.length,
|
|
93
|
-
indexed: new Date().toISOString(),
|
|
94
|
-
symbols,
|
|
95
|
-
chunks
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
fs.writeFileSync(getIndexPath(filepath), JSON.stringify(index, null, 2));
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
status: 'indexed',
|
|
102
|
-
filepath,
|
|
103
|
-
lines: lines.length,
|
|
104
|
-
functions: symbols.filter(s => s.type !== 'class').length,
|
|
105
|
-
classes: symbols.filter(s => s.type === 'class').length
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function findSymbol(name, filepath = null) {
|
|
110
|
-
const files = filepath ? [getIndexPath(path.resolve(filepath))] :
|
|
111
|
-
fs.readdirSync(INDEX_DIR).map(f => path.join(INDEX_DIR, f));
|
|
112
|
-
|
|
113
|
-
const results = [];
|
|
114
|
-
for (const indexFile of files) {
|
|
115
|
-
if (!fs.existsSync(indexFile)) continue;
|
|
116
|
-
try {
|
|
117
|
-
const index = JSON.parse(fs.readFileSync(indexFile, 'utf8'));
|
|
118
|
-
const matches = index.symbols.filter(s =>
|
|
119
|
-
s.name.toLowerCase().includes(name.toLowerCase())
|
|
120
|
-
);
|
|
121
|
-
matches.forEach(m => results.push({ ...m, filepath: index.filepath }));
|
|
122
|
-
} catch (e) {}
|
|
123
|
-
}
|
|
124
|
-
return results;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function getLines(filepath, start, end) {
|
|
128
|
-
filepath = path.resolve(filepath);
|
|
129
|
-
if (!fs.existsSync(filepath)) {
|
|
130
|
-
return { error: `File not found: ${filepath}` };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const lines = fs.readFileSync(filepath, 'utf8').split('\n');
|
|
134
|
-
const result = [];
|
|
135
|
-
for (let i = start - 1; i < Math.min(end, lines.length); i++) {
|
|
136
|
-
result.push(`${(i + 1).toString().padStart(5)}| ${lines[i]}`);
|
|
137
|
-
}
|
|
138
|
-
return result.join('\n');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function searchFile(filepath, query) {
|
|
142
|
-
filepath = path.resolve(filepath);
|
|
143
|
-
const indexPath = getIndexPath(filepath);
|
|
144
|
-
|
|
145
|
-
if (!fs.existsSync(indexPath)) {
|
|
146
|
-
return { error: `File not indexed. Run: file_index("${filepath}")` };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
150
|
-
const queryLower = query.toLowerCase();
|
|
151
|
-
|
|
152
|
-
// Search symbols first
|
|
153
|
-
const symbolMatches = index.symbols.filter(s =>
|
|
154
|
-
s.name.toLowerCase().includes(queryLower) ||
|
|
155
|
-
s.sig.toLowerCase().includes(queryLower)
|
|
156
|
-
).map(s => ({
|
|
157
|
-
type: 'symbol',
|
|
158
|
-
name: s.name,
|
|
159
|
-
line: s.line,
|
|
160
|
-
sig: s.sig
|
|
161
|
-
}));
|
|
162
|
-
|
|
163
|
-
// Search chunks
|
|
164
|
-
const content = fs.readFileSync(filepath, 'utf8');
|
|
165
|
-
const lines = content.split('\n');
|
|
166
|
-
const lineMatches = [];
|
|
167
|
-
|
|
168
|
-
lines.forEach((line, i) => {
|
|
169
|
-
if (line.toLowerCase().includes(queryLower)) {
|
|
170
|
-
lineMatches.push({
|
|
171
|
-
type: 'line',
|
|
172
|
-
line: i + 1,
|
|
173
|
-
content: line.trim().slice(0, 100)
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
symbols: symbolMatches.slice(0, 10),
|
|
180
|
-
lines: lineMatches.slice(0, 20)
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function fileSummary(filepath) {
|
|
185
|
-
filepath = path.resolve(filepath);
|
|
186
|
-
const indexPath = getIndexPath(filepath);
|
|
187
|
-
|
|
188
|
-
if (!fs.existsSync(indexPath)) {
|
|
189
|
-
return { error: `File not indexed. Run: file_index("${filepath}")` };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
filepath: index.filepath,
|
|
196
|
-
totalLines: index.totalLines,
|
|
197
|
-
indexed: index.indexed,
|
|
198
|
-
functions: index.symbols.filter(s => s.type !== 'class').length,
|
|
199
|
-
classes: index.symbols.filter(s => s.type === 'class').length,
|
|
200
|
-
topSymbols: index.symbols.slice(0, 30).map(s => ({
|
|
201
|
-
name: s.name,
|
|
202
|
-
type: s.type,
|
|
203
|
-
line: s.line
|
|
204
|
-
}))
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// MCP Tool definitions for local file operations
|
|
209
|
-
const FILE_TOOLS = {
|
|
210
|
-
file_index: {
|
|
211
|
-
description: "Index a large local file for fast symbol/line lookup. FREE.",
|
|
212
|
-
schema: {
|
|
213
|
-
type: "object",
|
|
214
|
-
properties: { filepath: { type: "string", description: "Path to file" } },
|
|
215
|
-
required: ["filepath"]
|
|
216
|
-
},
|
|
217
|
-
handler: (args) => indexFile(args.filepath)
|
|
218
|
-
},
|
|
219
|
-
file_find: {
|
|
220
|
-
description: "Find where a function/class is defined in indexed files. FREE.",
|
|
221
|
-
schema: {
|
|
222
|
-
type: "object",
|
|
223
|
-
properties: {
|
|
224
|
-
name: { type: "string", description: "Symbol name to find" },
|
|
225
|
-
filepath: { type: "string", description: "Optional: limit to one file" }
|
|
226
|
-
},
|
|
227
|
-
required: ["name"]
|
|
228
|
-
},
|
|
229
|
-
handler: (args) => findSymbol(args.name, args.filepath)
|
|
230
|
-
},
|
|
231
|
-
file_lines: {
|
|
232
|
-
description: "Get specific line range from a file with line numbers. FREE.",
|
|
233
|
-
schema: {
|
|
234
|
-
type: "object",
|
|
235
|
-
properties: {
|
|
236
|
-
filepath: { type: "string" },
|
|
237
|
-
start: { type: "number", description: "Start line" },
|
|
238
|
-
end: { type: "number", description: "End line" }
|
|
239
|
-
},
|
|
240
|
-
required: ["filepath", "start", "end"]
|
|
241
|
-
},
|
|
242
|
-
handler: (args) => getLines(args.filepath, args.start, args.end)
|
|
243
|
-
},
|
|
244
|
-
file_search: {
|
|
245
|
-
description: "Search for content in an indexed file. FREE.",
|
|
246
|
-
schema: {
|
|
247
|
-
type: "object",
|
|
248
|
-
properties: {
|
|
249
|
-
filepath: { type: "string" },
|
|
250
|
-
query: { type: "string", description: "Search query" }
|
|
251
|
-
},
|
|
252
|
-
required: ["filepath", "query"]
|
|
253
|
-
},
|
|
254
|
-
handler: (args) => searchFile(args.filepath, args.query)
|
|
255
|
-
},
|
|
256
|
-
file_summary: {
|
|
257
|
-
description: "Get summary of indexed file (functions, classes, line count). FREE.",
|
|
258
|
-
schema: {
|
|
259
|
-
type: "object",
|
|
260
|
-
properties: { filepath: { type: "string" } },
|
|
261
|
-
required: ["filepath"]
|
|
262
|
-
},
|
|
263
|
-
handler: (args) => fileSummary(args.filepath)
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
module.exports = {
|
|
268
|
-
FILE_TOOLS,
|
|
269
|
-
indexFile,
|
|
270
|
-
findSymbol,
|
|
271
|
-
getLines,
|
|
272
|
-
searchFile,
|
|
273
|
-
fileSummary
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// CLI test
|
|
277
|
-
if (require.main === module) {
|
|
278
|
-
const testFile = process.argv[2] || 'C:\\Users\\Administrator\\Desktop\\50c\\nj-deploy\\api-unified-v8-FINAL-ALL.py';
|
|
279
|
-
|
|
280
|
-
console.log('='.repeat(60));
|
|
281
|
-
console.log('FILE MEMORY - Node.js Test');
|
|
282
|
-
console.log('='.repeat(60));
|
|
283
|
-
|
|
284
|
-
console.log('\n[1] Indexing...');
|
|
285
|
-
console.log(indexFile(testFile));
|
|
286
|
-
|
|
287
|
-
console.log('\n[2] Finding fog_check...');
|
|
288
|
-
console.log(findSymbol('fog_check'));
|
|
289
|
-
|
|
290
|
-
console.log('\n[3] Lines 2385-2395...');
|
|
291
|
-
console.log(getLines(testFile, 2385, 2395));
|
|
292
|
-
|
|
293
|
-
console.log('\n[4] Search "beacon"...');
|
|
294
|
-
const search = searchFile(testFile, 'beacon');
|
|
295
|
-
console.log('Symbols:', search.symbols?.slice(0, 5));
|
|
296
|
-
console.log('Lines:', search.lines?.slice(0, 5));
|
|
297
|
-
|
|
298
|
-
console.log('\n[5] Summary...');
|
|
299
|
-
const sum = fileSummary(testFile);
|
|
300
|
-
console.log(`${sum.totalLines} lines, ${sum.functions} functions, ${sum.classes} classes`);
|
|
301
|
-
}
|