project-graph-mcp 1.2.4 → 1.5.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/AGENT_ROLE.md +105 -29
- package/AGENT_ROLE_MINIMAL.md +23 -8
- package/README.md +100 -14
- package/package.json +1 -1
- package/src/.project-graph-cache.json +1 -1
- package/src/ai-context.js +113 -0
- package/src/analysis-cache.js +155 -0
- package/src/cli-handlers.js +131 -0
- package/src/cli.js +14 -2
- package/src/compact.js +207 -0
- package/src/complexity.js +21 -7
- package/src/compress.js +319 -0
- package/src/ctx-to-jsdoc.js +514 -0
- package/src/custom-rules.js +1 -0
- package/src/db-analysis.js +194 -0
- package/src/doc-dialect.js +716 -0
- package/src/full-analysis.js +322 -11
- package/src/graph-builder.js +32 -2
- package/src/instructions.js +3 -105
- package/src/jsdoc-checker.js +351 -0
- package/src/jsdoc-generator.js +0 -11
- package/src/lang-sql.js +309 -0
- package/src/large-files.js +1 -0
- package/src/mcp-server.js +236 -1
- package/src/mode-config.js +127 -0
- package/src/outdated-patterns.js +1 -0
- package/src/parser.js +364 -34
- package/src/similar-functions.js +1 -0
- package/src/test-annotations.js +203 -181
- package/src/tool-defs.js +318 -2
- package/src/tools.js +1 -1
- package/src/type-checker.js +188 -0
- package/src/undocumented.js +11 -12
- package/src/workspace.js +1 -1
- package/vendor/terser.mjs +49 -0
package/src/large-files.js
CHANGED
|
@@ -54,6 +54,7 @@ function findJSFiles(dir, rootDir = dir) {
|
|
|
54
54
|
/**
|
|
55
55
|
* Analyze a single file
|
|
56
56
|
* @param {string} filePath
|
|
57
|
+
* @param {string} rootDir - Root directory for relative path calculation
|
|
57
58
|
* @returns {LargeFileItem}
|
|
58
59
|
*/
|
|
59
60
|
function analyzeFile(filePath, rootDir) {
|
package/src/mcp-server.js
CHANGED
|
@@ -21,10 +21,21 @@ import { getSimilarFunctions } from './similar-functions.js';
|
|
|
21
21
|
import { getComplexity } from './complexity.js';
|
|
22
22
|
import { getLargeFiles } from './large-files.js';
|
|
23
23
|
import { getOutdatedPatterns } from './outdated-patterns.js';
|
|
24
|
-
import { getFullAnalysis } from './full-analysis.js';
|
|
24
|
+
import { getFullAnalysis, getAnalysisSummaryOnly } from './full-analysis.js';
|
|
25
25
|
import { getCustomRules, setCustomRule, checkCustomRules } from './custom-rules.js';
|
|
26
26
|
import { getFrameworkReference } from './framework-references.js';
|
|
27
27
|
import { setRoots, resolvePath } from './workspace.js';
|
|
28
|
+
import { getDBSchema, getTableUsage, getDBDeadTables } from './db-analysis.js';
|
|
29
|
+
import { compressFile, editCompressed } from './compress.js';
|
|
30
|
+
import { getProjectDocs, generateContextFiles, checkStaleness } from './doc-dialect.js';
|
|
31
|
+
import { getGraph } from './tools.js';
|
|
32
|
+
import { parseProject, discoverSubProjects } from './parser.js';
|
|
33
|
+
import { getAiContext } from './ai-context.js';
|
|
34
|
+
import { checkJSDocConsistency } from './jsdoc-checker.js';
|
|
35
|
+
import { checkTypes } from './type-checker.js';
|
|
36
|
+
import { compactProject, expandProject } from './compact.js';
|
|
37
|
+
import { validateCtxContracts } from './ctx-to-jsdoc.js';
|
|
38
|
+
import { getConfig, setConfig, getModeDescription, getModeWorkflow } from './mode-config.js';
|
|
28
39
|
|
|
29
40
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
30
41
|
|
|
@@ -108,6 +119,110 @@ const TOOL_HANDLERS = {
|
|
|
108
119
|
framework: args.framework,
|
|
109
120
|
path: args.path ? resolvePath(args.path) : undefined,
|
|
110
121
|
}),
|
|
122
|
+
|
|
123
|
+
// Database Analysis
|
|
124
|
+
get_db_schema: (args) => getDBSchema(resolvePath(args.path)),
|
|
125
|
+
get_table_usage: (args) => getTableUsage(resolvePath(args.path), args.table),
|
|
126
|
+
get_db_dead_tables: (args) => getDBDeadTables(resolvePath(args.path)),
|
|
127
|
+
|
|
128
|
+
// AI Context
|
|
129
|
+
get_compressed_file: (args) => compressFile(resolvePath(args.path), {
|
|
130
|
+
beautify: args.beautify,
|
|
131
|
+
legend: args.legend,
|
|
132
|
+
}),
|
|
133
|
+
get_project_docs: async (args) => {
|
|
134
|
+
const projectPath = resolvePath(args.path);
|
|
135
|
+
const graph = await getGraph(projectPath);
|
|
136
|
+
const docs = getProjectDocs(graph, projectPath, { file: args.file });
|
|
137
|
+
// Lazy staleness check — wrapped in try-catch for projects with parse errors
|
|
138
|
+
try {
|
|
139
|
+
const parsed = await parseProject(projectPath);
|
|
140
|
+
const staleness = checkStaleness(projectPath, parsed);
|
|
141
|
+
return { docs, staleFiles: staleness.stale, freshCount: staleness.fresh };
|
|
142
|
+
} catch { return { docs }; }
|
|
143
|
+
},
|
|
144
|
+
generate_context_docs: async (args) => {
|
|
145
|
+
const projectPath = resolvePath(args.path);
|
|
146
|
+
const graph = await getGraph(projectPath);
|
|
147
|
+
const parsed = await parseProject(projectPath);
|
|
148
|
+
return generateContextFiles(graph, projectPath, parsed, {
|
|
149
|
+
overwrite: args.overwrite,
|
|
150
|
+
scope: args.scope,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
check_stale_docs: async (args) => {
|
|
154
|
+
const projectPath = resolvePath(args.path);
|
|
155
|
+
const parsed = await parseProject(projectPath);
|
|
156
|
+
return checkStaleness(projectPath, parsed);
|
|
157
|
+
},
|
|
158
|
+
get_ai_context: async (args) => {
|
|
159
|
+
const projectPath = resolvePath(args.path);
|
|
160
|
+
const result = await getAiContext(projectPath, {
|
|
161
|
+
includeFiles: args.includeFiles,
|
|
162
|
+
includeDocs: args.includeDocs,
|
|
163
|
+
includeSkeleton: args.includeSkeleton,
|
|
164
|
+
});
|
|
165
|
+
// Add staleness info
|
|
166
|
+
try {
|
|
167
|
+
const parsed = await parseProject(projectPath);
|
|
168
|
+
const staleness = checkStaleness(projectPath, parsed);
|
|
169
|
+
result.staleFiles = staleness.stale;
|
|
170
|
+
} catch { /* parse error — skip staleness */ }
|
|
171
|
+
return result;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// JSDoc Consistency
|
|
175
|
+
check_jsdoc_consistency: (args) => {
|
|
176
|
+
return checkJSDocConsistency(resolvePath(args.path));
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// Type Checker (optional tsc)
|
|
180
|
+
check_types: async (args) => {
|
|
181
|
+
return checkTypes(resolvePath(args.path), {
|
|
182
|
+
files: args.files,
|
|
183
|
+
maxDiagnostics: args.maxDiagnostics,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Monorepo & Performance
|
|
188
|
+
discover_sub_projects: (args) => {
|
|
189
|
+
return discoverSubProjects(resolvePath(args.path));
|
|
190
|
+
},
|
|
191
|
+
get_analysis_summary: (args) => {
|
|
192
|
+
return getAnalysisSummaryOnly(resolvePath(args.path));
|
|
193
|
+
},
|
|
194
|
+
compact_project: (args) => {
|
|
195
|
+
return compactProject(resolvePath(args.path), { dryRun: args.dryRun || false });
|
|
196
|
+
},
|
|
197
|
+
beautify_project: (args) => {
|
|
198
|
+
return expandProject(resolvePath(args.path), { dryRun: args.dryRun || false });
|
|
199
|
+
},
|
|
200
|
+
validate_ctx_contracts: (args) => {
|
|
201
|
+
return validateCtxContracts(resolvePath(args.path), { strict: args.strict || false });
|
|
202
|
+
},
|
|
203
|
+
edit_compressed: (args) => {
|
|
204
|
+
return editCompressed(resolvePath(args.path), args.symbol, args.code, {
|
|
205
|
+
beautify: args.beautify !== false,
|
|
206
|
+
dryRun: args.dryRun || false,
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
get_mode: (args) => {
|
|
210
|
+
const dir = resolvePath(args.path);
|
|
211
|
+
const config = getConfig(dir);
|
|
212
|
+
return {
|
|
213
|
+
...config,
|
|
214
|
+
description: getModeDescription(config.mode),
|
|
215
|
+
workflow: getModeWorkflow(config.mode),
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
set_mode: (args) => {
|
|
219
|
+
const dir = resolvePath(args.path);
|
|
220
|
+
const updates = { mode: args.mode };
|
|
221
|
+
if (args.beautify !== undefined) updates.beautify = args.beautify;
|
|
222
|
+
if (args.autoValidate !== undefined) updates.autoValidate = args.autoValidate;
|
|
223
|
+
if (args.stripJSDoc !== undefined) updates.stripJSDoc = args.stripJSDoc;
|
|
224
|
+
return setConfig(dir, updates);
|
|
225
|
+
},
|
|
111
226
|
};
|
|
112
227
|
|
|
113
228
|
/**
|
|
@@ -130,6 +245,10 @@ const RESPONSE_HINTS = {
|
|
|
130
245
|
hints.push('💡 Large class detected. Run get_complexity() to find refactoring targets.');
|
|
131
246
|
}
|
|
132
247
|
hints.push('💡 Use deps() to see what depends on this symbol.');
|
|
248
|
+
// Nudge: document if no .ctx exists
|
|
249
|
+
if (result.file) {
|
|
250
|
+
hints.push(`📝 No .ctx for ${result.file}? Run generate_context_docs({ scope: ["${result.file}"] }) to create documentation.`);
|
|
251
|
+
}
|
|
133
252
|
return hints;
|
|
134
253
|
},
|
|
135
254
|
|
|
@@ -177,6 +296,122 @@ const RESPONSE_HINTS = {
|
|
|
177
296
|
get_pending_tests: () => [
|
|
178
297
|
'💡 Use mark_test_passed(testId) or mark_test_failed(testId, reason) to track progress.',
|
|
179
298
|
],
|
|
299
|
+
|
|
300
|
+
get_db_schema: (result) => {
|
|
301
|
+
const hints = [];
|
|
302
|
+
if (result.totalTables > 0) {
|
|
303
|
+
hints.push(`💡 Found ${result.totalTables} tables. Use get_table_usage() to see which code reads/writes them.`);
|
|
304
|
+
} else {
|
|
305
|
+
hints.push('💡 No .sql schema files found. Add schema.sql or migrations/*.sql to your project.');
|
|
306
|
+
}
|
|
307
|
+
return hints;
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
get_table_usage: (result) => {
|
|
311
|
+
const hints = ['💡 Use get_db_dead_tables() to find tables defined in schema but never queried.'];
|
|
312
|
+
if (result.totalTables === 0) {
|
|
313
|
+
hints.push('💡 No SQL queries detected. This tool finds SQL in .query(), .execute(), sql`...` patterns.');
|
|
314
|
+
}
|
|
315
|
+
return hints;
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
get_db_dead_tables: () => [
|
|
319
|
+
'💡 Dead columns detection is best-effort — verify before removing.',
|
|
320
|
+
],
|
|
321
|
+
|
|
322
|
+
get_compressed_file: (result) => {
|
|
323
|
+
const hints = [`💡 Saved ${result.savings} tokens (${result.original} → ${result.compressed}).`];
|
|
324
|
+
hints.push('💡 Use get_ai_context() for full project boot: skeleton + docs + compressed files.');
|
|
325
|
+
if (result.file) {
|
|
326
|
+
hints.push(`📝 Working on ${result.file}? Run generate_context_docs({ scope: ["${result.file}"] }) to document it.`);
|
|
327
|
+
}
|
|
328
|
+
return hints;
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
get_project_docs: (result) => {
|
|
332
|
+
const hints = [
|
|
333
|
+
'💡 Enrich docs by editing .context/*.ctx files — they are git-tracked.',
|
|
334
|
+
'💡 Use generate_context_docs() to create initial .ctx stubs.',
|
|
335
|
+
];
|
|
336
|
+
if (result.staleFiles?.length > 0) {
|
|
337
|
+
hints.push(`⚠️ ${result.staleFiles.length} .ctx files are STALE: ${result.staleFiles.slice(0, 5).join(', ')}. Run generate_context_docs({ scope: ${JSON.stringify(result.staleFiles)}, overwrite: true }) to update (descriptions will be preserved).`);
|
|
338
|
+
}
|
|
339
|
+
return hints;
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
check_stale_docs: (result) => {
|
|
343
|
+
const hints = [];
|
|
344
|
+
if (result.stale?.length > 0) {
|
|
345
|
+
hints.push(`⚠️ ${result.stale.length} stale: ${result.stale.join(', ')}`);
|
|
346
|
+
hints.push(`💡 Run generate_context_docs({ scope: ${JSON.stringify(result.stale)}, overwrite: true }) — existing descriptions will be preserved.`);
|
|
347
|
+
} else {
|
|
348
|
+
hints.push('✅ All .ctx docs are up to date.');
|
|
349
|
+
}
|
|
350
|
+
if (result.unknown > 0) {
|
|
351
|
+
hints.push(`ℹ️ ${result.unknown} .ctx files without @sig header (pre-staleness format).`);
|
|
352
|
+
}
|
|
353
|
+
return hints;
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
generate_context_docs: (result) => {
|
|
357
|
+
const hints = [];
|
|
358
|
+
if (result.created?.length > 0) {
|
|
359
|
+
hints.push(`✅ Created ${result.created.length} .ctx files with @sig hashes.`);
|
|
360
|
+
}
|
|
361
|
+
if (result.skipped?.length > 0) {
|
|
362
|
+
hints.push(`ℹ️ Skipped ${result.skipped.length} existing files. Use overwrite=true to regenerate (descriptions are preserved via merge).`);
|
|
363
|
+
}
|
|
364
|
+
if (result.templates && Object.keys(result.templates).length > 0) {
|
|
365
|
+
hints.push(`📝 .ctx files have {DESCRIBE} markers. To enrich automatically:`);
|
|
366
|
+
hints.push(` delegate_task({ prompt: "Enrich .context/*.ctx files — replace {DESCRIBE} with compact descriptions", skill: "doc-enricher" })`);
|
|
367
|
+
hints.push(` Or enrich manually: read source files and replace {DESCRIBE} markers with pipe-separated descriptions (max 80 chars).`);
|
|
368
|
+
}
|
|
369
|
+
return hints;
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
get_ai_context: (result) => {
|
|
373
|
+
const hints = [`💡 Context loaded: ${result.totalTokens} tokens (${result.savings} savings vs ${result.vsOriginal} original).`];
|
|
374
|
+
hints.push('💡 Use expand() to drill into specific symbols. Use get_compressed_file() for additional files.');
|
|
375
|
+
if (result.staleFiles?.length > 0) {
|
|
376
|
+
hints.push(`⚠️ ${result.staleFiles.length} .ctx docs are stale. Run generate_context_docs({ scope: ${JSON.stringify(result.staleFiles)}, overwrite: true }) then delegate_task({ skill: "doc-enricher" }) to update.`);
|
|
377
|
+
}
|
|
378
|
+
return hints;
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
validate_ctx_contracts: (result) => {
|
|
382
|
+
const hints = [];
|
|
383
|
+
if (result.summary?.errors > 0) {
|
|
384
|
+
hints.push(`⚠️ ${result.summary.errors} contract violations found. Run generate_context_docs({ overwrite: true }) to regenerate .ctx files.`);
|
|
385
|
+
} else {
|
|
386
|
+
hints.push('✅ All .ctx contracts valid — documentation matches source.');
|
|
387
|
+
}
|
|
388
|
+
return hints;
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
edit_compressed: (result) => {
|
|
392
|
+
const hints = [];
|
|
393
|
+
if (result.success) {
|
|
394
|
+
hints.push(`✅ Symbol "${result.symbol}" replaced in ${result.file}.`);
|
|
395
|
+
hints.push('💡 Run invalidate_cache() to refresh the graph after editing.');
|
|
396
|
+
hints.push('💡 Run validate_ctx_contracts() to check if .ctx docs need updating.');
|
|
397
|
+
}
|
|
398
|
+
return hints;
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
get_mode: (result) => {
|
|
402
|
+
const hints = [`📋 Current mode: ${result.mode} — ${result.description}`];
|
|
403
|
+
if (result.mode === 2) {
|
|
404
|
+
hints.push('💡 Workflow: get_compressed_file() → read → edit_compressed() → write.');
|
|
405
|
+
}
|
|
406
|
+
return hints;
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
set_mode: (result) => {
|
|
410
|
+
if (result.saved) {
|
|
411
|
+
return [`✅ Mode set to ${result.config.mode}. Saved to ${result.path}.`];
|
|
412
|
+
}
|
|
413
|
+
return [];
|
|
414
|
+
},
|
|
180
415
|
};
|
|
181
416
|
|
|
182
417
|
/**
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact Code Mode Configuration
|
|
3
|
+
*
|
|
4
|
+
* Manages project-level mode selection for the compact code architecture.
|
|
5
|
+
* Reads/writes `.context/config.json` to configure how agents interact with code.
|
|
6
|
+
*
|
|
7
|
+
* Modes:
|
|
8
|
+
* 1 = Native Compact: code stored minified, agent edits directly
|
|
9
|
+
* 2 = Full Storage: code stored formatted, agent reads compressed view, edits via edit_compressed
|
|
10
|
+
* 3 = Future (IDE): compact storage with IDE virtual display (reserved)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
14
|
+
import { join, dirname } from 'path';
|
|
15
|
+
|
|
16
|
+
const CONFIG_FILE = '.context/config.json';
|
|
17
|
+
|
|
18
|
+
/** Default configuration */
|
|
19
|
+
const DEFAULTS = {
|
|
20
|
+
mode: 2,
|
|
21
|
+
beautify: true,
|
|
22
|
+
autoValidate: false,
|
|
23
|
+
stripJSDoc: false,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read project mode configuration from .context/config.json
|
|
28
|
+
* Returns defaults if file doesn't exist.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} projectDir - Project root directory
|
|
31
|
+
* @returns {{ mode: number, beautify: boolean, autoValidate: boolean, stripJSDoc: boolean }}
|
|
32
|
+
*/
|
|
33
|
+
export function getConfig(projectDir) {
|
|
34
|
+
const configPath = join(projectDir, CONFIG_FILE);
|
|
35
|
+
|
|
36
|
+
if (!existsSync(configPath)) {
|
|
37
|
+
return { ...DEFAULTS };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
return { ...DEFAULTS, ...parsed };
|
|
44
|
+
} catch {
|
|
45
|
+
return { ...DEFAULTS };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Write project mode configuration to .context/config.json
|
|
51
|
+
*
|
|
52
|
+
* @param {string} projectDir - Project root directory
|
|
53
|
+
* @param {Object} config - Configuration to save (merged with existing)
|
|
54
|
+
* @returns {{ saved: boolean, path: string, config: Object }}
|
|
55
|
+
*/
|
|
56
|
+
export function setConfig(projectDir, config) {
|
|
57
|
+
const configPath = join(projectDir, CONFIG_FILE);
|
|
58
|
+
const dir = dirname(configPath);
|
|
59
|
+
|
|
60
|
+
if (!existsSync(dir)) {
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Merge with existing
|
|
65
|
+
const existing = getConfig(projectDir);
|
|
66
|
+
const merged = { ...existing, ...config };
|
|
67
|
+
|
|
68
|
+
// Validate mode
|
|
69
|
+
if (![1, 2, 3].includes(merged.mode)) {
|
|
70
|
+
throw new Error(`Invalid mode: ${merged.mode}. Valid: 1 (compact), 2 (full), 3 (IDE)`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
saved: true,
|
|
77
|
+
path: configPath,
|
|
78
|
+
config: merged,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get human-readable description of current mode
|
|
84
|
+
* @param {number} mode
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
export function getModeDescription(mode) {
|
|
88
|
+
switch (mode) {
|
|
89
|
+
case 1: return 'Native Compact — code stored minified, agent edits directly';
|
|
90
|
+
case 2: return 'Full Storage — code stored formatted, agent uses get_compressed_file + edit_compressed';
|
|
91
|
+
case 3: return 'IDE Virtual — compact storage with IDE virtual display (future)';
|
|
92
|
+
default: return `Unknown mode: ${mode}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get recommended workflow for current mode
|
|
98
|
+
* @param {number} mode
|
|
99
|
+
* @returns {{ read: string, edit: string, docs: string, validate: string }}
|
|
100
|
+
*/
|
|
101
|
+
export function getModeWorkflow(mode) {
|
|
102
|
+
switch (mode) {
|
|
103
|
+
case 1:
|
|
104
|
+
return {
|
|
105
|
+
read: 'Read .js files directly (already compact)',
|
|
106
|
+
edit: 'Edit .js files directly',
|
|
107
|
+
docs: 'Read .ctx files for types and descriptions',
|
|
108
|
+
validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
|
|
109
|
+
};
|
|
110
|
+
case 2:
|
|
111
|
+
return {
|
|
112
|
+
read: 'Use get_compressed_file for token-efficient reading',
|
|
113
|
+
edit: 'Use edit_compressed(path, symbol, code) for AST-safe editing',
|
|
114
|
+
docs: 'Read .ctx files for types and descriptions',
|
|
115
|
+
validate: 'Run validate-ctx to check .ctx ↔ AST consistency',
|
|
116
|
+
};
|
|
117
|
+
case 3:
|
|
118
|
+
return {
|
|
119
|
+
read: 'IDE renders full view from compact storage',
|
|
120
|
+
edit: 'IDE handles bidirectional mapping',
|
|
121
|
+
docs: 'Managed by IDE plugin',
|
|
122
|
+
validate: 'Automatic via IDE integration',
|
|
123
|
+
};
|
|
124
|
+
default:
|
|
125
|
+
return { read: 'N/A', edit: 'N/A', docs: 'N/A', validate: 'N/A' };
|
|
126
|
+
}
|
|
127
|
+
}
|
package/src/outdated-patterns.js
CHANGED
|
@@ -152,6 +152,7 @@ function findJSFiles(dir, rootDir = dir) {
|
|
|
152
152
|
/**
|
|
153
153
|
* Analyze file for outdated patterns
|
|
154
154
|
* @param {string} filePath
|
|
155
|
+
* @param {string} rootDir - Root directory for relative path calculation
|
|
155
156
|
* @returns {PatternMatch[]}
|
|
156
157
|
*/
|
|
157
158
|
function analyzeFilePatterns(filePath, rootDir) {
|