@polycode-projects/the-mechanical-code-talker 1.12.0 → 2.0.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 (148) hide show
  1. package/README.md +244 -48
  2. package/ROADMAP.md +23 -34
  3. package/bin/tmct.mjs +107 -71
  4. package/corpus/LICENSES.json +118 -0
  5. package/corpus/README.md +17 -13
  6. package/corpus/conceptnet/README.md +5 -5
  7. package/corpus/conceptnet/fetch-slice.mjs +1 -1
  8. package/corpus/conceptnet/filter-dump.mjs +1 -1
  9. package/corpus/generated/README.md +9 -10
  10. package/corpus/namenet/README.md +39 -0
  11. package/corpus/seon/README.md +2 -2
  12. package/corpus/tier2/generate.mjs +58 -9
  13. package/corpus/tier2/manifest.json +44 -0
  14. package/corpus/wordnet/README.md +37 -0
  15. package/corpus/wordnet/generate.mjs +1 -1
  16. package/data/templates/constructions/agent-noun-relations.toml +2 -2
  17. package/data/templates/grammar-rules.toml +1 -1
  18. package/package.json +13 -22
  19. package/src/{ask-nlp.mjs → adapters/ask-nlp.mjs} +1 -1
  20. package/src/{config.mjs → adapters/config.mjs} +1 -1
  21. package/src/{corpus → adapters/corpus}/conceptnet-map.toml +4 -4
  22. package/src/{corpus → adapters/corpus}/conceptnet.mjs +3 -3
  23. package/src/adapters/corpus/construction-banks.mjs +43 -0
  24. package/src/{corpus → adapters/corpus}/templates.mjs +1 -1
  25. package/src/{embed.mjs → adapters/embed.mjs} +1 -11
  26. package/src/{graph-build.mjs → adapters/graph-build.mjs} +6 -6
  27. package/src/{memory → adapters/memory}/blocks.mjs +2 -2
  28. package/src/{memory → adapters/memory}/core.mjs +38 -94
  29. package/src/adapters/prose-tokens.mjs +98 -0
  30. package/src/{providers → adapters/providers}/bootstrap.mjs +2 -2
  31. package/src/{providers → adapters/providers}/fixture.mjs +3 -3
  32. package/src/{providers → adapters/providers}/graph-service.mjs +9 -4
  33. package/src/{source-slice.mjs → adapters/source-slice.mjs} +2 -2
  34. package/src/{source.mjs → adapters/source.mjs} +1 -1
  35. package/src/{toml-config.mjs → adapters/toml-config.mjs} +1 -1
  36. package/src/{answer-variants.json → domain/answer-variants.json} +1 -1
  37. package/src/domain/answer-variants.mjs +23 -0
  38. package/src/{ask-vocab.mjs → domain/ask-vocab.mjs} +37 -5
  39. package/src/{ask.mjs → domain/ask.mjs} +216 -52
  40. package/src/{codegraph.mjs → domain/codegraph.mjs} +31 -315
  41. package/src/{completions → domain/completions}/complete.mjs +16 -10
  42. package/src/{completions → domain/completions}/graph-adapter.mjs +10 -4
  43. package/src/{completions → domain/completions}/group.mjs +14 -6
  44. package/src/{completions → domain/completions}/infer.mjs +57 -39
  45. package/src/domain/completions/injected.mjs +21 -0
  46. package/src/{completions → domain/completions}/rank.mjs +15 -8
  47. package/src/{completions → domain/completions}/search.mjs +4 -2
  48. package/src/{grammar → domain/grammar}/ace.mjs +3 -3
  49. package/src/{grammar → domain/grammar}/assert.mjs +12 -8
  50. package/src/{grammar → domain/grammar}/lexicon-core.json +1 -1
  51. package/src/{grammar → domain/grammar}/lexicon.mjs +4 -6
  52. package/src/domain/hash.mjs +147 -0
  53. package/src/{interpret → domain/interpret}/fuzzy.mjs +42 -4
  54. package/src/domain/interpret/nlp-registry.mjs +20 -0
  55. package/src/{interpret → domain/interpret}/normalize.mjs +35 -5
  56. package/src/{interpret → domain/interpret}/pipeline.mjs +1 -5
  57. package/src/{interpret → domain/interpret}/strategies/ace.mjs +1 -1
  58. package/src/{interpret → domain/interpret}/strategies/constructions.mjs +30 -52
  59. package/src/{interpret → domain/interpret}/strategies/keywords.mjs +48 -26
  60. package/src/domain/memory/capability.mjs +235 -0
  61. package/src/domain/memory/fold.mjs +54 -0
  62. package/src/domain/memory/session-turns.mjs +7 -0
  63. package/src/{memory → domain/memory}/trust.mjs +48 -0
  64. package/src/{paraphrase.mjs → domain/paraphrase.mjs} +2 -2
  65. package/src/{prose.mjs → domain/prose.mjs} +1 -1
  66. package/src/domain/real-word-collisions.json +1 -0
  67. package/src/{router → domain/router}/call-validator.mjs +1 -1
  68. package/src/{router → domain/router}/drive.mjs +34 -25
  69. package/src/{router → domain/router}/goal-reasoner.mjs +1 -1
  70. package/src/{router → domain/router}/guardrail.mjs +1 -1
  71. package/src/{router → domain/router}/planner.mjs +1 -1
  72. package/src/{router → domain/router}/registry.mjs +5 -5
  73. package/src/{router → domain/router}/resolver.mjs +16 -13
  74. package/src/{router → domain/router}/results.mjs +1 -1
  75. package/src/{router → domain/router}/set-algebra.mjs +1 -1
  76. package/src/{router → domain/router}/taught.mjs +10 -9
  77. package/src/{syllogise.mjs → domain/syllogise.mjs} +21 -4
  78. package/src/domain/vector.mjs +12 -0
  79. package/src/services/chat-session.mjs +451 -0
  80. package/src/{chat.mjs → services/chat.mjs} +1209 -684
  81. package/src/{cli-args.mjs → services/cli-args.mjs} +2 -2
  82. package/src/services/completions.mjs +55 -0
  83. package/src/{extensions.mjs → services/extensions.mjs} +7 -7
  84. package/src/{finish.mjs → services/finish.mjs} +2 -2
  85. package/src/{memory → services}/fold.mjs +0 -0
  86. package/src/{import-file.mjs → services/import-file.mjs} +3 -3
  87. package/src/{index.mjs → services/index.mjs} +21 -12
  88. package/src/{init.mjs → services/init.mjs} +9 -9
  89. package/src/{ledger-viz.mjs → services/ledger-viz.mjs} +3 -3
  90. package/src/{plan-viz.mjs → services/plan-viz.mjs} +65 -26
  91. package/src/{sentences.mjs → services/sentences.mjs} +1 -1
  92. package/src/{sessions.mjs → services/sessions.mjs} +4 -5
  93. package/src/{telemetry.mjs → services/telemetry.mjs} +1 -1
  94. package/src/{server-http.mjs → surfaces/http/server-http.mjs} +11 -65
  95. package/src/{tui → surfaces/tui}/app.mjs +3 -3
  96. package/src/{memory-ask-browser-entry.mjs → surfaces/web/memory-ask-browser-entry.mjs} +5 -5
  97. package/src/{memory-ask-browser.bundle.js → surfaces/web/memory-ask-browser.bundle.js} +9465 -6366
  98. package/src/tools/catalog.mjs +29 -0
  99. package/src/{conformance.mjs → tools/conformance.mjs} +2 -2
  100. package/src/tools/definitions.mjs +288 -0
  101. package/src/tools/graph-load.mjs +20 -0
  102. package/src/tools/handlers/index.mjs +54 -0
  103. package/src/tools/handlers/kit.mjs +33 -0
  104. package/src/tools/handlers/tmct-architecture.mjs +7 -0
  105. package/src/tools/handlers/tmct-ask.mjs +14 -0
  106. package/src/tools/handlers/tmct-callees.mjs +6 -0
  107. package/src/tools/handlers/tmct-callers.mjs +6 -0
  108. package/src/tools/handlers/tmct-calls.mjs +6 -0
  109. package/src/tools/handlers/tmct-class-history.mjs +6 -0
  110. package/src/tools/handlers/tmct-cochanges.mjs +6 -0
  111. package/src/tools/handlers/tmct-context-more.mjs +9 -0
  112. package/src/tools/handlers/tmct-context.mjs +163 -0
  113. package/src/tools/handlers/tmct-describe.mjs +15 -0
  114. package/src/tools/handlers/tmct-exports.mjs +9 -0
  115. package/src/tools/handlers/tmct-file-history.mjs +6 -0
  116. package/src/tools/handlers/tmct-history.mjs +6 -0
  117. package/src/tools/handlers/tmct-impact.mjs +9 -0
  118. package/src/tools/handlers/tmct-members.mjs +16 -0
  119. package/src/tools/handlers/tmct-method-history.mjs +6 -0
  120. package/src/tools/handlers/tmct-search.mjs +22 -0
  121. package/src/tools/handlers/tmct-signature.mjs +6 -0
  122. package/src/tools/handlers/tmct-snippet.mjs +37 -0
  123. package/src/tools/handlers/tmct-subclasses.mjs +16 -0
  124. package/src/tools/handlers/tmct-tests-for.mjs +6 -0
  125. package/src/tools/handlers/tmct-untested.mjs +7 -0
  126. package/src/tools/memory-fallthrough.mjs +65 -0
  127. package/src/{schema-docs.mjs → tools/schema-docs.mjs} +1 -1
  128. package/src/tools/server.mjs +61 -0
  129. package/src/answer-variants.mjs +0 -39
  130. package/src/hash.mjs +0 -24
  131. package/src/server.mjs +0 -501
  132. /package/src/{corpus → adapters/corpus}/unknown-ingest.mjs +0 -0
  133. /package/src/{graph-merge.mjs → adapters/graph-merge.mjs} +0 -0
  134. /package/src/{memory → adapters/memory}/inspect.mjs +0 -0
  135. /package/src/{memory → adapters/memory}/shacl.mjs +0 -0
  136. /package/src/{prose-nlp.mjs → adapters/prose-nlp.mjs} +0 -0
  137. /package/src/{repository-interface.mjs → adapters/repository-interface.mjs} +0 -0
  138. /package/src/{uuid.mjs → adapters/uuid.mjs} +0 -0
  139. /package/src/{wink-model.mjs → adapters/wink-model.mjs} +0 -0
  140. /package/src/{completions → domain/completions}/prune.mjs +0 -0
  141. /package/src/{concept.mjs → domain/concept.mjs} +0 -0
  142. /package/src/{domain.mjs → domain/domain.mjs} +0 -0
  143. /package/src/{interpret → domain/interpret}/merge.mjs +0 -0
  144. /package/src/{interpret → domain/interpret}/strategies/grammar.mjs +0 -0
  145. /package/src/{interpret → domain/interpret}/strategies/noise-strip.mjs +0 -0
  146. /package/src/{memory → domain/memory}/bias.mjs +0 -0
  147. /package/src/{planning.mjs → domain/planning.mjs} +0 -0
  148. /package/src/{viz-theme.mjs → services/viz-theme.mjs} +0 -0
@@ -0,0 +1,29 @@
1
+ // The cold-tool catalog `tmct init` writes to <repo>/.tmct/TOOLS.md, so a repo carries
2
+ // the Bash invocation for every tool that has no resident schema. Rendered from the tool
3
+ // definitions, so a tool documents itself once.
4
+
5
+ import { HOT_TOOLS, COLD_TOOLS } from "./definitions.mjs";
6
+
7
+ /** Markdown catalog of the COLD tools (everything except the hot catalog tools): each
8
+ * with a one-line purpose and the exact Bash invocation via the CLI `cli <tool>` route.
9
+ * Pure — `cliPath` is the absolute path to bin/tmct.mjs the caller wants embedded. */
10
+ export function renderToolsCatalog(cliPath) {
11
+ const hot = HOT_TOOLS.map((t) => `\`${t.name}\` (${t.summary})`).join(", ");
12
+ const lines = [
13
+ "# tmct cold-tool catalog",
14
+ "",
15
+ `The hot tools — ${hot} — carry full schemas in the TOOLS catalog.`,
16
+ "",
17
+ "The cold tools below invoke via the CLI:",
18
+ "",
19
+ ];
20
+ for (const { name, summary, example } of COLD_TOOLS) {
21
+ lines.push(`## ${name}`);
22
+ lines.push(summary);
23
+ lines.push("```bash");
24
+ lines.push(`node ${cliPath} cli ${name} '${JSON.stringify(example)}'`);
25
+ lines.push("```");
26
+ lines.push("");
27
+ }
28
+ return lines.join("\n");
29
+ }
@@ -15,7 +15,7 @@ import {
15
15
  SERVICES,
16
16
  isHit,
17
17
  isMiss,
18
- } from "./repository-interface.mjs";
18
+ } from "../adapters/repository-interface.mjs";
19
19
 
20
20
  const REASONS = new Set(Object.values(MISS_REASONS));
21
21
 
@@ -146,7 +146,7 @@ export function runConformance(name, makeProvider) {
146
146
  });
147
147
 
148
148
  // 2f: the source-capable branch — dead code until a provider actually sets sourceAccess:true
149
- // (test/repository-interface.test.mjs's third runConformance call, against a source-capable
149
+ // (test/adapters/repository-interface.test.mjs's third runConformance call, against a source-capable
150
150
  // fixture provider, is what makes this execute at all).
151
151
  test(`[${name}] source-capable: snippet/context return real body text for a resolvable spanned symbol`, async () => {
152
152
  const svc = makeProvider();
@@ -0,0 +1,288 @@
1
+ // The tool definitions — one entry per tool tmct serves, and the only place a
2
+ // tool's name, schema, purpose or example is written down. Everything else reads
3
+ // from here: the resident hot catalog and the dispatch set (server.mjs), the
4
+ // cold-tool catalog written into a repo (catalog.mjs), and the README's tool
5
+ // section (scripts/generate-tool-docs.mjs, checked by test/estate/tool-docs.test.mjs).
6
+ //
7
+ // Each definition carries:
8
+ // name the tool name callers invoke;
9
+ // tier "hot" (schema resident in the catalog, re-billed every turn) or
10
+ // "cold" (served by dispatchTool, reached via the CLI `cli <tool>` route);
11
+ // summary one plain-English line: what the tool answers. Documentation voice;
12
+ // agentDescription hot tools only — the description billed to a calling agent, written
13
+ // to steer it to one call instead of a Read/Grep loop;
14
+ // inputSchema JSON Schema for the arguments dispatchTool reads;
15
+ // example arguments for a worked invocation in the cold-tool catalog;
16
+ // chat chat-facing tools only — the canonical grammar the tool accepts,
17
+ // lexicon hints, and worked examples (see tmct_ask below).
18
+ //
19
+ // The lexicon hints are DERIVED from the parser's own relation table rather than
20
+ // restated here, so a verb the parser learns is a verb the documentation gains.
21
+
22
+ import { RELATIONS, WHERE_MARKERS } from "../domain/ask-vocab.mjs";
23
+
24
+ /** The relation kinds tmct_ask traverses, each with the opening verbs a question can
25
+ * use for it. Read from the parser's RELATIONS table — never a hand-kept copy. */
26
+ export const askLexicon = () =>
27
+ Object.entries(RELATIONS).map(([kind, { verbs }]) => ({ kind, verbs: verbs.slice(0, 4) }));
28
+
29
+ const symbolArg = (description) => ({
30
+ type: "object",
31
+ required: ["symbol"],
32
+ properties: { symbol: { type: "string", description } },
33
+ });
34
+
35
+ const moduleArg = (description) => ({
36
+ type: "object",
37
+ required: ["module"],
38
+ properties: { module: { type: "string", description } },
39
+ });
40
+
41
+ const classArg = (description) => ({
42
+ type: "object",
43
+ required: ["class"],
44
+ properties: { class: { type: "string", description } },
45
+ });
46
+
47
+ export const TOOL_DEFINITIONS = Object.freeze([
48
+ {
49
+ name: "tmct_context",
50
+ tier: "hot",
51
+ summary:
52
+ "A sized edit bundle for one symbol — exemplar source, sibling signatures, registration anchor and the insertion region, in one call.",
53
+ // Lean resident schema (re-billed every turn): the minimum that still steers the agent to
54
+ // ONE call → write, not Read.
55
+ agentDescription:
56
+ "START HERE to add/modify code: ONE call returns a sized edit bundle (exemplar source, sibling signatures, registration, insertion region) — then write directly, don't Read.",
57
+ inputSchema: {
58
+ type: "object",
59
+ required: ["symbol"],
60
+ properties: {
61
+ symbol: { type: "string", description: "Module path (e.g. path/to/module) or a sibling function/class name defined in it." },
62
+ depth: { type: "string", enum: ["min", "auto", "full"], default: "auto", description: "auto (sized to the task) | min (leanest) | full (every section)." },
63
+ },
64
+ },
65
+ example: { symbol: "django/utils/text.py" },
66
+ },
67
+ {
68
+ name: "tmct_snippet",
69
+ tier: "hot",
70
+ summary:
71
+ "The exact source of one function, class or Class.method — its line span only, plus a one-line in-repo call hint.",
72
+ agentDescription:
73
+ "EXACT source of one function/class/Class.method by name (its line span only) + a one-line in-repo call hint. Prefer over Read for a single symbol.",
74
+ inputSchema: {
75
+ type: "object",
76
+ required: ["symbol"],
77
+ properties: {
78
+ symbol: { type: "string", description: "function/class name, Class.method, or fn:<path>#name." },
79
+ },
80
+ },
81
+ example: { symbol: "Truncator.chars" },
82
+ },
83
+ {
84
+ name: "tmct_ask",
85
+ tier: "hot",
86
+ summary:
87
+ "A structural question in plain English, answered from the graph in one call — no model, and a clean miss instead of a guess.",
88
+ agentDescription:
89
+ "Ask a structural question in plain English: \"which functions call X\", \"what uses X\", \"where is X defined\", \"when did X change\". One call, no model. A clean miss beats a guess.",
90
+ inputSchema: {
91
+ type: "object",
92
+ required: ["query"],
93
+ properties: {
94
+ query: { type: "string", description: "A free-text question, e.g. \"which functions explicitly couple to logging\"." },
95
+ },
96
+ },
97
+ example: { query: "which modules import src/core/model.mjs" },
98
+ chat: {
99
+ // The canonical question shapes ask.mjs resolves to a traversal. Every example runs
100
+ // against examples/mini-webapp, which is what the README's worked block invokes.
101
+ exampleRepo: "examples/mini-webapp",
102
+ grammar: [
103
+ {
104
+ form: "which <things> <relation-verb> <entity>",
105
+ example: "which modules import src/core/model.mjs",
106
+ answers: "the imports edge, read forwards",
107
+ },
108
+ {
109
+ form: "what is <relation-verb-passive> <entity>",
110
+ example: "what is imported by src/core/store.mjs",
111
+ answers: "the same edge, read backwards — a passive is the opposite direction, not a synonym",
112
+ },
113
+ {
114
+ form: "what <relation-verb> <entity>",
115
+ example: "what uses src/lib/http.mjs",
116
+ answers: "the uses union: imports plus calls",
117
+ },
118
+ {
119
+ form: "where is <entity> <where-marker>",
120
+ example: "where is saveStore defined",
121
+ answers: "the definition's file and line span",
122
+ },
123
+ {
124
+ form: "when did <entity> change",
125
+ example: "when did src/core/store.mjs change",
126
+ answers: "the commits that touched it, newest first",
127
+ },
128
+ ],
129
+ whereMarkers: WHERE_MARKERS,
130
+ },
131
+ },
132
+ {
133
+ name: "tmct_describe",
134
+ tier: "cold",
135
+ summary: "Locate one symbol and list its typed edges (both directions) with provenance.",
136
+ inputSchema: symbolArg("A module path, symbol name, or Class.method."),
137
+ example: { symbol: "django/utils/text.py" },
138
+ },
139
+ {
140
+ name: "tmct_signature",
141
+ tier: "cold",
142
+ summary: "One symbol's API surface (params, returns, raises/catches, flags, decorators, doc) without the body.",
143
+ inputSchema: symbolArg("A function, method, or Class.method name."),
144
+ example: { symbol: "Truncator.chars" },
145
+ },
146
+ {
147
+ name: "tmct_impact",
148
+ tier: "cold",
149
+ summary: "Transitive reverse closure over imports/calls — what breaks if a module changes, by depth, with tests.",
150
+ inputSchema: moduleArg("The module whose dependents you want."),
151
+ example: { module: "django/utils/text.py" },
152
+ },
153
+ {
154
+ name: "tmct_search",
155
+ tier: "cold",
156
+ summary: "Free-text/ranked lookup over the code-map to find the right module or symbol.",
157
+ inputSchema: {
158
+ type: "object",
159
+ required: [],
160
+ properties: {
161
+ query: { type: "string", description: "Free text to rank against the code-map. Required unless kind narrows the search on its own." },
162
+ kind: { type: "string", description: "Restrict to an entity class, e.g. function, class, module." },
163
+ decorator: { type: "string", description: "Restrict to definitions carrying this decorator." },
164
+ name: { type: "string", description: "Restrict to definitions whose name matches." },
165
+ },
166
+ },
167
+ example: { query: "template filters", kind: "function" },
168
+ },
169
+ {
170
+ name: "tmct_members",
171
+ tier: "cold",
172
+ summary: "A class's methods + attributes (file:line, decorators) in one slice.",
173
+ inputSchema: classArg("The class whose members you want."),
174
+ example: { class: "Truncator" },
175
+ },
176
+ {
177
+ name: "tmct_subclasses",
178
+ tier: "cold",
179
+ summary: "A class's base classes plus the transitive set of classes that extend it.",
180
+ inputSchema: classArg("The class to walk the inheritance edges of."),
181
+ example: { class: "Field" },
182
+ },
183
+ {
184
+ name: "tmct_architecture",
185
+ tier: "cold",
186
+ summary: "Package/module map + the most-imported hub modules (optionally scoped to a package).",
187
+ inputSchema: {
188
+ type: "object",
189
+ required: [],
190
+ properties: {
191
+ package: { type: "string", description: "Scope the map to one package. Omit for the whole repository." },
192
+ },
193
+ },
194
+ example: { package: "django/template" },
195
+ },
196
+ {
197
+ name: "tmct_exports",
198
+ tier: "cold",
199
+ summary: "A module's public __all__ surface, each name resolved to the module that defines it.",
200
+ inputSchema: moduleArg("The module whose public API surface you want."),
201
+ example: { module: "django/db/models/__init__.py" },
202
+ },
203
+ {
204
+ name: "tmct_tests_for",
205
+ tier: "cold",
206
+ summary: "The test modules covering a symbol or module, from the typed test edges.",
207
+ inputSchema: symbolArg("The symbol or module to find covering tests for."),
208
+ example: { symbol: "django/utils/text.py" },
209
+ },
210
+ {
211
+ name: "tmct_untested",
212
+ tier: "cold",
213
+ summary: "Source modules with no covering test module — a coverage-gap view (no arguments).",
214
+ inputSchema: { type: "object", required: [], properties: {} },
215
+ example: {},
216
+ },
217
+ {
218
+ name: "tmct_history",
219
+ tier: "cold",
220
+ summary: "Recent commits that touched a symbol's module (newest first).",
221
+ inputSchema: symbolArg("The symbol whose module's history you want."),
222
+ example: { symbol: "django/utils/text.py" },
223
+ },
224
+ {
225
+ name: "tmct_file_history",
226
+ tier: "cold",
227
+ summary: "Commits that touched a symbol's module, each with author / date / subject.",
228
+ inputSchema: symbolArg("The symbol whose module's history you want."),
229
+ example: { symbol: "django/utils/text.py" },
230
+ },
231
+ {
232
+ name: "tmct_method_history",
233
+ tier: "cold",
234
+ summary: "Commits that touched a specific method symbol (fine-grained), with author / date / subject.",
235
+ inputSchema: symbolArg("A Class.method name."),
236
+ example: { symbol: "Truncator.chars" },
237
+ },
238
+ {
239
+ name: "tmct_class_history",
240
+ tier: "cold",
241
+ summary: "Commits that touched a specific class symbol (fine-grained), with author / date / subject.",
242
+ inputSchema: symbolArg("A class name."),
243
+ example: { symbol: "Truncator" },
244
+ },
245
+ {
246
+ name: "tmct_callers",
247
+ tier: "cold",
248
+ summary: "Modules that call into a symbol's module (one hop).",
249
+ inputSchema: symbolArg("The symbol whose callers you want."),
250
+ example: { symbol: "django/utils/text.py" },
251
+ },
252
+ {
253
+ name: "tmct_callees",
254
+ tier: "cold",
255
+ summary: "Modules a symbol's module calls into (one hop).",
256
+ inputSchema: symbolArg("The symbol whose callees you want."),
257
+ example: { symbol: "django/utils/text.py" },
258
+ },
259
+ {
260
+ name: "tmct_calls",
261
+ tier: "cold",
262
+ summary: "The in-repo symbols a function calls (fn→fn), each with file:line.",
263
+ inputSchema: symbolArg("The function whose in-repo call sites you want."),
264
+ example: { symbol: "slugify" },
265
+ },
266
+ {
267
+ name: "tmct_cochanges",
268
+ tier: "cold",
269
+ summary: "Modules that historically change in the same commit as a symbol's module (git co-change).",
270
+ inputSchema: symbolArg("The symbol whose module's change-coupling you want."),
271
+ example: { symbol: "django/utils/text.py" },
272
+ },
273
+ {
274
+ name: "tmct_context_more",
275
+ tier: "cold",
276
+ summary: "The bundle sections a lean tmct_context omitted (siblings / tests / cochange / class members / re-exports).",
277
+ inputSchema: symbolArg("The symbol a lean tmct_context bundle was built for."),
278
+ example: { symbol: "django/utils/text.py" },
279
+ },
280
+ ]);
281
+
282
+ export const HOT_TOOLS = TOOL_DEFINITIONS.filter((t) => t.tier === "hot");
283
+ export const COLD_TOOLS = TOOL_DEFINITIONS.filter((t) => t.tier === "cold");
284
+
285
+ /** Every tool name dispatchTool serves. */
286
+ export const TOOL_NAMES = Object.freeze(TOOL_DEFINITIONS.map((t) => t.name));
287
+
288
+ export const toolByName = (name) => TOOL_DEFINITIONS.find((t) => t.name === name) || null;
@@ -0,0 +1,20 @@
1
+ // The one graph load the tool layer does: config -> source.fetchEntities -> parseEntities.
2
+ // Re-exported by server.mjs so chat.mjs's compare lane loads the SAME graph the tools
3
+ // themselves load, with no second loading path.
4
+
5
+ import { ToolError } from "../adapters/config.mjs";
6
+ import { parseEntities } from "../domain/codegraph.mjs";
7
+
8
+ export async function loadGraph(config, source) {
9
+ const payload = await source.fetchEntities(config);
10
+ const graph = parseEntities(payload);
11
+ if (!graph.individuals.length) {
12
+ // Honest miss, never a stack: a fresh repo simply has no graph yet (the chat
13
+ // session itself creates one as the conversation folds in).
14
+ throw new ToolError(
15
+ `the graph at ${config.graphFile} is empty — no entities to answer from yet ` +
16
+ "(this repo starts with no graph; the chat session folds the conversation into one).",
17
+ );
18
+ }
19
+ return graph;
20
+ }
@@ -0,0 +1,54 @@
1
+ // The tool-name → handler registry dispatchTool maps over. One module per tool, so a
2
+ // tool's behaviour has exactly one home and this file only wires names to it.
3
+ //
4
+ // A handler takes (args, ctx) and returns the caller-facing string. ctx carries the
5
+ // already-loaded { graph, svc, config, repoRoot }. A handler marked `ownsGraphLoad`
6
+ // gets { config, source, tel } instead and loads whatever it needs itself.
7
+
8
+ import { tmct_context } from "./tmct-context.mjs";
9
+ import { tmct_context_more } from "./tmct-context-more.mjs";
10
+ import { tmct_describe } from "./tmct-describe.mjs";
11
+ import { tmct_snippet } from "./tmct-snippet.mjs";
12
+ import { tmct_signature } from "./tmct-signature.mjs";
13
+ import { tmct_impact } from "./tmct-impact.mjs";
14
+ import { tmct_search } from "./tmct-search.mjs";
15
+ import { tmct_members } from "./tmct-members.mjs";
16
+ import { tmct_subclasses } from "./tmct-subclasses.mjs";
17
+ import { tmct_architecture } from "./tmct-architecture.mjs";
18
+ import { tmct_exports } from "./tmct-exports.mjs";
19
+ import { tmct_untested } from "./tmct-untested.mjs";
20
+ import { tmct_ask } from "./tmct-ask.mjs";
21
+ import { tmct_tests_for } from "./tmct-tests-for.mjs";
22
+ import { tmct_history } from "./tmct-history.mjs";
23
+ import { tmct_file_history } from "./tmct-file-history.mjs";
24
+ import { tmct_method_history } from "./tmct-method-history.mjs";
25
+ import { tmct_class_history } from "./tmct-class-history.mjs";
26
+ import { tmct_callers } from "./tmct-callers.mjs";
27
+ import { tmct_callees } from "./tmct-callees.mjs";
28
+ import { tmct_calls } from "./tmct-calls.mjs";
29
+ import { tmct_cochanges } from "./tmct-cochanges.mjs";
30
+
31
+ export const HANDLERS = Object.freeze({
32
+ tmct_context,
33
+ tmct_context_more,
34
+ tmct_describe,
35
+ tmct_snippet,
36
+ tmct_signature,
37
+ tmct_impact,
38
+ tmct_search,
39
+ tmct_members,
40
+ tmct_subclasses,
41
+ tmct_architecture,
42
+ tmct_exports,
43
+ tmct_untested,
44
+ tmct_ask,
45
+ tmct_tests_for,
46
+ tmct_history,
47
+ tmct_file_history,
48
+ tmct_method_history,
49
+ tmct_class_history,
50
+ tmct_callers,
51
+ tmct_callees,
52
+ tmct_calls,
53
+ tmct_cochanges,
54
+ });
@@ -0,0 +1,33 @@
1
+ // What every per-tool handler module shares: the argument contract, the resolve-or-miss
2
+ // contract, and the one shape most cold tools have (take a symbol, resolve it, render it).
3
+
4
+ import { ToolError } from "../../adapters/config.mjs";
5
+ import { resolveSymbol } from "../../domain/codegraph.mjs";
6
+
7
+ export const SNIPPET_MAX_LINES = 200;
8
+
9
+ /** A required string argument, trimmed. Missing or blank raises "<key> is required". */
10
+ export function requiredArg(args, key) {
11
+ const value = String(args?.[key] || "").trim();
12
+ if (!value) throw new ToolError(`${key} is required`);
13
+ return value;
14
+ }
15
+
16
+ /** A clean miss on the interface becomes the instructive ToolError the CLI/chat expect —
17
+ * message-only, never a stack, no fabricated entity names. */
18
+ export function resolveOrThrow(svc, symbol, what) {
19
+ const { match, candidates } = resolveSymbol(svc.graph, symbol);
20
+ if (!match) {
21
+ throw new ToolError(
22
+ `no entity matching ${what} "${symbol}" in the code-map graph. ` +
23
+ "Try a repo-relative path (e.g. path/to/module), a basename, or tmct_search for a fuzzy lookup.",
24
+ );
25
+ }
26
+ return { match, candidates };
27
+ }
28
+
29
+ /** The shape most cold tools share: one required `symbol`, resolved, then rendered. */
30
+ export const symbolHandler = (render) => (args, { graph, svc }) => {
31
+ const { match } = resolveOrThrow(svc, requiredArg(args, "symbol"), "symbol");
32
+ return render(graph, match);
33
+ };
@@ -0,0 +1,7 @@
1
+ // tmct_architecture — package/module map plus the most-imported hub modules.
2
+
3
+ import { renderArchitecture } from "../../domain/codegraph.mjs";
4
+
5
+ export function tmct_architecture(args, { graph }) {
6
+ return renderArchitecture(graph, { pkg: String(args?.package || "").trim() });
7
+ }
@@ -0,0 +1,14 @@
1
+ // tmct_ask — a plain-English structural question answered from the graph in one
2
+ // mechanical, zero-model-call round-trip. See src/domain/ask.mjs.
3
+
4
+ import { ToolError } from "../../adapters/config.mjs";
5
+ import { ask } from "../../domain/ask.mjs";
6
+ import { requiredArg } from "./kit.mjs";
7
+
8
+ export function tmct_ask(args, { graph }) {
9
+ const { content, tmct_ask: envelope } = ask(graph, requiredArg(args, "query"));
10
+ // Every dispatchTool caller (the chat surface, the CLI fallback) expects a plain string —
11
+ // append the structured envelope as a delimited, machine-parseable block rather than
12
+ // changing that shared contract for one tool.
13
+ return `${content}\n\n---tmct_ask---\n${JSON.stringify(envelope, null, 2)}`;
14
+ }
@@ -0,0 +1,6 @@
1
+ // tmct_callees — modules a symbol's module calls into, one hop.
2
+
3
+ import { renderCallees } from "../../domain/codegraph.mjs";
4
+ import { symbolHandler } from "./kit.mjs";
5
+
6
+ export const tmct_callees = symbolHandler(renderCallees);
@@ -0,0 +1,6 @@
1
+ // tmct_callers — modules that call into a symbol's module, one hop.
2
+
3
+ import { renderCallers } from "../../domain/codegraph.mjs";
4
+ import { symbolHandler } from "./kit.mjs";
5
+
6
+ export const tmct_callers = symbolHandler(renderCallers);
@@ -0,0 +1,6 @@
1
+ // tmct_calls — the in-repo symbols a function calls, each with file:line.
2
+
3
+ import { renderCalls } from "../../domain/codegraph.mjs";
4
+ import { symbolHandler } from "./kit.mjs";
5
+
6
+ export const tmct_calls = symbolHandler(renderCalls);
@@ -0,0 +1,6 @@
1
+ // tmct_class_history — commits that touched one class symbol.
2
+
3
+ import { renderClassHistory } from "../../domain/codegraph.mjs";
4
+ import { symbolHandler } from "./kit.mjs";
5
+
6
+ export const tmct_class_history = symbolHandler(renderClassHistory);
@@ -0,0 +1,6 @@
1
+ // tmct_cochanges — modules that historically change in the same commit as a symbol's module.
2
+
3
+ import { renderCochanges } from "../../domain/codegraph.mjs";
4
+ import { symbolHandler } from "./kit.mjs";
5
+
6
+ export const tmct_cochanges = symbolHandler(renderCochanges);
@@ -0,0 +1,9 @@
1
+ // tmct_context_more — the bundle sections a lean tmct_context omitted.
2
+
3
+ import { contextPlan, renderContextMore } from "../../domain/codegraph.mjs";
4
+ import { requiredArg, resolveOrThrow } from "./kit.mjs";
5
+
6
+ export function tmct_context_more(args, { graph, svc }) {
7
+ const { match } = resolveOrThrow(svc, requiredArg(args, "symbol"), "symbol");
8
+ return renderContextMore(contextPlan(graph, match));
9
+ }