mincut-context 1.3.0 → 1.4.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.
Files changed (47) hide show
  1. package/README.md +104 -50
  2. package/dist/adapters/cli/bin.js +5 -1
  3. package/dist/adapters/cli/bin.js.map +1 -1
  4. package/dist/adapters/mcp/index.js +1 -1
  5. package/dist/index/builder.d.ts +10 -0
  6. package/dist/index/builder.d.ts.map +1 -1
  7. package/dist/index/builder.js +37 -7
  8. package/dist/index/builder.js.map +1 -1
  9. package/dist/lsp/resolver.d.ts +24 -0
  10. package/dist/lsp/resolver.d.ts.map +1 -0
  11. package/dist/lsp/resolver.js +78 -0
  12. package/dist/lsp/resolver.js.map +1 -0
  13. package/dist/lsp/stdio-client.d.ts +31 -0
  14. package/dist/lsp/stdio-client.d.ts.map +1 -0
  15. package/dist/lsp/stdio-client.js +140 -0
  16. package/dist/lsp/stdio-client.js.map +1 -0
  17. package/dist/lsp/types.d.ts +35 -0
  18. package/dist/lsp/types.d.ts.map +1 -0
  19. package/dist/lsp/types.js +7 -0
  20. package/dist/lsp/types.js.map +1 -0
  21. package/dist/lsp/typescript.d.ts +16 -0
  22. package/dist/lsp/typescript.d.ts.map +1 -0
  23. package/dist/lsp/typescript.js +29 -0
  24. package/dist/lsp/typescript.js.map +1 -0
  25. package/dist/parsers/chunking.d.ts +34 -0
  26. package/dist/parsers/chunking.d.ts.map +1 -0
  27. package/dist/parsers/chunking.js +74 -0
  28. package/dist/parsers/chunking.js.map +1 -0
  29. package/dist/parsers/parser.d.ts +4 -0
  30. package/dist/parsers/parser.d.ts.map +1 -1
  31. package/dist/parsers/parser.js.map +1 -1
  32. package/dist/parsers/php.d.ts +2 -2
  33. package/dist/parsers/php.d.ts.map +1 -1
  34. package/dist/parsers/php.js +35 -3
  35. package/dist/parsers/php.js.map +1 -1
  36. package/dist/parsers/py.d.ts +2 -2
  37. package/dist/parsers/py.d.ts.map +1 -1
  38. package/dist/parsers/py.js +21 -4
  39. package/dist/parsers/py.js.map +1 -1
  40. package/dist/parsers/ts.d.ts.map +1 -1
  41. package/dist/parsers/ts.js +35 -84
  42. package/dist/parsers/ts.js.map +1 -1
  43. package/dist/select/pack.d.ts +12 -0
  44. package/dist/select/pack.d.ts.map +1 -1
  45. package/dist/select/pack.js +49 -2
  46. package/dist/select/pack.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1,78 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ /**
4
+ * Use an LSP client to refine ambiguous call edges. For each call site,
5
+ * ask the language server "where is this symbol defined?". If the answer
6
+ * lands inside a known graph node, add (or upgrade) the edge.
7
+ *
8
+ * Existing edges are left alone; this is purely additive / refinement.
9
+ */
10
+ export async function resolveCallsWithLsp(graph, callSites, lsp, repoRoot) {
11
+ let resolved = 0;
12
+ let added = 0;
13
+ for (const cs of callSites) {
14
+ if (!graph.hasNode(cs.from))
15
+ continue;
16
+ let result;
17
+ try {
18
+ result = await lsp.definition(cs.file, { line: cs.line - 1, character: cs.character });
19
+ }
20
+ catch {
21
+ continue;
22
+ }
23
+ if (result.locations.length === 0)
24
+ continue;
25
+ let mappedAny = false;
26
+ for (const loc of result.locations) {
27
+ const targetFile = lspUriToRelPath(loc.uri, repoRoot);
28
+ if (!targetFile)
29
+ continue;
30
+ const targetLine = loc.range.start.line + 1;
31
+ const targetId = findNodeAt(graph, targetFile, targetLine);
32
+ if (!targetId || targetId === cs.from)
33
+ continue;
34
+ mappedAny = true;
35
+ if (graph.hasEdge(cs.from, targetId))
36
+ continue;
37
+ graph.addEdge(cs.from, targetId, { weight: 1.5, kind: 'call' });
38
+ added += 1;
39
+ }
40
+ if (mappedAny)
41
+ resolved += 1;
42
+ }
43
+ return { resolved, added };
44
+ }
45
+ function lspUriToRelPath(uri, repoRoot) {
46
+ let abs;
47
+ try {
48
+ abs = uri.startsWith('file://') ? fileURLToPath(uri) : uri;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ if (!abs.startsWith(repoRoot))
54
+ return null;
55
+ const rel = path.relative(repoRoot, abs).split(path.sep).join('/');
56
+ if (rel.startsWith('..'))
57
+ return null;
58
+ return rel;
59
+ }
60
+ function findNodeAt(graph, file, line) {
61
+ // Prefer the smallest range containing the line (innermost symbol wins).
62
+ let best = null;
63
+ for (const id of graph.nodes()) {
64
+ if (!id.startsWith(`${file}:`))
65
+ continue;
66
+ const data = graph.getNode(id);
67
+ if (!data?.startLine || !data.endLine)
68
+ continue;
69
+ if (line < data.startLine || line > data.endLine)
70
+ continue;
71
+ const span = data.endLine - data.startLine;
72
+ if (!best || span < best.span) {
73
+ best = { id, span };
74
+ }
75
+ }
76
+ return best?.id ?? null;
77
+ }
78
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/lsp/resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { LspClient, LspDefinitionResult, LspPosition } from './types.js';
2
+ /**
3
+ * Bare-metal JSON-RPC client speaking the LSP wire protocol (Content-Length
4
+ * framed messages) over stdio. Spawns the language-server binary, sends
5
+ * initialize, and exposes definition() for our resolver.
6
+ *
7
+ * Designed to be small, dep-free, and fully decoupled from any specific
8
+ * language server. The TypeScript adapter just wires the right command +
9
+ * languageId on top of this.
10
+ */
11
+ export declare class StdioLspClient implements LspClient {
12
+ private readonly command;
13
+ private readonly args;
14
+ private child;
15
+ private buffer;
16
+ private nextId;
17
+ private pending;
18
+ private openFiles;
19
+ private rootPath;
20
+ constructor(command: string, args: string[]);
21
+ initialize(rootPath: string): Promise<void>;
22
+ didOpen(file: string, source: string, languageId: string): Promise<void>;
23
+ definition(file: string, position: LspPosition): Promise<LspDefinitionResult>;
24
+ shutdown(): Promise<void>;
25
+ private request;
26
+ private notify;
27
+ private send;
28
+ private onData;
29
+ private flushPendingAsErrors;
30
+ }
31
+ //# sourceMappingURL=stdio-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio-client.d.ts","sourceRoot":"","sources":["../../src/lsp/stdio-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAe,WAAW,EAAE,MAAM,YAAY,CAAC;AAsB3F;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,SAAS;IAQlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPnE,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAoF;IACnG,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAM;gBAEO,OAAO,EAAE,MAAM,EAAmB,IAAI,EAAE,MAAM,EAAE;IAEvE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB3C,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAU7E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,MAAM;IA8Bd,OAAO,CAAC,oBAAoB;CAI7B"}
@@ -0,0 +1,140 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { pathToFileURL } from 'node:url';
3
+ import path from 'node:path';
4
+ /**
5
+ * Bare-metal JSON-RPC client speaking the LSP wire protocol (Content-Length
6
+ * framed messages) over stdio. Spawns the language-server binary, sends
7
+ * initialize, and exposes definition() for our resolver.
8
+ *
9
+ * Designed to be small, dep-free, and fully decoupled from any specific
10
+ * language server. The TypeScript adapter just wires the right command +
11
+ * languageId on top of this.
12
+ */
13
+ export class StdioLspClient {
14
+ command;
15
+ args;
16
+ child = null;
17
+ buffer = Buffer.alloc(0);
18
+ nextId = 1;
19
+ pending = new Map();
20
+ openFiles = new Set();
21
+ rootPath = '';
22
+ constructor(command, args) {
23
+ this.command = command;
24
+ this.args = args;
25
+ }
26
+ async initialize(rootPath) {
27
+ this.rootPath = rootPath;
28
+ this.child = spawn(this.command, this.args, { stdio: ['pipe', 'pipe', 'pipe'] });
29
+ if (!this.child.stdout || !this.child.stdin)
30
+ throw new Error('lsp: no stdio on spawned process');
31
+ this.child.stdout.on('data', (chunk) => this.onData(chunk));
32
+ this.child.on('exit', () => this.flushPendingAsErrors('lsp server exited'));
33
+ this.child.stderr?.on('data', () => {
34
+ /* swallow — many LSPs are chatty on stderr */
35
+ });
36
+ await this.request('initialize', {
37
+ processId: process.pid,
38
+ rootUri: pathToFileURL(rootPath).toString(),
39
+ capabilities: { textDocument: { definition: { dynamicRegistration: false } } },
40
+ });
41
+ this.notify('initialized', {});
42
+ }
43
+ async didOpen(file, source, languageId) {
44
+ if (this.openFiles.has(file))
45
+ return;
46
+ this.openFiles.add(file);
47
+ const uri = pathToFileURL(path.resolve(this.rootPath, file)).toString();
48
+ this.notify('textDocument/didOpen', {
49
+ textDocument: { uri, languageId, version: 1, text: source },
50
+ });
51
+ }
52
+ async definition(file, position) {
53
+ const uri = pathToFileURL(path.resolve(this.rootPath, file)).toString();
54
+ const result = (await this.request('textDocument/definition', {
55
+ textDocument: { uri },
56
+ position,
57
+ }));
58
+ if (!result)
59
+ return { locations: [] };
60
+ return { locations: Array.isArray(result) ? result : [result] };
61
+ }
62
+ async shutdown() {
63
+ if (!this.child)
64
+ return;
65
+ try {
66
+ await this.request('shutdown', null);
67
+ this.notify('exit', null);
68
+ }
69
+ catch {
70
+ // ignore
71
+ }
72
+ this.child.kill('SIGTERM');
73
+ this.child = null;
74
+ }
75
+ request(method, params) {
76
+ const id = this.nextId++;
77
+ const req = { jsonrpc: '2.0', id, method, params };
78
+ this.send(req);
79
+ return new Promise((resolve, reject) => {
80
+ this.pending.set(id, { resolve, reject });
81
+ setTimeout(() => {
82
+ if (this.pending.has(id)) {
83
+ this.pending.delete(id);
84
+ reject(new Error(`lsp: timeout on ${method}`));
85
+ }
86
+ }, 10000);
87
+ });
88
+ }
89
+ notify(method, params) {
90
+ const n = { jsonrpc: '2.0', method, params };
91
+ this.send(n);
92
+ }
93
+ send(message) {
94
+ if (!this.child?.stdin)
95
+ return;
96
+ const body = JSON.stringify(message);
97
+ const header = `Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n`;
98
+ this.child.stdin.write(header + body);
99
+ }
100
+ onData(chunk) {
101
+ this.buffer = Buffer.concat([this.buffer, chunk]);
102
+ while (true) {
103
+ const headerEnd = this.buffer.indexOf('\r\n\r\n');
104
+ if (headerEnd < 0)
105
+ return;
106
+ const headerText = this.buffer.slice(0, headerEnd).toString('utf8');
107
+ const m = headerText.match(/Content-Length:\s*(\d+)/i);
108
+ if (!m) {
109
+ this.buffer = this.buffer.slice(headerEnd + 4);
110
+ continue;
111
+ }
112
+ const len = Number(m[1]);
113
+ const total = headerEnd + 4 + len;
114
+ if (this.buffer.length < total)
115
+ return;
116
+ const body = this.buffer.slice(headerEnd + 4, total).toString('utf8');
117
+ this.buffer = this.buffer.slice(total);
118
+ try {
119
+ const msg = JSON.parse(body);
120
+ if (typeof msg.id === 'number' && this.pending.has(msg.id)) {
121
+ const cb = this.pending.get(msg.id);
122
+ this.pending.delete(msg.id);
123
+ if (msg.error)
124
+ cb.reject(new Error(msg.error.message));
125
+ else
126
+ cb.resolve(msg.result);
127
+ }
128
+ }
129
+ catch {
130
+ // ignore non-JSON server output
131
+ }
132
+ }
133
+ }
134
+ flushPendingAsErrors(reason) {
135
+ for (const [, cb] of this.pending)
136
+ cb.reject(new Error(reason));
137
+ this.pending.clear();
138
+ }
139
+ }
140
+ //# sourceMappingURL=stdio-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio-client.js","sourceRoot":"","sources":["../../src/lsp/stdio-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Minimal LSP types we use. Faithful subset of the LSP spec
3
+ * (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/),
4
+ * just the bits the resolver needs.
5
+ */
6
+ export interface LspPosition {
7
+ line: number;
8
+ character: number;
9
+ }
10
+ export interface LspRange {
11
+ start: LspPosition;
12
+ end: LspPosition;
13
+ }
14
+ export interface LspLocation {
15
+ uri: string;
16
+ range: LspRange;
17
+ }
18
+ export interface LspDefinitionResult {
19
+ /** Either a single Location or an array of Locations. */
20
+ locations: LspLocation[];
21
+ }
22
+ export interface LspClient {
23
+ /** Initialize the LSP server with the given root path. */
24
+ initialize(rootPath: string): Promise<void>;
25
+ /** Tell the server the file is open (some servers require this before definition queries). */
26
+ didOpen(file: string, source: string, languageId: string): Promise<void>;
27
+ /**
28
+ * Ask the server "where is the symbol at this position defined?"
29
+ * Returns 0+ locations.
30
+ */
31
+ definition(file: string, position: LspPosition): Promise<LspDefinitionResult>;
32
+ /** Gracefully shut down. */
33
+ shutdown(): Promise<void>;
34
+ }
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lsp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,0DAA0D;IAC1D,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,8FAA8F;IAC9F,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9E,4BAA4B;IAC5B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Minimal LSP types we use. Faithful subset of the LSP spec
3
+ * (https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/),
4
+ * just the bits the resolver needs.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lsp/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,16 @@
1
+ import type { LspClient } from './types.js';
2
+ /**
3
+ * Spawn typescript-language-server for the given repo.
4
+ *
5
+ * Requires the binary to be installed (peerOptional dep — we don't bundle).
6
+ * npm install -g typescript-language-server
7
+ * or per-project: npm install --save-dev typescript-language-server
8
+ *
9
+ * If the binary is missing, `initialize()` will throw with a helpful message
10
+ * and the caller should fall back to syntactic resolution.
11
+ */
12
+ export declare function createTypeScriptLsp(opts?: {
13
+ command?: string;
14
+ }): LspClient;
15
+ export declare function tsLanguageIdFor(file: string): string;
16
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../src/lsp/typescript.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,CAG9E;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOpD"}
@@ -0,0 +1,29 @@
1
+ import { StdioLspClient } from './stdio-client.js';
2
+ /**
3
+ * Spawn typescript-language-server for the given repo.
4
+ *
5
+ * Requires the binary to be installed (peerOptional dep — we don't bundle).
6
+ * npm install -g typescript-language-server
7
+ * or per-project: npm install --save-dev typescript-language-server
8
+ *
9
+ * If the binary is missing, `initialize()` will throw with a helpful message
10
+ * and the caller should fall back to syntactic resolution.
11
+ */
12
+ export function createTypeScriptLsp(opts = {}) {
13
+ const command = opts.command ?? 'typescript-language-server';
14
+ return new StdioLspClient(command, ['--stdio']);
15
+ }
16
+ export function tsLanguageIdFor(file) {
17
+ if (file.endsWith('.tsx'))
18
+ return 'typescriptreact';
19
+ if (file.endsWith('.jsx'))
20
+ return 'javascriptreact';
21
+ if (file.endsWith('.ts'))
22
+ return 'typescript';
23
+ if (file.endsWith('.js'))
24
+ return 'javascript';
25
+ if (file.endsWith('.mjs') || file.endsWith('.cjs'))
26
+ return 'javascript';
27
+ return 'plaintext';
28
+ }
29
+ //# sourceMappingURL=typescript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../src/lsp/typescript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B,EAAE;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,4BAA4B,CAAC;IAC7D,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,iBAAiB,CAAC;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,iBAAiB,CAAC;IACpD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAC9C,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IACxE,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type Parser from 'tree-sitter';
2
+ import { type ChunkOptions, type ParsedSymbol, type NodeKind } from './parser.js';
3
+ /**
4
+ * Language-agnostic chunking helper.
5
+ *
6
+ * Each parser's visit-function calls this whenever it enters a function-like
7
+ * node. We split the body's top-level statements (whatever `bodyNode`'s
8
+ * named children are) into chunks whose token sum is roughly maxTokens.
9
+ *
10
+ * Returns `null` if the function should NOT be chunked (small enough, or
11
+ * chunking disabled). Returns the emitted chunk symbols if it WAS chunked,
12
+ * along with a `visitStmt` callback the caller invokes per statement so that
13
+ * call edges attribute to the correct chunk id.
14
+ */
15
+ export interface ChunkResult {
16
+ symbols: ParsedSymbol[];
17
+ /** Per-statement walker for the caller — must be invoked in order. */
18
+ walkStatements: (visitStmt: (stmt: Parser.SyntaxNode) => void) => void;
19
+ }
20
+ export declare function tryChunkBody(args: {
21
+ file: string;
22
+ source: string;
23
+ qualifiedName: string;
24
+ bareName: string;
25
+ kind: NodeKind;
26
+ body: Parser.SyntaxNode | null;
27
+ chunkOptions?: ChunkOptions;
28
+ /**
29
+ * Push/pop caller stack around each chunk walk so the body's calls
30
+ * attribute to the chunk id instead of the parent.
31
+ */
32
+ callerStack: string[];
33
+ }): ChunkResult | null;
34
+ //# sourceMappingURL=chunking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../../src/parsers/chunking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAgB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEhG;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,sEAAsE;IACtE,cAAc,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,KAAK,IAAI,KAAK,IAAI,CAAC;CACxE;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE;IACJ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GACA,WAAW,GAAG,IAAI,CA+EpB"}
@@ -0,0 +1,74 @@
1
+ import { approxTokens } from './parser.js';
2
+ export function tryChunkBody(args) {
3
+ const { file, source, qualifiedName, body, chunkOptions, callerStack, kind } = args;
4
+ if (!chunkOptions?.enabled || !body)
5
+ return null;
6
+ const bodyText = source.slice(body.startIndex, body.endIndex);
7
+ if (approxTokens(bodyText) <= chunkOptions.maxTokens)
8
+ return null;
9
+ const statements = [];
10
+ for (let i = 0; i < body.namedChildCount; i++) {
11
+ const c = body.namedChild(i);
12
+ if (c)
13
+ statements.push(c);
14
+ }
15
+ if (statements.length < 2)
16
+ return null;
17
+ const stmtTokens = statements.map((s) => approxTokens(source.slice(s.startIndex, s.endIndex)));
18
+ const stmtsTotal = stmtTokens.reduce((a, b) => a + b, 0);
19
+ // Use the larger of (maxTokens) and (stmtsTotal / 2) so we always split at
20
+ // least once when the body is over threshold.
21
+ const target = Math.max(chunkOptions.maxTokens, Math.ceil(stmtsTotal / 2));
22
+ const groups = [];
23
+ let current = [];
24
+ let currentTokens = 0;
25
+ for (let i = 0; i < statements.length; i++) {
26
+ const stmt = statements[i];
27
+ if (currentTokens + stmtTokens[i] > target && current.length > 0) {
28
+ groups.push({ start: current[0], end: current[current.length - 1], members: current });
29
+ current = [];
30
+ currentTokens = 0;
31
+ }
32
+ current.push(stmt);
33
+ currentTokens += stmtTokens[i];
34
+ }
35
+ if (current.length > 0) {
36
+ groups.push({ start: current[0], end: current[current.length - 1], members: current });
37
+ }
38
+ if (groups.length < 2) {
39
+ // Body exceeded threshold but greedy packing put everything in one group.
40
+ // Fall back to a midpoint split so the user still gets >=2 chunks.
41
+ const mid = Math.ceil(statements.length / 2);
42
+ const a = statements.slice(0, mid);
43
+ const b = statements.slice(mid);
44
+ if (a.length === 0 || b.length === 0)
45
+ return null;
46
+ groups.length = 0;
47
+ groups.push({ start: a[0], end: a[a.length - 1], members: a });
48
+ groups.push({ start: b[0], end: b[b.length - 1], members: b });
49
+ }
50
+ const symbols = groups.map((g, index) => {
51
+ const text = source.slice(g.start.startIndex, g.end.endIndex);
52
+ const baseLeaf = qualifiedName.split('.').pop() ?? qualifiedName;
53
+ return {
54
+ id: `${file}:${qualifiedName}#${index}`,
55
+ name: `${baseLeaf}#${index}`,
56
+ file,
57
+ kind,
58
+ startLine: g.start.startPosition.row + 1,
59
+ endLine: g.end.endPosition.row + 1,
60
+ tokens: approxTokens(text),
61
+ chunk: { parent: qualifiedName, index },
62
+ };
63
+ });
64
+ const walkStatements = (visitStmt) => {
65
+ groups.forEach((g, index) => {
66
+ callerStack.push(symbols[index].id);
67
+ for (const member of g.members)
68
+ visitStmt(member);
69
+ callerStack.pop();
70
+ });
71
+ };
72
+ return { symbols, walkStatements };
73
+ }
74
+ //# sourceMappingURL=chunking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.js","sourceRoot":"","sources":["../../src/parsers/chunking.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { NodeKind } from '../core/graph.js';
2
+ export type { NodeKind } from '../core/graph.js';
2
3
  export interface ParsedSymbol {
3
4
  id: string;
4
5
  name: string;
@@ -26,6 +27,9 @@ export interface ParsedImport {
26
27
  export interface ParsedCall {
27
28
  from: string;
28
29
  toName: string;
30
+ /** Optional source position of the callee identifier (1-based line, 0-based col). */
31
+ line?: number;
32
+ character?: number;
29
33
  }
30
34
  export interface ParseResult {
31
35
  symbols: ParsedSymbol[];
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parsers/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parsers/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parsers/parser.ts"],"names":[],"mappings":"AA2CA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parsers/parser.ts"],"names":[],"mappings":"AA+CA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -1,3 +1,3 @@
1
- import { type ParseResult } from './parser.js';
2
- export declare function parsePhp(file: string, source: string): ParseResult;
1
+ import { type ChunkOptions, type ParseResult } from './parser.js';
2
+ export declare function parsePhp(file: string, source: string, chunkOptions?: ChunkOptions): ParseResult;
3
3
  //# sourceMappingURL=php.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/parsers/php.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAQrB,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAmBlE"}
1
+ {"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/parsers/php.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,YAAY,EAGjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AASrB,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,YAAY,GAC1B,WAAW,CAoBb"}
@@ -1,11 +1,12 @@
1
1
  import Parser from 'tree-sitter';
2
2
  import { createRequire } from 'node:module';
3
3
  import { approxTokens, } from './parser.js';
4
+ import { tryChunkBody } from './chunking.js';
4
5
  const require_ = createRequire(import.meta.url);
5
6
  const Php = require_('tree-sitter-php');
6
7
  const phpParser = new Parser();
7
8
  phpParser.setLanguage(Php.php);
8
- export function parsePhp(file, source) {
9
+ export function parsePhp(file, source, chunkOptions) {
9
10
  let tree;
10
11
  try {
11
12
  tree = phpParser.parse(source);
@@ -21,6 +22,7 @@ export function parsePhp(file, source) {
21
22
  calls: [],
22
23
  classStack: [],
23
24
  callerStack: [],
25
+ chunkOptions,
24
26
  };
25
27
  visit(tree.rootNode, ctx);
26
28
  return { symbols: ctx.symbols, imports: ctx.imports, calls: ctx.calls };
@@ -30,10 +32,25 @@ function visit(node, ctx) {
30
32
  case 'function_definition': {
31
33
  const name = node.childForFieldName('name')?.text;
32
34
  if (name) {
35
+ const body = node.childForFieldName('body');
36
+ const chunked = tryChunkBody({
37
+ file: ctx.file,
38
+ source: ctx.source,
39
+ qualifiedName: name,
40
+ bareName: name,
41
+ kind: 'function',
42
+ body,
43
+ chunkOptions: ctx.chunkOptions,
44
+ callerStack: ctx.callerStack,
45
+ });
46
+ if (chunked) {
47
+ ctx.symbols.push(...chunked.symbols);
48
+ chunked.walkStatements((stmt) => visit(stmt, ctx));
49
+ return;
50
+ }
33
51
  const sym = mk(ctx, node, name, name, 'function');
34
52
  ctx.symbols.push(sym);
35
53
  ctx.callerStack.push(sym.id);
36
- const body = node.childForFieldName('body');
37
54
  if (body)
38
55
  visit(body, ctx);
39
56
  ctx.callerStack.pop();
@@ -67,10 +84,25 @@ function visit(node, ctx) {
67
84
  break;
68
85
  const cls = ctx.classStack[ctx.classStack.length - 1];
69
86
  const qualified = cls ? `${cls}.${name}` : name;
87
+ const body = node.childForFieldName('body');
88
+ const chunked = tryChunkBody({
89
+ file: ctx.file,
90
+ source: ctx.source,
91
+ qualifiedName: qualified,
92
+ bareName: name,
93
+ kind: 'method',
94
+ body,
95
+ chunkOptions: ctx.chunkOptions,
96
+ callerStack: ctx.callerStack,
97
+ });
98
+ if (chunked) {
99
+ ctx.symbols.push(...chunked.symbols);
100
+ chunked.walkStatements((stmt) => visit(stmt, ctx));
101
+ return;
102
+ }
70
103
  const sym = mk(ctx, node, name, qualified, 'method');
71
104
  ctx.symbols.push(sym);
72
105
  ctx.callerStack.push(sym.id);
73
- const body = node.childForFieldName('body');
74
106
  if (body)
75
107
  visit(body, ctx);
76
108
  ctx.callerStack.pop();