ts-knowledge-graph 0.1.1 → 0.1.2

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 (172) hide show
  1. package/README.md +34 -13
  2. package/contribs/web_visualisation/README.md +55 -0
  3. package/contribs/web_visualisation/web/css/style.css +115 -0
  4. package/contribs/web_visualisation/web/data/.gitignore +2 -0
  5. package/contribs/web_visualisation/web/index.html +58 -0
  6. package/contribs/web_visualisation/web/js/app.js +364 -0
  7. package/dist/agent/agent_tools.d.ts +13 -0
  8. package/dist/agent/agent_tools.d.ts.map +1 -0
  9. package/dist/agent/agent_tools.js +153 -0
  10. package/dist/agent/agent_tools.js.map +1 -0
  11. package/dist/agent/code_editor.d.ts +18 -0
  12. package/dist/agent/code_editor.d.ts.map +1 -0
  13. package/dist/agent/code_editor.js +43 -0
  14. package/dist/agent/code_editor.js.map +1 -0
  15. package/dist/agent/optimizer_agent.d.ts +30 -0
  16. package/dist/agent/optimizer_agent.d.ts.map +1 -0
  17. package/dist/agent/optimizer_agent.js +97 -0
  18. package/dist/agent/optimizer_agent.js.map +1 -0
  19. package/dist/cli.d.ts +0 -9
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +24 -208
  22. package/dist/cli.js.map +1 -1
  23. package/dist/commands/blast-radius.d.ts +5 -0
  24. package/dist/commands/blast-radius.d.ts.map +1 -0
  25. package/dist/commands/blast-radius.js +18 -0
  26. package/dist/commands/blast-radius.js.map +1 -0
  27. package/dist/commands/blast_radius.d.ts +5 -0
  28. package/dist/commands/blast_radius.d.ts.map +1 -0
  29. package/dist/commands/blast_radius.js +18 -0
  30. package/dist/commands/blast_radius.js.map +1 -0
  31. package/dist/commands/blast_radius_command.d.ts +5 -0
  32. package/dist/commands/blast_radius_command.d.ts.map +1 -0
  33. package/dist/commands/blast_radius_command.js +18 -0
  34. package/dist/commands/blast_radius_command.js.map +1 -0
  35. package/dist/commands/calls.d.ts +5 -0
  36. package/dist/commands/calls.d.ts.map +1 -0
  37. package/dist/commands/calls.js +7 -0
  38. package/dist/commands/calls.js.map +1 -0
  39. package/dist/commands/calls_command.d.ts +5 -0
  40. package/dist/commands/calls_command.d.ts.map +1 -0
  41. package/dist/commands/calls_command.js +7 -0
  42. package/dist/commands/calls_command.js.map +1 -0
  43. package/dist/commands/command-helpers.d.ts +15 -0
  44. package/dist/commands/command-helpers.d.ts.map +1 -0
  45. package/dist/commands/command-helpers.js +61 -0
  46. package/dist/commands/command-helpers.js.map +1 -0
  47. package/dist/commands/command_helpers.d.ts +15 -0
  48. package/dist/commands/command_helpers.d.ts.map +1 -0
  49. package/dist/commands/command_helpers.js +61 -0
  50. package/dist/commands/command_helpers.js.map +1 -0
  51. package/dist/commands/dead-exports.d.ts +5 -0
  52. package/dist/commands/dead-exports.d.ts.map +1 -0
  53. package/dist/commands/dead-exports.js +7 -0
  54. package/dist/commands/dead-exports.js.map +1 -0
  55. package/dist/commands/dead_exports.d.ts +5 -0
  56. package/dist/commands/dead_exports.d.ts.map +1 -0
  57. package/dist/commands/dead_exports.js +7 -0
  58. package/dist/commands/dead_exports.js.map +1 -0
  59. package/dist/commands/dead_exports_command.d.ts +5 -0
  60. package/dist/commands/dead_exports_command.d.ts.map +1 -0
  61. package/dist/commands/dead_exports_command.js +7 -0
  62. package/dist/commands/dead_exports_command.js.map +1 -0
  63. package/dist/commands/extract.d.ts +8 -0
  64. package/dist/commands/extract.d.ts.map +1 -0
  65. package/dist/commands/extract.js +49 -0
  66. package/dist/commands/extract.js.map +1 -0
  67. package/dist/commands/extract_command.d.ts +8 -0
  68. package/dist/commands/extract_command.d.ts.map +1 -0
  69. package/dist/commands/extract_command.js +49 -0
  70. package/dist/commands/extract_command.js.map +1 -0
  71. package/dist/commands/find.d.ts +5 -0
  72. package/dist/commands/find.d.ts.map +1 -0
  73. package/dist/commands/find.js +7 -0
  74. package/dist/commands/find.js.map +1 -0
  75. package/dist/commands/find_command.d.ts +5 -0
  76. package/dist/commands/find_command.d.ts.map +1 -0
  77. package/dist/commands/find_command.js +7 -0
  78. package/dist/commands/find_command.js.map +1 -0
  79. package/dist/commands/install_command.d.ts +16 -0
  80. package/dist/commands/install_command.d.ts.map +1 -0
  81. package/dist/commands/install_command.js +42 -0
  82. package/dist/commands/install_command.js.map +1 -0
  83. package/dist/commands/load.d.ts +6 -0
  84. package/dist/commands/load.d.ts.map +1 -0
  85. package/dist/commands/load.js +28 -0
  86. package/dist/commands/load.js.map +1 -0
  87. package/dist/commands/load_command.d.ts +6 -0
  88. package/dist/commands/load_command.d.ts.map +1 -0
  89. package/dist/commands/load_command.js +28 -0
  90. package/dist/commands/load_command.js.map +1 -0
  91. package/dist/commands/neighbors.d.ts +5 -0
  92. package/dist/commands/neighbors.d.ts.map +1 -0
  93. package/dist/commands/neighbors.js +17 -0
  94. package/dist/commands/neighbors.js.map +1 -0
  95. package/dist/commands/neighbors_command.d.ts +5 -0
  96. package/dist/commands/neighbors_command.d.ts.map +1 -0
  97. package/dist/commands/neighbors_command.js +17 -0
  98. package/dist/commands/neighbors_command.js.map +1 -0
  99. package/dist/commands/optimize.d.ts +6 -0
  100. package/dist/commands/optimize.d.ts.map +1 -0
  101. package/dist/commands/optimize.js +59 -0
  102. package/dist/commands/optimize.js.map +1 -0
  103. package/dist/commands/optimize_command.d.ts +6 -0
  104. package/dist/commands/optimize_command.d.ts.map +1 -0
  105. package/dist/commands/optimize_command.js +59 -0
  106. package/dist/commands/optimize_command.js.map +1 -0
  107. package/dist/commands/references.d.ts +5 -0
  108. package/dist/commands/references.d.ts.map +1 -0
  109. package/dist/commands/references.js +17 -0
  110. package/dist/commands/references.js.map +1 -0
  111. package/dist/commands/references_command.d.ts +5 -0
  112. package/dist/commands/references_command.d.ts.map +1 -0
  113. package/dist/commands/references_command.js +17 -0
  114. package/dist/commands/references_command.js.map +1 -0
  115. package/dist/commands/web.d.ts +19 -0
  116. package/dist/commands/web.d.ts.map +1 -0
  117. package/dist/commands/web.js +120 -0
  118. package/dist/commands/web.js.map +1 -0
  119. package/dist/commands/web_command.d.ts +19 -0
  120. package/dist/commands/web_command.d.ts.map +1 -0
  121. package/dist/commands/web_command.js +120 -0
  122. package/dist/commands/web_command.js.map +1 -0
  123. package/dist/commands/who-calls.d.ts +5 -0
  124. package/dist/commands/who-calls.d.ts.map +1 -0
  125. package/dist/commands/who-calls.js +7 -0
  126. package/dist/commands/who-calls.js.map +1 -0
  127. package/dist/commands/who_calls.d.ts +5 -0
  128. package/dist/commands/who_calls.d.ts.map +1 -0
  129. package/dist/commands/who_calls.js +7 -0
  130. package/dist/commands/who_calls.js.map +1 -0
  131. package/dist/commands/who_calls_command.d.ts +5 -0
  132. package/dist/commands/who_calls_command.d.ts.map +1 -0
  133. package/dist/commands/who_calls_command.js +7 -0
  134. package/dist/commands/who_calls_command.js.map +1 -0
  135. package/dist/extract/graph_builder.d.ts +16 -0
  136. package/dist/extract/graph_builder.d.ts.map +1 -0
  137. package/dist/extract/graph_builder.js +39 -0
  138. package/dist/extract/graph_builder.js.map +1 -0
  139. package/dist/extract/node_id.d.ts +8 -0
  140. package/dist/extract/node_id.d.ts.map +1 -0
  141. package/dist/extract/node_id.js +22 -0
  142. package/dist/extract/node_id.js.map +1 -0
  143. package/dist/extract/project_loader.d.ts +5 -0
  144. package/dist/extract/project_loader.d.ts.map +1 -0
  145. package/dist/extract/project_loader.js +19 -0
  146. package/dist/extract/project_loader.js.map +1 -0
  147. package/dist/extract/semantic_extractor.d.ts +22 -0
  148. package/dist/extract/semantic_extractor.d.ts.map +1 -0
  149. package/dist/extract/semantic_extractor.js +254 -0
  150. package/dist/extract/semantic_extractor.js.map +1 -0
  151. package/dist/extract/structural_extractor.d.ts +18 -0
  152. package/dist/extract/structural_extractor.d.ts.map +1 -0
  153. package/dist/extract/structural_extractor.js +97 -0
  154. package/dist/extract/structural_extractor.js.map +1 -0
  155. package/dist/query/graph_query.d.ts +28 -0
  156. package/dist/query/graph_query.d.ts.map +1 -0
  157. package/dist/query/graph_query.js +93 -0
  158. package/dist/query/graph_query.js.map +1 -0
  159. package/dist/store/jsonl_reader.d.ts +11 -0
  160. package/dist/store/jsonl_reader.d.ts.map +1 -0
  161. package/dist/store/jsonl_reader.js +19 -0
  162. package/dist/store/jsonl_reader.js.map +1 -0
  163. package/dist/store/jsonl_store.d.ts +7 -0
  164. package/dist/store/jsonl_store.d.ts.map +1 -0
  165. package/dist/store/jsonl_store.js +13 -0
  166. package/dist/store/jsonl_store.js.map +1 -0
  167. package/dist/store/kuzu_store.d.ts +20 -0
  168. package/dist/store/kuzu_store.d.ts.map +1 -0
  169. package/dist/store/kuzu_store.js +66 -0
  170. package/dist/store/kuzu_store.js.map +1 -0
  171. package/package.json +6 -2
  172. package/skills/ts-knowledge-graph/SKILL.md +91 -0
@@ -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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"}
package/dist/cli.d.ts CHANGED
@@ -1,14 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  export declare class Cli {
3
3
  static run(argv: string[]): void;
4
- private static registerQuery;
5
- private static extract;
6
- private static load;
7
- private static optimize;
8
- private static withQuery;
9
- private static printRefs;
10
- private static printNeighbors;
11
- private static printBreakdown;
12
- private static countBy;
13
4
  }
14
5
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAmCA,qBAAa,GAAG;IACf,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAoFhC,OAAO,CAAC,MAAM,CAAC,aAAa;mBAoBP,OAAO;mBAkBP,IAAI;mBAWJ,QAAQ;mBAuCR,SAAS;IAU9B,OAAO,CAAC,MAAM,CAAC,SAAS;IAexB,OAAO,CAAC,MAAM,CAAC,cAAc;IAgB7B,OAAO,CAAC,MAAM,CAAC,cAAc;IAW7B,OAAO,CAAC,MAAM,CAAC,OAAO;CAOtB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAeA,qBAAa,GAAG;IACf,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAqBhC"}
package/dist/cli.js CHANGED
@@ -1,221 +1,37 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync } from 'node:fs';
3
- import { resolve } from 'node:path';
4
- import chalk from 'chalk';
5
2
  import { Command } from 'commander';
6
- import { AgentTools } from './agent/agent-tools.js';
7
- import { CodeEditor } from './agent/code-editor.js';
8
- import { OptimizerAgent } from './agent/optimizer-agent.js';
9
- import { GraphBuilder } from './extract/graph-builder.js';
10
- import { ProjectLoader } from './extract/project-loader.js';
11
- import { GraphQuery } from './query/graph-query.js';
12
- import { JsonlReader } from './store/jsonl-reader.js';
13
- import { JsonlStore } from './store/jsonl-store.js';
14
- import { KuzuStore } from './store/kuzu-store.js';
15
- const DEFAULT_TASK = 'Find one genuinely dead exported symbol using dead_exports, confirm with references that it has zero inbound references, then remove it safely.';
16
- const DEFAULT_DB_PATH = './outputs/graph.kuzu';
17
- const DEFAULT_GRAPH_DIR = './outputs/graph';
3
+ import { BlastRadiusCommand } from './commands/blast_radius_command.js';
4
+ import { CallsCommand } from './commands/calls_command.js';
5
+ import { DeadExportsCommand } from './commands/dead_exports_command.js';
6
+ import { ExtractCommand } from './commands/extract_command.js';
7
+ import { FindCommand } from './commands/find_command.js';
8
+ import { InstallCommand } from './commands/install_command.js';
9
+ import { LoadCommand } from './commands/load_command.js';
10
+ import { NeighborsCommand } from './commands/neighbors_command.js';
11
+ import { OptimizeCommand } from './commands/optimize_command.js';
12
+ import { ReferencesCommand } from './commands/references_command.js';
13
+ import { WebCommand } from './commands/web_command.js';
14
+ import { WhoCallsCommand } from './commands/who_calls_command.js';
18
15
  export class Cli {
19
16
  static run(argv) {
20
17
  const program = new Command();
21
18
  program
22
19
  .name('ts-knowledge-graph')
23
20
  .description('Parse a TypeScript project into a knowledge graph and query it');
24
- program
25
- .command('extract')
26
- .argument('<root>', 'path to the TypeScript project to parse')
27
- .option('-o, --out <dir>', 'output directory for the JSONL graph', DEFAULT_GRAPH_DIR)
28
- .option('--semantic', 'resolve heritage and CALLS edges (slower)', false)
29
- .action(async (root, options) => {
30
- await Cli.extract(root, options);
31
- });
32
- program
33
- .command('load')
34
- .description('load a JSONL graph into an embedded Kùzu database')
35
- .argument('[graphDir]', 'directory holding nodes.jsonl and edges.jsonl', DEFAULT_GRAPH_DIR)
36
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
37
- .action(async (graphDir, options) => {
38
- await Cli.load(graphDir, options.db);
39
- });
40
- Cli.registerQuery(program, 'find', '<pattern>', 'find symbols whose name contains <pattern>', (query, arg) => query.find(arg));
41
- Cli.registerQuery(program, 'who-calls', '<id>', 'list symbols that call <id>', (query, arg) => query.whoCalls(arg));
42
- Cli.registerQuery(program, 'calls', '<id>', 'list symbols that <id> calls', (query, arg) => query.calls(arg));
43
- Cli.registerQuery(program, 'dead-exports', '', 'list exported symbols with no inbound references', (query) => query.deadExports());
44
- program
45
- .command('blast-radius')
46
- .description('list every symbol transitively impacted by changing <id>')
47
- .argument('<id>', 'node id to analyse')
48
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
49
- .option('--depth <n>', 'maximum traversal depth', '10')
50
- .option('--json', 'emit raw JSON', false)
51
- .action(async (id, options) => {
52
- await Cli.withQuery(options.db, async (query) => {
53
- Cli.printRefs(await query.blastRadius(id, Number(options.depth)), options.json === true);
54
- });
55
- });
56
- program
57
- .command('neighbors')
58
- .description('show the one-hop neighbourhood of <id>')
59
- .argument('<id>', 'node id to inspect')
60
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
61
- .option('--json', 'emit raw JSON', false)
62
- .action(async (id, options) => {
63
- await Cli.withQuery(options.db, async (query) => {
64
- Cli.printNeighbors(await query.neighborhood(id), options.json === true);
65
- });
66
- });
67
- program
68
- .command('references')
69
- .description('list everything that references <id> (calls, type usage, heritage, new)')
70
- .argument('<id>', 'node id to inspect')
71
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
72
- .option('--json', 'emit raw JSON', false)
73
- .action(async (id, options) => {
74
- await Cli.withQuery(options.db, async (query) => {
75
- Cli.printNeighbors(await query.references(id), options.json === true);
76
- });
77
- });
78
- program
79
- .command('optimize')
80
- .description('run the autonomous optimization agent against the loaded graph')
81
- .argument('[task]', 'what the agent should try to optimize', DEFAULT_TASK)
82
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
83
- .option('-m, --model <name>', 'model name (defaults to OPENAI_MODEL)')
84
- .option('--max-steps <n>', 'maximum agent steps', '12')
85
- .action(async (task, options) => {
86
- await Cli.optimize(task, options);
87
- });
21
+ ExtractCommand.register(program);
22
+ LoadCommand.register(program);
23
+ FindCommand.register(program);
24
+ WhoCallsCommand.register(program);
25
+ CallsCommand.register(program);
26
+ DeadExportsCommand.register(program);
27
+ BlastRadiusCommand.register(program);
28
+ NeighborsCommand.register(program);
29
+ ReferencesCommand.register(program);
30
+ OptimizeCommand.register(program);
31
+ WebCommand.register(program);
32
+ InstallCommand.register(program);
88
33
  void program.parseAsync(argv);
89
34
  }
90
- static registerQuery(program, name, argSpec, description, run) {
91
- const command = program.command(argSpec === '' ? name : `${name} ${argSpec}`).description(description);
92
- command
93
- .option('-d, --db <path>', 'Kùzu database path', DEFAULT_DB_PATH)
94
- .option('--json', 'emit raw JSON', false)
95
- .action(async (...args) => {
96
- const options = args[args.length - 2];
97
- const arg = argSpec === '' ? '' : args[0];
98
- await Cli.withQuery(options.db, async (query) => {
99
- Cli.printRefs(await run(query, arg), options.json === true);
100
- });
101
- });
102
- }
103
- static async extract(root, options) {
104
- const rootPath = resolve(root);
105
- const outPath = resolve(options.out);
106
- console.log(chalk.cyan(`Loading project at ${rootPath} ...`));
107
- const project = ProjectLoader.load(rootPath);
108
- const builder = new GraphBuilder();
109
- builder.build(project, rootPath, { semantic: options.semantic });
110
- const nodes = builder.getNodes();
111
- const edges = builder.getEdges();
112
- await JsonlStore.write(outPath, nodes, edges);
113
- console.log(chalk.green(`✓ ${nodes.length} nodes, ${edges.length} edges -> ${outPath}`));
114
- Cli.printBreakdown(nodes, edges);
115
- }
116
- static async load(graphDir, dbPath) {
117
- const resolvedDb = resolve(dbPath);
118
- console.log(chalk.cyan(`Loading ${resolve(graphDir)} into ${resolvedDb} ...`));
119
- const { nodes, edges } = await JsonlReader.read(resolve(graphDir));
120
- const store = new KuzuStore(resolvedDb);
121
- await store.initSchema();
122
- await store.load(nodes, edges);
123
- await store.close();
124
- console.log(chalk.green(`✓ loaded ${nodes.length} nodes, ${edges.length} edges`));
125
- }
126
- static async optimize(task, options) {
127
- if (existsSync('.env') === true) {
128
- process.loadEnvFile('.env');
129
- }
130
- if (process.env.OPENAI_API_KEY === undefined) {
131
- console.log(chalk.red('Set OPENAI_API_KEY before running the optimizer — copy .env-sample to .env and pick a provider.'));
132
- return;
133
- }
134
- const model = options.model ?? process.env.OPENAI_MODEL;
135
- if (model === undefined) {
136
- console.log(chalk.red('Set OPENAI_MODEL in .env (or pass --model) — see .env-sample for per-provider examples.'));
137
- return;
138
- }
139
- const rootPath = process.cwd();
140
- await Cli.withQuery(options.db, async (query) => {
141
- const agent = new OptimizerAgent({
142
- tools: new AgentTools(query, rootPath),
143
- editor: new CodeEditor(rootPath),
144
- rootPath,
145
- model,
146
- maxSteps: Number(options.maxSteps),
147
- });
148
- console.log(chalk.gray(`Model: ${model}${process.env.OPENAI_BASE_URL === undefined ? '' : ` @ ${process.env.OPENAI_BASE_URL}`}`));
149
- console.log(chalk.cyan(`Task: ${task}\n`));
150
- const outcome = await agent.run(task);
151
- for (const line of outcome.transcript) {
152
- console.log(chalk.gray(line));
153
- }
154
- console.log(chalk.bold(`\nApplied ${outcome.applied.length} verified edit(s):`));
155
- for (const edit of outcome.applied) {
156
- console.log(` ${chalk.green('✓')} ${edit.filePath} — ${edit.rationale}`);
157
- }
158
- if (outcome.applied.length === 0) {
159
- console.log(chalk.yellow(' (none — the agent found no safe change, or reverted what it tried)'));
160
- }
161
- });
162
- }
163
- static async withQuery(dbPath, fn) {
164
- const store = new KuzuStore(resolve(dbPath));
165
- await store.initSchema();
166
- try {
167
- await fn(new GraphQuery(store));
168
- }
169
- finally {
170
- await store.close();
171
- }
172
- }
173
- static printRefs(refs, json) {
174
- if (json === true) {
175
- console.log(JSON.stringify(refs, null, 2));
176
- return;
177
- }
178
- if (refs.length === 0) {
179
- console.log(chalk.yellow('(no results)'));
180
- return;
181
- }
182
- for (const ref of refs) {
183
- console.log(`${chalk.gray(ref.kind.padEnd(14))} ${chalk.bold(ref.name)} ${chalk.gray(`${ref.filePath}:${ref.startLine}`)}`);
184
- }
185
- console.log(chalk.gray(`\n${refs.length} result(s)`));
186
- }
187
- static printNeighbors(neighbors, json) {
188
- if (json === true) {
189
- console.log(JSON.stringify(neighbors, null, 2));
190
- return;
191
- }
192
- if (neighbors.length === 0) {
193
- console.log(chalk.yellow('(no neighbours)'));
194
- return;
195
- }
196
- for (const neighbor of neighbors) {
197
- const arrow = neighbor.direction === 'out' ? '->' : '<-';
198
- console.log(`${chalk.cyan(arrow)} ${chalk.gray(neighbor.edgeKind.padEnd(12))} ${chalk.bold(neighbor.name)} ${chalk.gray(`${neighbor.filePath}:${neighbor.startLine}`)}`);
199
- }
200
- console.log(chalk.gray(`\n${neighbors.length} edge(s)`));
201
- }
202
- static printBreakdown(nodes, edges) {
203
- console.log(chalk.bold('\nNodes'));
204
- for (const [kind, count] of Cli.countBy(nodes.map((node) => node.kind))) {
205
- console.log(` ${kind.padEnd(16)} ${count}`);
206
- }
207
- console.log(chalk.bold('\nEdges'));
208
- for (const [kind, count] of Cli.countBy(edges.map((edge) => edge.kind))) {
209
- console.log(` ${kind.padEnd(16)} ${count}`);
210
- }
211
- }
212
- static countBy(values) {
213
- const counts = new Map();
214
- for (const value of values) {
215
- counts.set(value, (counts.get(value) ?? 0) + 1);
216
- }
217
- return [...counts.entries()].sort((a, b) => b[1] - a[1]);
218
- }
219
35
  }
220
36
  Cli.run(process.argv);
221
37
  //# sourceMappingURL=cli.js.map