projscan 0.5.0 → 0.6.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/README.md +55 -33
- package/dist/core/ast.d.ts +34 -0
- package/dist/core/ast.js +270 -0
- package/dist/core/ast.js.map +1 -0
- package/dist/core/codeGraph.d.ts +36 -0
- package/dist/core/codeGraph.js +167 -0
- package/dist/core/codeGraph.js.map +1 -0
- package/dist/core/importGraph.d.ts +5 -15
- package/dist/core/importGraph.js +12 -110
- package/dist/core/importGraph.js.map +1 -1
- package/dist/core/indexCache.d.ts +12 -0
- package/dist/core/indexCache.js +97 -0
- package/dist/core/indexCache.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +12 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tokenBudget.d.ts +25 -0
- package/dist/mcp/tokenBudget.js +89 -0
- package/dist/mcp/tokenBudget.js.map +1 -0
- package/dist/mcp/tools.js +170 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/utils/banner.js +5 -5
- package/dist/utils/banner.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FileEntry } from '../types.js';
|
|
2
|
+
import { toPackageName as graphToPackageName } from './codeGraph.js';
|
|
2
3
|
export interface ImportGraph {
|
|
3
4
|
/** file → set of import specifiers exactly as they appear in source */
|
|
4
5
|
byFile: Map<string, Set<string>>;
|
|
@@ -8,23 +9,12 @@ export interface ImportGraph {
|
|
|
8
9
|
scannedFiles: number;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
|
-
* Walk source files and build an import graph.
|
|
12
|
-
*
|
|
12
|
+
* Walk source files and build an import graph. Now backed by AST-based
|
|
13
|
+
* codeGraph — this function is retained for public API compatibility.
|
|
13
14
|
*/
|
|
14
15
|
export declare function buildImportGraph(rootPath: string, files: FileEntry[]): Promise<ImportGraph>;
|
|
15
|
-
/**
|
|
16
|
-
|
|
17
|
-
* Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
|
|
18
|
-
*
|
|
19
|
-
* 'react' -> 'react'
|
|
20
|
-
* 'react/jsx-runtime' -> 'react'
|
|
21
|
-
* '@types/node' -> '@types/node'
|
|
22
|
-
* '@scope/pkg/deep' -> '@scope/pkg'
|
|
23
|
-
* './local' -> null
|
|
24
|
-
* 'node:fs' -> null
|
|
25
|
-
* 'fs' -> null (node builtin)
|
|
26
|
-
*/
|
|
27
|
-
export declare function toPackageName(specifier: string): string | null;
|
|
16
|
+
/** Convert an import specifier to a bare package name. */
|
|
17
|
+
export declare const toPackageName: typeof graphToPackageName;
|
|
28
18
|
/** Check if a package is referenced by at least one file in the graph. */
|
|
29
19
|
export declare function isPackageUsed(graph: ImportGraph, pkg: string): boolean;
|
|
30
20
|
/** List files that import a given package. */
|
package/dist/core/importGraph.js
CHANGED
|
@@ -1,124 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { extractImports } from './fileInspector.js';
|
|
4
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
5
|
-
'.ts',
|
|
6
|
-
'.tsx',
|
|
7
|
-
'.js',
|
|
8
|
-
'.jsx',
|
|
9
|
-
'.mjs',
|
|
10
|
-
'.cjs',
|
|
11
|
-
'.mts',
|
|
12
|
-
'.cts',
|
|
13
|
-
]);
|
|
14
|
-
const MAX_FILE_SIZE = 1024 * 1024; // 1 MB — skip giant generated files
|
|
15
|
-
const NODE_BUILTINS = new Set([
|
|
16
|
-
'assert',
|
|
17
|
-
'async_hooks',
|
|
18
|
-
'buffer',
|
|
19
|
-
'child_process',
|
|
20
|
-
'cluster',
|
|
21
|
-
'console',
|
|
22
|
-
'constants',
|
|
23
|
-
'crypto',
|
|
24
|
-
'dgram',
|
|
25
|
-
'dns',
|
|
26
|
-
'domain',
|
|
27
|
-
'events',
|
|
28
|
-
'fs',
|
|
29
|
-
'fs/promises',
|
|
30
|
-
'http',
|
|
31
|
-
'http2',
|
|
32
|
-
'https',
|
|
33
|
-
'inspector',
|
|
34
|
-
'module',
|
|
35
|
-
'net',
|
|
36
|
-
'os',
|
|
37
|
-
'path',
|
|
38
|
-
'perf_hooks',
|
|
39
|
-
'process',
|
|
40
|
-
'punycode',
|
|
41
|
-
'querystring',
|
|
42
|
-
'readline',
|
|
43
|
-
'repl',
|
|
44
|
-
'stream',
|
|
45
|
-
'string_decoder',
|
|
46
|
-
'sys',
|
|
47
|
-
'timers',
|
|
48
|
-
'tls',
|
|
49
|
-
'trace_events',
|
|
50
|
-
'tty',
|
|
51
|
-
'url',
|
|
52
|
-
'util',
|
|
53
|
-
'v8',
|
|
54
|
-
'vm',
|
|
55
|
-
'wasi',
|
|
56
|
-
'worker_threads',
|
|
57
|
-
'zlib',
|
|
58
|
-
]);
|
|
1
|
+
import { buildCodeGraph, toPackageName as graphToPackageName } from './codeGraph.js';
|
|
59
2
|
/**
|
|
60
|
-
* Walk source files and build an import graph.
|
|
61
|
-
*
|
|
3
|
+
* Walk source files and build an import graph. Now backed by AST-based
|
|
4
|
+
* codeGraph — this function is retained for public API compatibility.
|
|
62
5
|
*/
|
|
63
6
|
export async function buildImportGraph(rootPath, files) {
|
|
7
|
+
const code = await buildCodeGraph(rootPath, files);
|
|
64
8
|
const byFile = new Map();
|
|
65
9
|
const externalPackages = new Set();
|
|
66
|
-
|
|
67
|
-
const sourceFiles = files.filter((f) => SOURCE_EXTENSIONS.has(f.extension) && f.sizeBytes <= MAX_FILE_SIZE);
|
|
68
|
-
await Promise.all(sourceFiles.map(async (file) => {
|
|
69
|
-
const abs = path.isAbsolute(file.absolutePath)
|
|
70
|
-
? file.absolutePath
|
|
71
|
-
: path.resolve(rootPath, file.relativePath);
|
|
72
|
-
let content;
|
|
73
|
-
try {
|
|
74
|
-
content = await fs.readFile(abs, 'utf-8');
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
scannedFiles++;
|
|
80
|
-
const imports = extractImports(content);
|
|
10
|
+
for (const [file, entry] of code.files) {
|
|
81
11
|
const specifiers = new Set();
|
|
82
|
-
for (const imp of imports) {
|
|
12
|
+
for (const imp of entry.imports) {
|
|
83
13
|
specifiers.add(imp.source);
|
|
84
|
-
const pkg =
|
|
85
|
-
if (pkg
|
|
14
|
+
const pkg = graphToPackageName(imp.source);
|
|
15
|
+
if (pkg)
|
|
86
16
|
externalPackages.add(pkg);
|
|
87
|
-
}
|
|
88
17
|
}
|
|
89
|
-
byFile.set(file
|
|
90
|
-
}));
|
|
91
|
-
return { byFile, externalPackages, scannedFiles };
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Convert an import specifier to a bare package name.
|
|
95
|
-
* Returns null for relative paths ("./", "../", "/"), node: builtins, and bare builtins.
|
|
96
|
-
*
|
|
97
|
-
* 'react' -> 'react'
|
|
98
|
-
* 'react/jsx-runtime' -> 'react'
|
|
99
|
-
* '@types/node' -> '@types/node'
|
|
100
|
-
* '@scope/pkg/deep' -> '@scope/pkg'
|
|
101
|
-
* './local' -> null
|
|
102
|
-
* 'node:fs' -> null
|
|
103
|
-
* 'fs' -> null (node builtin)
|
|
104
|
-
*/
|
|
105
|
-
export function toPackageName(specifier) {
|
|
106
|
-
if (!specifier)
|
|
107
|
-
return null;
|
|
108
|
-
if (specifier.startsWith('.') || specifier.startsWith('/'))
|
|
109
|
-
return null;
|
|
110
|
-
if (specifier.startsWith('node:'))
|
|
111
|
-
return null;
|
|
112
|
-
if (NODE_BUILTINS.has(specifier))
|
|
113
|
-
return null;
|
|
114
|
-
if (specifier.startsWith('@')) {
|
|
115
|
-
const segments = specifier.split('/');
|
|
116
|
-
if (segments.length < 2)
|
|
117
|
-
return null;
|
|
118
|
-
return `${segments[0]}/${segments[1]}`;
|
|
18
|
+
byFile.set(file, specifiers);
|
|
119
19
|
}
|
|
120
|
-
return
|
|
20
|
+
return { byFile, externalPackages, scannedFiles: code.scannedFiles };
|
|
121
21
|
}
|
|
22
|
+
/** Convert an import specifier to a bare package name. */
|
|
23
|
+
export const toPackageName = graphToPackageName;
|
|
122
24
|
/** Check if a package is referenced by at least one file in the graph. */
|
|
123
25
|
export function isPackageUsed(graph, pkg) {
|
|
124
26
|
return graph.externalPackages.has(pkg);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importGraph.js","sourceRoot":"","sources":["../../src/core/importGraph.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"importGraph.js","sourceRoot":"","sources":["../../src/core/importGraph.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,IAAI,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAWrF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,KAAkB;IAElB,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAChC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,GAAG;gBAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;AACvE,CAAC;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAEhD,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,KAAkB,EAAE,GAAW;IAC3D,OAAO,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzC,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,cAAc,CAAC,KAAkB,EAAE,GAAW;IAC5D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACf,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CodeGraph } from './codeGraph.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load a previously cached code graph, if present and valid. Returns undefined
|
|
4
|
+
* when there's no cache or the cache is incompatible — caller should rebuild.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadCachedGraph(rootPath: string): Promise<CodeGraph | undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Persist the graph. Creates .projscan-cache/ if needed. Swallows errors —
|
|
9
|
+
* caching is best-effort, never blocks a run.
|
|
10
|
+
*/
|
|
11
|
+
export declare function saveCachedGraph(rootPath: string, graph: CodeGraph): Promise<void>;
|
|
12
|
+
export declare function invalidateCache(rootPath: string): Promise<void>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const CACHE_DIR = '.projscan-cache';
|
|
4
|
+
const CACHE_FILE = 'graph.json';
|
|
5
|
+
const CACHE_VERSION = 1;
|
|
6
|
+
/**
|
|
7
|
+
* Load a previously cached code graph, if present and valid. Returns undefined
|
|
8
|
+
* when there's no cache or the cache is incompatible — caller should rebuild.
|
|
9
|
+
*/
|
|
10
|
+
export async function loadCachedGraph(rootPath) {
|
|
11
|
+
const cachePath = path.join(rootPath, CACHE_DIR, CACHE_FILE);
|
|
12
|
+
let raw;
|
|
13
|
+
try {
|
|
14
|
+
raw = await fs.readFile(cachePath, 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
if (parsed.version !== CACHE_VERSION)
|
|
27
|
+
return undefined;
|
|
28
|
+
const files = new Map();
|
|
29
|
+
for (const entry of parsed.files) {
|
|
30
|
+
files.set(entry.relativePath, {
|
|
31
|
+
relativePath: entry.relativePath,
|
|
32
|
+
imports: entry.imports,
|
|
33
|
+
exports: entry.exports,
|
|
34
|
+
callSites: entry.callSites,
|
|
35
|
+
lineCount: entry.lineCount,
|
|
36
|
+
mtimeMs: entry.mtimeMs,
|
|
37
|
+
parseOk: entry.parseOk,
|
|
38
|
+
parseReason: entry.parseReason,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Derived indexes are rebuilt on load — cheap compared to re-parsing.
|
|
42
|
+
// Return a partial graph the caller will rehydrate via buildCodeGraph.
|
|
43
|
+
return {
|
|
44
|
+
files,
|
|
45
|
+
packageImporters: new Map(),
|
|
46
|
+
localImporters: new Map(),
|
|
47
|
+
symbolDefs: new Map(),
|
|
48
|
+
scannedFiles: files.size,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Persist the graph. Creates .projscan-cache/ if needed. Swallows errors —
|
|
53
|
+
* caching is best-effort, never blocks a run.
|
|
54
|
+
*/
|
|
55
|
+
export async function saveCachedGraph(rootPath, graph) {
|
|
56
|
+
const dir = path.join(rootPath, CACHE_DIR);
|
|
57
|
+
try {
|
|
58
|
+
await fs.mkdir(dir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const payload = {
|
|
64
|
+
version: CACHE_VERSION,
|
|
65
|
+
rootPath,
|
|
66
|
+
files: [...graph.files.values()].map((entry) => ({
|
|
67
|
+
relativePath: entry.relativePath,
|
|
68
|
+
imports: entry.imports,
|
|
69
|
+
exports: entry.exports,
|
|
70
|
+
callSites: entry.callSites,
|
|
71
|
+
lineCount: entry.lineCount,
|
|
72
|
+
mtimeMs: entry.mtimeMs,
|
|
73
|
+
parseOk: entry.parseOk,
|
|
74
|
+
parseReason: entry.parseReason,
|
|
75
|
+
})),
|
|
76
|
+
createdAt: new Date().toISOString(),
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
await fs.writeFile(path.join(dir, CACHE_FILE), JSON.stringify(payload), 'utf-8');
|
|
80
|
+
// Ensure users don't accidentally commit the cache.
|
|
81
|
+
const gitignorePath = path.join(dir, '.gitignore');
|
|
82
|
+
await fs.writeFile(gitignorePath, '*\n', 'utf-8');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// ignore — cache is best-effort
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function invalidateCache(rootPath) {
|
|
89
|
+
const cachePath = path.join(rootPath, CACHE_DIR, CACHE_FILE);
|
|
90
|
+
try {
|
|
91
|
+
await fs.unlink(cachePath);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// ignore
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=indexCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexCache.js","sourceRoot":"","sources":["../../src/core/indexCache.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export { runAudit, auditFindingsToIssues } from './core/auditRunner.js';
|
|
|
11
11
|
export { previewUpgrade } from './core/upgradePreview.js';
|
|
12
12
|
export { parseCoverage, coverageMap } from './core/coverageParser.js';
|
|
13
13
|
export { joinCoverageWithHotspots } from './core/coverageJoin.js';
|
|
14
|
+
export { parseSource, isParseable } from './core/ast.js';
|
|
15
|
+
export { buildCodeGraph, filesImportingFile, filesImportingPackage, filesDefiningSymbol, exportsOf, importsOf, importersOf, } from './core/codeGraph.js';
|
|
16
|
+
export { loadCachedGraph, saveCachedGraph, invalidateCache } from './core/indexCache.js';
|
|
17
|
+
export { applyBudget, estimateTokens } from './mcp/tokenBudget.js';
|
|
14
18
|
export { findDependencyLines } from './utils/packageJsonLocator.js';
|
|
15
19
|
export { parse as parseSemver, compare as compareSemver, drift as semverDrift } from './utils/semver.js';
|
|
16
20
|
export { walkFiles } from './utils/fileWalker.js';
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,10 @@ export { runAudit, auditFindingsToIssues } from './core/auditRunner.js';
|
|
|
11
11
|
export { previewUpgrade } from './core/upgradePreview.js';
|
|
12
12
|
export { parseCoverage, coverageMap } from './core/coverageParser.js';
|
|
13
13
|
export { joinCoverageWithHotspots } from './core/coverageJoin.js';
|
|
14
|
+
export { parseSource, isParseable } from './core/ast.js';
|
|
15
|
+
export { buildCodeGraph, filesImportingFile, filesImportingPackage, filesDefiningSymbol, exportsOf, importsOf, importersOf, } from './core/codeGraph.js';
|
|
16
|
+
export { loadCachedGraph, saveCachedGraph, invalidateCache } from './core/indexCache.js';
|
|
17
|
+
export { applyBudget, estimateTokens } from './mcp/tokenBudget.js';
|
|
14
18
|
export { findDependencyLines } from './utils/packageJsonLocator.js';
|
|
15
19
|
export { parse as parseSemver, compare as compareSemver, drift as semverDrift } from './utils/semver.js';
|
|
16
20
|
export { walkFiles } from './utils/fileWalker.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,OAAO,IAAI,aAAa,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,OAAO,IAAI,aAAa,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { getToolDefinitions, getToolHandler } from './tools.js';
|
|
6
6
|
import { getPromptDefinitions, getPrompt } from './prompts.js';
|
|
7
7
|
import { getResourceDefinitions, readResource } from './resources.js';
|
|
8
|
+
import { applyBudget } from './tokenBudget.js';
|
|
8
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
9
10
|
const JSONRPC_ERROR = {
|
|
10
11
|
ParseError: -32700,
|
|
@@ -69,12 +70,21 @@ export function createMcpServer(rootPath) {
|
|
|
69
70
|
return fail(id, JSONRPC_ERROR.MethodNotFound, `Unknown tool: ${name}`);
|
|
70
71
|
}
|
|
71
72
|
try {
|
|
72
|
-
const
|
|
73
|
+
const args = params.arguments ?? {};
|
|
74
|
+
const result = await handler(args, rootPath);
|
|
75
|
+
const rawMaxTokens = args.max_tokens;
|
|
76
|
+
const maxTokens = typeof rawMaxTokens === 'number' && Number.isFinite(rawMaxTokens) && rawMaxTokens > 0
|
|
77
|
+
? rawMaxTokens
|
|
78
|
+
: undefined;
|
|
79
|
+
const budgeted = applyBudget(result, maxTokens !== undefined ? { maxTokens } : {});
|
|
80
|
+
const payload = budgeted.truncated
|
|
81
|
+
? { ...budgeted.value, _budget: { truncated: true, estimatedTokens: budgeted.estimatedTokens, maxTokens } }
|
|
82
|
+
: budgeted.value;
|
|
73
83
|
return ok(id, {
|
|
74
84
|
content: [
|
|
75
85
|
{
|
|
76
86
|
type: 'text',
|
|
77
|
-
text: safeStringify(
|
|
87
|
+
text: safeStringify(payload),
|
|
78
88
|
},
|
|
79
89
|
],
|
|
80
90
|
isError: false,
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rough token estimator and record-aware truncator for MCP tool output.
|
|
3
|
+
*
|
|
4
|
+
* Uses the widely-used "~4 chars per token" heuristic. Good enough for
|
|
5
|
+
* prioritization — absolute accuracy is not required.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CHARS_PER_TOKEN = 4;
|
|
8
|
+
export declare function estimateTokens(value: string): number;
|
|
9
|
+
export interface BudgetOptions {
|
|
10
|
+
/** Max tokens allowed in the serialized output. */
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
/** Treat arrays at these paths (e.g. "entries", "hotspots") as truncatable. */
|
|
13
|
+
truncatablePaths?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface BudgetResult<T> {
|
|
16
|
+
value: T;
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
estimatedTokens: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Apply a token budget to a structured result. If the JSON-serialized form
|
|
22
|
+
* fits within maxTokens, return as-is. Otherwise, walk truncatable array
|
|
23
|
+
* fields and trim them until we fit (or give up and mark truncated).
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyBudget<T>(value: T, options?: BudgetOptions): BudgetResult<T>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rough token estimator and record-aware truncator for MCP tool output.
|
|
3
|
+
*
|
|
4
|
+
* Uses the widely-used "~4 chars per token" heuristic. Good enough for
|
|
5
|
+
* prioritization — absolute accuracy is not required.
|
|
6
|
+
*/
|
|
7
|
+
export const CHARS_PER_TOKEN = 4;
|
|
8
|
+
export function estimateTokens(value) {
|
|
9
|
+
return Math.ceil(value.length / CHARS_PER_TOKEN);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Apply a token budget to a structured result. If the JSON-serialized form
|
|
13
|
+
* fits within maxTokens, return as-is. Otherwise, walk truncatable array
|
|
14
|
+
* fields and trim them until we fit (or give up and mark truncated).
|
|
15
|
+
*/
|
|
16
|
+
export function applyBudget(value, options = {}) {
|
|
17
|
+
const maxTokens = options.maxTokens;
|
|
18
|
+
if (!maxTokens || maxTokens <= 0) {
|
|
19
|
+
return {
|
|
20
|
+
value,
|
|
21
|
+
truncated: false,
|
|
22
|
+
estimatedTokens: estimateTokens(safeStringify(value)),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
let current = value;
|
|
26
|
+
let serialized = safeStringify(current);
|
|
27
|
+
let tokens = estimateTokens(serialized);
|
|
28
|
+
if (tokens <= maxTokens) {
|
|
29
|
+
return { value: current, truncated: false, estimatedTokens: tokens };
|
|
30
|
+
}
|
|
31
|
+
// Try trimming truncatable arrays progressively: halve each step.
|
|
32
|
+
const paths = options.truncatablePaths ?? findArrayPaths(current);
|
|
33
|
+
if (paths.length === 0) {
|
|
34
|
+
return { value: current, truncated: true, estimatedTokens: tokens };
|
|
35
|
+
}
|
|
36
|
+
let attempt = 0;
|
|
37
|
+
while (tokens > maxTokens && attempt < 20) {
|
|
38
|
+
current = trimArrays(current, paths, 0.5);
|
|
39
|
+
serialized = safeStringify(current);
|
|
40
|
+
tokens = estimateTokens(serialized);
|
|
41
|
+
attempt++;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
value: current,
|
|
45
|
+
truncated: tokens > maxTokens || attempt > 0,
|
|
46
|
+
estimatedTokens: tokens,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function safeStringify(value) {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.stringify(value) ?? '';
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return String(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Find top-level array field names — our convention is that MCP results
|
|
59
|
+
* expose a primary array (hotspots, entries, findings, files) worth
|
|
60
|
+
* trimming before scalar fields.
|
|
61
|
+
*/
|
|
62
|
+
function findArrayPaths(value) {
|
|
63
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
64
|
+
return [];
|
|
65
|
+
const paths = [];
|
|
66
|
+
for (const [key, v] of Object.entries(value)) {
|
|
67
|
+
if (Array.isArray(v))
|
|
68
|
+
paths.push(key);
|
|
69
|
+
}
|
|
70
|
+
return paths;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Shrink arrays at the given paths by the factor (0–1). Preserves shape.
|
|
74
|
+
* Returns a new object, does not mutate.
|
|
75
|
+
*/
|
|
76
|
+
function trimArrays(value, paths, factor) {
|
|
77
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
78
|
+
return value;
|
|
79
|
+
const clone = { ...value };
|
|
80
|
+
for (const key of paths) {
|
|
81
|
+
const arr = clone[key];
|
|
82
|
+
if (Array.isArray(arr) && arr.length > 1) {
|
|
83
|
+
const keep = Math.max(1, Math.floor(arr.length * factor));
|
|
84
|
+
clone[key] = arr.slice(0, keep);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return clone;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=tokenBudget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenBudget.js","sourceRoot":"","sources":["../../src/mcp/tokenBudget.ts"],"names":[],"mappings":"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"}
|