ts-knowledge-graph 0.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.
Files changed (68) hide show
  1. package/.env-sample +34 -0
  2. package/LICENSE +21 -0
  3. package/README.md +153 -0
  4. package/dist/agent/agent-tools.d.ts +13 -0
  5. package/dist/agent/agent-tools.d.ts.map +1 -0
  6. package/dist/agent/agent-tools.js +153 -0
  7. package/dist/agent/agent-tools.js.map +1 -0
  8. package/dist/agent/code-editor.d.ts +18 -0
  9. package/dist/agent/code-editor.d.ts.map +1 -0
  10. package/dist/agent/code-editor.js +43 -0
  11. package/dist/agent/code-editor.js.map +1 -0
  12. package/dist/agent/optimizer-agent.d.ts +30 -0
  13. package/dist/agent/optimizer-agent.d.ts.map +1 -0
  14. package/dist/agent/optimizer-agent.js +97 -0
  15. package/dist/agent/optimizer-agent.js.map +1 -0
  16. package/dist/agent/verifier.d.ts +9 -0
  17. package/dist/agent/verifier.d.ts.map +1 -0
  18. package/dist/agent/verifier.js +19 -0
  19. package/dist/agent/verifier.js.map +1 -0
  20. package/dist/cli.d.ts +14 -0
  21. package/dist/cli.d.ts.map +1 -0
  22. package/dist/cli.js +221 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/extract/graph-builder.d.ts +16 -0
  25. package/dist/extract/graph-builder.d.ts.map +1 -0
  26. package/dist/extract/graph-builder.js +39 -0
  27. package/dist/extract/graph-builder.js.map +1 -0
  28. package/dist/extract/node-id.d.ts +8 -0
  29. package/dist/extract/node-id.d.ts.map +1 -0
  30. package/dist/extract/node-id.js +22 -0
  31. package/dist/extract/node-id.js.map +1 -0
  32. package/dist/extract/project-loader.d.ts +5 -0
  33. package/dist/extract/project-loader.d.ts.map +1 -0
  34. package/dist/extract/project-loader.js +19 -0
  35. package/dist/extract/project-loader.js.map +1 -0
  36. package/dist/extract/semantic-extractor.d.ts +22 -0
  37. package/dist/extract/semantic-extractor.d.ts.map +1 -0
  38. package/dist/extract/semantic-extractor.js +254 -0
  39. package/dist/extract/semantic-extractor.js.map +1 -0
  40. package/dist/extract/structural-extractor.d.ts +18 -0
  41. package/dist/extract/structural-extractor.d.ts.map +1 -0
  42. package/dist/extract/structural-extractor.js +97 -0
  43. package/dist/extract/structural-extractor.js.map +1 -0
  44. package/dist/query/graph-query.d.ts +28 -0
  45. package/dist/query/graph-query.d.ts.map +1 -0
  46. package/dist/query/graph-query.js +93 -0
  47. package/dist/query/graph-query.js.map +1 -0
  48. package/dist/schema/edge.d.ts +25 -0
  49. package/dist/schema/edge.d.ts.map +1 -0
  50. package/dist/schema/edge.js +25 -0
  51. package/dist/schema/edge.js.map +1 -0
  52. package/dist/schema/node.d.ts +73 -0
  53. package/dist/schema/node.d.ts.map +1 -0
  54. package/dist/schema/node.js +31 -0
  55. package/dist/schema/node.js.map +1 -0
  56. package/dist/store/jsonl-reader.d.ts +11 -0
  57. package/dist/store/jsonl-reader.d.ts.map +1 -0
  58. package/dist/store/jsonl-reader.js +19 -0
  59. package/dist/store/jsonl-reader.js.map +1 -0
  60. package/dist/store/jsonl-store.d.ts +7 -0
  61. package/dist/store/jsonl-store.d.ts.map +1 -0
  62. package/dist/store/jsonl-store.js +13 -0
  63. package/dist/store/jsonl-store.js.map +1 -0
  64. package/dist/store/kuzu-store.d.ts +14 -0
  65. package/dist/store/kuzu-store.d.ts.map +1 -0
  66. package/dist/store/kuzu-store.js +52 -0
  67. package/dist/store/kuzu-store.js.map +1 -0
  68. package/package.json +41 -0
package/.env-sample ADDED
@@ -0,0 +1,34 @@
1
+ # Copy to .env and pick ONE provider block below.
2
+ # The agent uses the OpenAI-compatible chat-completions API, so any provider
3
+ # exposing that surface works — only the three variables below change.
4
+ #
5
+ # OPENAI_API_KEY auth token for the provider (any non-empty string for local servers)
6
+ # OPENAI_BASE_URL the provider's OpenAI-compatible endpoint (omit for openai.com)
7
+ # OPENAI_MODEL model name, in the provider's naming scheme
8
+
9
+ # --- OpenAI (default) -------------------------------------------------------
10
+ OPENAI_API_KEY=sk-...
11
+ # OPENAI_BASE_URL is not needed — defaults to https://api.openai.com/v1
12
+ OPENAI_MODEL=gpt-5.1
13
+
14
+ # --- OpenRouter (any model behind one key) ----------------------------------
15
+ # OPENAI_API_KEY=sk-or-v1-...
16
+ # OPENAI_BASE_URL=https://openrouter.ai/api/v1
17
+ # OPENAI_MODEL=anthropic/claude-sonnet-4.5
18
+ # OPENAI_MODEL=openai/gpt-5.1
19
+ # OPENAI_MODEL=deepseek/deepseek-chat-v3.1
20
+
21
+ # --- Ollama (local, free) ---------------------------------------------------
22
+ # OPENAI_API_KEY=ollama
23
+ # OPENAI_BASE_URL=http://localhost:11434/v1
24
+ # OPENAI_MODEL=qwen2.5-coder:32b
25
+
26
+ # --- LM Studio (local) ------------------------------------------------------
27
+ # OPENAI_API_KEY=lm-studio
28
+ # OPENAI_BASE_URL=http://localhost:1234/v1
29
+ # OPENAI_MODEL=local-model
30
+
31
+ # --- vLLM (self-hosted) -----------------------------------------------------
32
+ # OPENAI_API_KEY=token-abc123
33
+ # OPENAI_BASE_URL=http://localhost:8000/v1
34
+ # OPENAI_MODEL=Qwen/Qwen2.5-Coder-32B-Instruct
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jerome Etienne
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # open_ts_optim_ai
2
+
3
+ Parse TypeScript source code into a **knowledge graph**, then use that graph as
4
+ the substrate for an autonomous AI agent that finds and applies code
5
+ optimizations.
6
+
7
+ ## Why a graph
8
+
9
+ An optimization agent constantly needs to reason about *blast radius*:
10
+
11
+ - *If I rewrite this function, who calls it and what breaks?* — `CALLS` edges
12
+ - *Is this export dead code I can delete?* — cross-file reference resolution
13
+ - *What is affected if I change this type?* — `USES_TYPE` / type-checker edges
14
+
15
+ These questions require **semantic** parsing (symbol + type resolution), which is
16
+ why the extractor is built on [`ts-morph`](https://ts-morph.com) (the TypeScript
17
+ Compiler API) rather than a syntax-only parser.
18
+
19
+ ## Graph model
20
+
21
+ **Nodes** — `Module`, `Class`, `Interface`, `TypeAlias`, `Enum`, `Function`,
22
+ `Method`, `Property`, `Parameter`, `Variable`, `ExternalModule`.
23
+
24
+ **Edges**
25
+
26
+ | Layer | Edges |
27
+ | --- | --- |
28
+ | Structural | `CONTAINS`, `IMPORTS`, `EXPORTS` |
29
+ | Type | `EXTENDS`, `IMPLEMENTS`, `USES_TYPE`, `RETURNS`, `PARAM_TYPE` |
30
+ | Behavioral | `CALLS`, `INSTANTIATES`, `OVERRIDES`, `READS`, `WRITES` |
31
+
32
+ The structural layer is cheap and always emitted. The type + behavioral layers
33
+ require symbol resolution and are emitted with `--semantic`.
34
+
35
+ ## Usage
36
+
37
+ ```bash
38
+ npm install
39
+
40
+ # structural graph only (fast)
41
+ npm run extract -- <path-to-project>
42
+
43
+ # full graph with heritage + CALLS edges
44
+ npm run extract -- <path-to-project> --semantic
45
+ ```
46
+
47
+ Output is two JSONL files — `outputs/graph/nodes.jsonl` and
48
+ `outputs/graph/edges.jsonl` (override with `--out`) — one record per line, easy
49
+ to inspect, diff, and load into any store.
50
+
51
+ ### Querying the graph
52
+
53
+ Load the JSONL into an embedded [Kùzu](https://kuzudb.com) database, then run the
54
+ query tools:
55
+
56
+ ```bash
57
+ npm run dev -- load # reads ./outputs/graph, writes ./outputs/graph.kuzu
58
+
59
+ npm run dev -- find <name> # resolve a name to node ids
60
+ npm run dev -- who-calls <id> # direct callers of a symbol
61
+ npm run dev -- calls <id> # what a symbol calls
62
+ npm run dev -- blast-radius <id> --depth 10 # transitive callers (impact set)
63
+ npm run dev -- references <id> # everything that references a symbol/type
64
+ npm run dev -- dead-exports # exported symbols with no inbound refs
65
+ npm run dev -- neighbors <id> # one-hop neighbourhood (in + out)
66
+ ```
67
+
68
+ Every query command accepts `--json` to emit machine-readable output — this is
69
+ the shape the optimization agent consumes. Node ids come from `find` or another
70
+ query's results; do not hand-write them.
71
+
72
+ The query methods on `GraphQuery` (`whoCalls`, `blastRadius`, `deadExports`,
73
+ `neighborhood`, …) are designed to map one-to-one onto agent tools: JSON in,
74
+ JSON out.
75
+
76
+ > **`dead-exports` accuracy:** it is member-aware (a class/interface counts as
77
+ > live when any contained member is referenced) and considers `CALLS`,
78
+ > `EXTENDS`, `IMPLEMENTS`, `USES_TYPE`, `RETURNS`, `PARAM_TYPE`, `INSTANTIATES`,
79
+ > and `READS` (value-identifier) edges. On this repository it reports exactly the
80
+ > two genuinely-unused type aliases — no false positives.
81
+
82
+ ### The optimization agent
83
+
84
+ The end goal: an agent that uses the graph to find and apply optimizations,
85
+ verifying each one before keeping it.
86
+
87
+ ```bash
88
+ cp .env-sample .env # pick a provider block, set key + model
89
+ npm run dev -- optimize --db ./outputs/graph.kuzu
90
+ npm run dev -- optimize "Inline the single-use helper X" --db ./outputs/graph.kuzu --model gpt-5.1
91
+ ```
92
+
93
+ The LLM layer sits on the **OpenAI-compatible chat-completions API**, so any
94
+ provider exposing that surface works — OpenAI, OpenRouter, Ollama, LM Studio,
95
+ vLLM — configured entirely through `OPENAI_API_KEY`, `OPENAI_BASE_URL`, and
96
+ `OPENAI_MODEL` (see [.env-sample](.env-sample)).
97
+
98
+ The agent runs a tool-calling loop. Its tools are the read-only `GraphQuery`
99
+ methods plus `read_file`; it gathers context, confirms blast radius, then calls
100
+ `propose_optimization`. The harness **applies the edit, runs `tsc --noEmit`,
101
+ and keeps it only if type-checking passes** — otherwise it reverts and hands
102
+ the compiler errors back for another attempt. Edits are unique-match
103
+ find/replace with in-memory backups; run on a clean git tree so you can review
104
+ (and `git checkout`) what it kept.
105
+
106
+ ## Architecture
107
+
108
+ ```
109
+ src/
110
+ schema/ Zod schemas for nodes and edges (the wire format)
111
+ extract/
112
+ project-loader.ts load a ts-morph Project from tsconfig
113
+ node-id.ts deterministic, position-stable node ids
114
+ structural-extractor.ts modules, declarations, imports, containment
115
+ semantic-extractor.ts heritage, CALLS, INSTANTIATES, type edges
116
+ graph-builder.ts orchestrates extraction, dedupes by id
117
+ store/
118
+ jsonl-store.ts serialize the graph to JSONL
119
+ jsonl-reader.ts read + Zod-validate the JSONL back in
120
+ kuzu-store.ts load the graph into embedded Kùzu, run Cypher
121
+ query/
122
+ graph-query.ts the agent's query tools (who-calls, blast-radius…)
123
+ agent/
124
+ agent-tools.ts graph queries + read_file + propose_optimization, as LLM tools
125
+ code-editor.ts unique-match find/replace with in-memory backup + revert
126
+ verifier.ts runs `tsc --noEmit`, returns pass/fail + output
127
+ optimizer-agent.ts the LLM tool-calling loop (propose → verify → keep/revert)
128
+ cli.ts extract / load / query / optimize commands
129
+ ```
130
+
131
+ Node ids are derived purely from the declaration (`kind:relPath#name@line`), so
132
+ any extractor computes the same id for the same symbol without a shared
133
+ registry — that is what lets the semantic layer link a call site to the exact
134
+ declaration node the structural layer emitted.
135
+
136
+ ## Roadmap
137
+
138
+ - [x] **Embedded query layer** — load into [Kùzu](https://kuzudb.com) (embedded,
139
+ Cypher) with traversal tools: `who-calls`, `calls`, `blast-radius`,
140
+ `dead-exports`, `neighbors`, `find`.
141
+ - [x] **Type edges** — `USES_TYPE`, `RETURNS`, `PARAM_TYPE` (plus `INSTANTIATES`)
142
+ resolved through import aliases.
143
+ - [x] **Member-aware reference counting** — a class/interface is live when any
144
+ contained member is referenced.
145
+ - [x] **Value-reference (`READS`) edges** — value-identifier usage, so exported
146
+ `const`s (e.g. schemas) are no longer false-positive dead exports.
147
+ - [x] **Optimization agent** — an LLM tool-calling loop (OpenAI-compatible API,
148
+ provider-agnostic) that proposes edits and keeps them only if `tsc --noEmit`
149
+ passes (otherwise reverts).
150
+ - [ ] **Test verification** — run the test suite alongside `tsc` in the verify
151
+ step, so behavior-changing edits are caught, not just type errors.
152
+ - [ ] **Vector index** — embed per-node summaries for hybrid graph + semantic
153
+ retrieval, so the agent can find candidates by meaning, not just by name.
@@ -0,0 +1,13 @@
1
+ import OpenAI from 'openai';
2
+ import { GraphQuery } from '../query/graph-query.js';
3
+ export declare const PROPOSE_TOOL_NAME = "propose_optimization";
4
+ export declare const AGENT_TOOLS: OpenAI.Chat.Completions.ChatCompletionTool[];
5
+ export declare class AgentTools {
6
+ private readonly query;
7
+ private readonly rootPath;
8
+ constructor(query: GraphQuery, rootPath: string);
9
+ dispatch(name: string, input: Record<string, unknown>): Promise<string>;
10
+ private readFile;
11
+ private static stringify;
12
+ }
13
+ //# sourceMappingURL=agent-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/agent/agent-tools.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,eAAO,MAAM,iBAAiB,yBAAyB,CAAC;AAExD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,kBAAkB,EAyGnE,CAAC;AAEF,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;IAKzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;YAqB/D,QAAQ;IAetB,OAAO,CAAC,MAAM,CAAC,SAAS;CAGxB"}
@@ -0,0 +1,153 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ export const PROPOSE_TOOL_NAME = 'propose_optimization';
4
+ export const AGENT_TOOLS = [
5
+ {
6
+ type: 'function',
7
+ function: {
8
+ name: 'find_symbol',
9
+ description: 'Resolve a symbol name (substring, case-insensitive) to candidate node ids. Always start here to obtain ids; never invent ids.',
10
+ parameters: {
11
+ type: 'object',
12
+ properties: { name: { type: 'string', description: 'symbol name or substring' } },
13
+ required: ['name'],
14
+ },
15
+ },
16
+ },
17
+ {
18
+ type: 'function',
19
+ function: {
20
+ name: 'who_calls',
21
+ description: 'List the direct callers of a function/method node id.',
22
+ parameters: {
23
+ type: 'object',
24
+ properties: { id: { type: 'string', description: 'node id from find_symbol' } },
25
+ required: ['id'],
26
+ },
27
+ },
28
+ },
29
+ {
30
+ type: 'function',
31
+ function: {
32
+ name: 'references',
33
+ description: 'List everything that references a symbol or type (calls, type usage, heritage, instantiation, value reads). Use this to judge whether a symbol is safe to remove.',
34
+ parameters: {
35
+ type: 'object',
36
+ properties: { id: { type: 'string', description: 'node id from find_symbol' } },
37
+ required: ['id'],
38
+ },
39
+ },
40
+ },
41
+ {
42
+ type: 'function',
43
+ function: {
44
+ name: 'blast_radius',
45
+ description: 'List every symbol transitively impacted by changing a node id (transitive callers).',
46
+ parameters: {
47
+ type: 'object',
48
+ properties: {
49
+ id: { type: 'string', description: 'node id from find_symbol' },
50
+ depth: { type: 'integer', description: 'max traversal depth (default 10)' },
51
+ },
52
+ required: ['id'],
53
+ },
54
+ },
55
+ },
56
+ {
57
+ type: 'function',
58
+ function: {
59
+ name: 'neighbors',
60
+ description: 'Show the one-hop neighbourhood (incoming and outgoing edges) of a node id.',
61
+ parameters: {
62
+ type: 'object',
63
+ properties: { id: { type: 'string', description: 'node id from find_symbol' } },
64
+ required: ['id'],
65
+ },
66
+ },
67
+ },
68
+ {
69
+ type: 'function',
70
+ function: {
71
+ name: 'dead_exports',
72
+ description: 'List exported symbols that have no inbound references anywhere in the project — prime candidates for safe removal.',
73
+ parameters: { type: 'object', properties: {} },
74
+ },
75
+ },
76
+ {
77
+ type: 'function',
78
+ function: {
79
+ name: 'read_file',
80
+ description: 'Read a project source file (optionally a line range) to see exact text before proposing an edit.',
81
+ parameters: {
82
+ type: 'object',
83
+ properties: {
84
+ path: { type: 'string', description: 'project-relative file path, e.g. src/schema/node.ts' },
85
+ startLine: { type: 'integer', description: 'first line (1-based, optional)' },
86
+ endLine: { type: 'integer', description: 'last line (inclusive, optional)' },
87
+ },
88
+ required: ['path'],
89
+ },
90
+ },
91
+ },
92
+ {
93
+ type: 'function',
94
+ function: {
95
+ name: PROPOSE_TOOL_NAME,
96
+ description: 'Propose ONE safe edit. The harness applies it, runs the TypeScript type-checker, and keeps it only if type-checking passes (otherwise it is reverted and you get the errors). `find` must match the file exactly and uniquely.',
97
+ parameters: {
98
+ type: 'object',
99
+ properties: {
100
+ filePath: { type: 'string', description: 'project-relative file path' },
101
+ find: { type: 'string', description: 'exact text to replace (must be unique in the file)' },
102
+ replace: { type: 'string', description: 'replacement text (empty string to delete)' },
103
+ rationale: { type: 'string', description: 'why this change is safe and beneficial' },
104
+ },
105
+ required: ['filePath', 'find', 'replace', 'rationale'],
106
+ },
107
+ },
108
+ },
109
+ ];
110
+ export class AgentTools {
111
+ constructor(query, rootPath) {
112
+ this.query = query;
113
+ this.rootPath = rootPath;
114
+ }
115
+ async dispatch(name, input) {
116
+ switch (name) {
117
+ case 'find_symbol':
118
+ return AgentTools.stringify(await this.query.find(String(input.name)));
119
+ case 'who_calls':
120
+ return AgentTools.stringify(await this.query.whoCalls(String(input.id)));
121
+ case 'references':
122
+ return AgentTools.stringify(await this.query.references(String(input.id)));
123
+ case 'blast_radius':
124
+ return AgentTools.stringify(await this.query.blastRadius(String(input.id), Number(input.depth ?? 10)));
125
+ case 'neighbors':
126
+ return AgentTools.stringify(await this.query.neighborhood(String(input.id)));
127
+ case 'dead_exports':
128
+ return AgentTools.stringify(await this.query.deadExports());
129
+ case 'read_file':
130
+ return this.readFile(input);
131
+ default:
132
+ return `unknown tool: ${name}`;
133
+ }
134
+ }
135
+ async readFile(input) {
136
+ const absolute = resolve(this.rootPath, String(input.path));
137
+ const content = await readFile(absolute, 'utf8').catch(() => undefined);
138
+ if (content === undefined) {
139
+ return `file not found: ${String(input.path)}`;
140
+ }
141
+ const lines = content.split('\n');
142
+ const start = input.startLine === undefined ? 1 : Number(input.startLine);
143
+ const end = input.endLine === undefined ? lines.length : Number(input.endLine);
144
+ return lines
145
+ .slice(start - 1, end)
146
+ .map((line, index) => `${start + index}\t${line}`)
147
+ .join('\n');
148
+ }
149
+ static stringify(value) {
150
+ return JSON.stringify(value, null, 2);
151
+ }
152
+ }
153
+ //# sourceMappingURL=agent-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tools.js","sourceRoot":"","sources":["../../src/agent/agent-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export type EditRequest = {
2
+ filePath: string;
3
+ find: string;
4
+ replace: string;
5
+ };
6
+ export type EditResult = {
7
+ ok: boolean;
8
+ message: string;
9
+ };
10
+ export declare class CodeEditor {
11
+ private readonly rootPath;
12
+ private readonly backups;
13
+ constructor(rootPath: string);
14
+ apply(request: EditRequest): Promise<EditResult>;
15
+ revert(filePath: string): Promise<void>;
16
+ private static readSafe;
17
+ }
18
+ //# sourceMappingURL=code-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.d.ts","sourceRoot":"","sources":["../../src/agent/code-editor.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;gBAEzC,QAAQ,EAAE,MAAM;IAItB,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAoBhD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;mBAQxB,QAAQ;CAO7B"}
@@ -0,0 +1,43 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ export class CodeEditor {
4
+ constructor(rootPath) {
5
+ this.backups = new Map();
6
+ this.rootPath = rootPath;
7
+ }
8
+ async apply(request) {
9
+ const absolute = resolve(this.rootPath, request.filePath);
10
+ const original = await CodeEditor.readSafe(absolute);
11
+ if (original === undefined) {
12
+ return { ok: false, message: `file not found: ${request.filePath}` };
13
+ }
14
+ const occurrences = original.split(request.find).length - 1;
15
+ if (occurrences === 0) {
16
+ return { ok: false, message: 'find text not found in file' };
17
+ }
18
+ if (occurrences > 1) {
19
+ return { ok: false, message: `find text matched ${occurrences} times; include more surrounding context to make it unique` };
20
+ }
21
+ if (this.backups.has(absolute) === false) {
22
+ this.backups.set(absolute, original);
23
+ }
24
+ await writeFile(absolute, original.replace(request.find, request.replace), 'utf8');
25
+ return { ok: true, message: 'applied' };
26
+ }
27
+ async revert(filePath) {
28
+ const absolute = resolve(this.rootPath, filePath);
29
+ const original = this.backups.get(absolute);
30
+ if (original !== undefined) {
31
+ await writeFile(absolute, original, 'utf8');
32
+ }
33
+ }
34
+ static async readSafe(absolute) {
35
+ try {
36
+ return await readFile(absolute, 'utf8');
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ }
43
+ //# sourceMappingURL=code-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.js","sourceRoot":"","sources":["../../src/agent/code-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,MAAM,OAAO,UAAU;IAItB,YAAY,QAAgB;QAFX,YAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAGpD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;QACtE,CAAC;QACD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5D,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC;QAC9D,CAAC;QACD,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,qBAAqB,WAAW,4DAA4D,EAAE,CAAC;QAC7H,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QACnF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAgB;QAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;IACF,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAgB;QAC7C,IAAI,CAAC;YACJ,OAAO,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;CACD"}
@@ -0,0 +1,30 @@
1
+ import { AgentTools } from './agent-tools.js';
2
+ import { CodeEditor } from './code-editor.js';
3
+ export type AppliedEdit = {
4
+ filePath: string;
5
+ rationale: string;
6
+ };
7
+ export type OptimizeOutcome = {
8
+ applied: AppliedEdit[];
9
+ transcript: string[];
10
+ };
11
+ export type OptimizerParams = {
12
+ tools: AgentTools;
13
+ editor: CodeEditor;
14
+ rootPath: string;
15
+ model: string;
16
+ maxSteps?: number;
17
+ };
18
+ export declare class OptimizerAgent {
19
+ private readonly client;
20
+ private readonly tools;
21
+ private readonly editor;
22
+ private readonly rootPath;
23
+ private readonly model;
24
+ private readonly maxSteps;
25
+ constructor(params: OptimizerParams);
26
+ run(task: string): Promise<OptimizeOutcome>;
27
+ private applyAndVerify;
28
+ private static parseArguments;
29
+ }
30
+ //# sourceMappingURL=optimizer-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimizer-agent.d.ts","sourceRoot":"","sources":["../../src/agent/optimizer-agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAmB9C,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,MAAM,EAAE,eAAe;IAS7B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;YA0CnC,cAAc;IA4B5B,OAAO,CAAC,MAAM,CAAC,cAAc;CAW7B"}
@@ -0,0 +1,97 @@
1
+ import OpenAI from 'openai';
2
+ import { AGENT_TOOLS, PROPOSE_TOOL_NAME } from './agent-tools.js';
3
+ import { Verifier } from './verifier.js';
4
+ const SYSTEM_PROMPT = `You are an autonomous TypeScript optimization agent working on a real codebase.
5
+
6
+ You have a code knowledge graph at your disposal through tools. Use it as your eyes.
7
+
8
+ Method (follow it):
9
+ 1. Find a candidate. Dead code is the safest win — call dead_exports first, or find_symbol for a named target.
10
+ 2. Understand the blast radius. Before proposing ANY change you MUST confirm safety with references / who_calls / blast_radius. A symbol is safe to remove only when it has zero inbound references.
11
+ 3. Read the exact text with read_file so your edit matches the file precisely.
12
+ 4. Propose exactly ONE edit via ${PROPOSE_TOOL_NAME}. The harness type-checks it and keeps it only if the check passes; on failure you receive the compiler errors and must fix or abandon.
13
+
14
+ Rules:
15
+ - ids come from tool results; never invent them.
16
+ - Act autonomously — do not ask the user questions. Make the call yourself.
17
+ - Prefer removing genuinely dead exports or behavior-preserving simplifications. Never change observable behavior.
18
+ - When you have applied a verified improvement (or concluded there is no safe one), stop and summarize.`;
19
+ export class OptimizerAgent {
20
+ constructor(params) {
21
+ this.client = new OpenAI();
22
+ this.tools = params.tools;
23
+ this.editor = params.editor;
24
+ this.rootPath = params.rootPath;
25
+ this.model = params.model;
26
+ this.maxSteps = params.maxSteps ?? 12;
27
+ }
28
+ async run(task) {
29
+ const messages = [
30
+ { role: 'system', content: SYSTEM_PROMPT },
31
+ { role: 'user', content: task },
32
+ ];
33
+ const applied = [];
34
+ const transcript = [];
35
+ for (let step = 0; step < this.maxSteps; step += 1) {
36
+ const completion = await this.client.chat.completions.create({
37
+ model: this.model,
38
+ messages,
39
+ tools: AGENT_TOOLS,
40
+ });
41
+ const message = completion.choices[0].message;
42
+ messages.push(message);
43
+ if (typeof message.content === 'string' && message.content.length > 0) {
44
+ transcript.push(message.content);
45
+ }
46
+ const toolCalls = message.tool_calls ?? [];
47
+ if (toolCalls.length === 0) {
48
+ break;
49
+ }
50
+ for (const call of toolCalls) {
51
+ if (call.type !== 'function') {
52
+ messages.push({ role: 'tool', tool_call_id: call.id, content: 'unsupported tool call type' });
53
+ continue;
54
+ }
55
+ const input = OptimizerAgent.parseArguments(call.function.arguments);
56
+ const content = call.function.name === PROPOSE_TOOL_NAME
57
+ ? await this.applyAndVerify(input, applied, transcript)
58
+ : await this.tools.dispatch(call.function.name, input);
59
+ messages.push({ role: 'tool', tool_call_id: call.id, content });
60
+ }
61
+ }
62
+ return { applied, transcript };
63
+ }
64
+ async applyAndVerify(input, applied, transcript) {
65
+ const request = {
66
+ filePath: String(input.filePath),
67
+ find: String(input.find),
68
+ replace: String(input.replace),
69
+ };
70
+ const rationale = String(input.rationale ?? '');
71
+ const edit = await this.editor.apply(request);
72
+ if (edit.ok === false) {
73
+ return `EDIT REJECTED: ${edit.message}`;
74
+ }
75
+ const verify = await Verifier.typecheck(this.rootPath);
76
+ if (verify.ok === false) {
77
+ await this.editor.revert(request.filePath);
78
+ return `TYPECHECK FAILED — change reverted. Fix the approach or abandon it. Compiler output:\n${verify.output}`;
79
+ }
80
+ applied.push({ filePath: request.filePath, rationale });
81
+ transcript.push(`APPLIED ${request.filePath} — ${rationale}`);
82
+ return 'VERIFIED: the type-checker passed and the edit was kept.';
83
+ }
84
+ static parseArguments(raw) {
85
+ try {
86
+ const parsed = JSON.parse(raw);
87
+ if (typeof parsed === 'object' && parsed !== null) {
88
+ return parsed;
89
+ }
90
+ return {};
91
+ }
92
+ catch {
93
+ return {};
94
+ }
95
+ }
96
+ }
97
+ //# sourceMappingURL=optimizer-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimizer-agent.js","sourceRoot":"","sources":["../../src/agent/optimizer-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type VerifyResult = {
2
+ ok: boolean;
3
+ output: string;
4
+ };
5
+ export declare class Verifier {
6
+ static typecheck(rootPath: string): Promise<VerifyResult>;
7
+ private static runCommand;
8
+ }
9
+ //# sourceMappingURL=verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/agent/verifier.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,YAAY,GAAG;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,qBAAa,QAAQ;WACP,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;mBAI1C,UAAU;CAS/B"}
@@ -0,0 +1,19 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const run = promisify(exec);
4
+ export class Verifier {
5
+ static async typecheck(rootPath) {
6
+ return Verifier.runCommand('npx tsc --noEmit', rootPath);
7
+ }
8
+ static async runCommand(command, cwd) {
9
+ try {
10
+ const { stdout, stderr } = await run(command, { cwd, maxBuffer: 10 * 1024 * 1024 });
11
+ return { ok: true, output: `${stdout}${stderr}`.trim() };
12
+ }
13
+ catch (error) {
14
+ const shaped = error;
15
+ return { ok: false, output: `${shaped.stdout ?? ''}${shaped.stderr ?? ''}`.trim() };
16
+ }
17
+ }
18
+ }
19
+ //# sourceMappingURL=verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../src/agent/verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAO5B,MAAM,OAAO,QAAQ;IACpB,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAgB;QACtC,OAAO,QAAQ,CAAC,UAAU,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAEO,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,GAAW;QAC3D,IAAI,CAAC;YACJ,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;YACpF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,KAA6C,CAAC;YAC7D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QACrF,CAAC;IACF,CAAC;CACD"}