@polycode-projects/the-mechanical-code-talker 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ // corpus/tier2/generate.mjs — the TIER-2 specialised-corpus generator + manifest
3
+ // writer. NOT part of the product path — a maintainer tool. Offline, $0.
4
+ //
5
+ // Tier-2 corpuses are LANGUAGE- or DOMAIN-specific fact sets (aws, python,
6
+ // java, …) that tmct fetches/generates into `.tmct/` at init time so it can
7
+ // "expand into a concept for an applicable codebase". They are NOT shipped in
8
+ // the npm package the way the tier-1 ConceptNet slice is — they are opt-in,
9
+ // selected per repo. See ../README.md for the full tier-1/2/3 policy.
10
+ //
11
+ // Every tier-2 corpus is written in the EXACT tier-1 fact shape
12
+ // (corpus/conceptnet/slice.jsonl): one JSON object per line,
13
+ // {"start":"/c/en/<term>","rel":"/r/<Rel>","end":"/c/en/<concept>","weight":N,"surfaceText":"…"}
14
+ // with `rel` drawn ONLY from the mapped relations in
15
+ // src/corpus/conceptnet-map.toml, so a tier-2 file loads through the very same
16
+ // loadSlice()/toFacts() path as the tier-1 slice (this file's --verify proves
17
+ // it). The Wave-2 tier-2 SEEDER (see ../README.md) is what stamps the right
18
+ // provenance (`corpus:tier2:<id> /r/…`) instead of the conceptnet default.
19
+ //
20
+ // node corpus/tier2/generate.mjs # (re)write every <id>.jsonl + manifest.json
21
+ // node corpus/tier2/generate.mjs --verify # generate + assert each file loads & seeds cleanly
22
+ //
23
+ // To ADD a specialised corpus: add an entry to CORPUSES below (a list of
24
+ // [subject, relation, concept] triples, optionally [.., surfaceText]) and
25
+ // re-run. Curated sets are authored here so they stay reviewable and diffable;
26
+ // a corpus too large to curate by hand is a `fetch` manifest entry instead
27
+ // (network, opt-in — see fetchCorpus() and ../README.md).
28
+
29
+ import { readFile, writeFile } from "node:fs/promises";
30
+ import { createHash } from "node:crypto";
31
+ import { fileURLToPath } from "node:url";
32
+ import { dirname, join } from "node:path";
33
+
34
+ const HERE = dirname(fileURLToPath(import.meta.url));
35
+
36
+ // A curated triple is [subject, rel, concept] or [subject, rel, concept, weight].
37
+ // `rel` MUST be a mapped (ace != "none") relation in conceptnet-map.toml so the
38
+ // fact actually seeds. Terms are lowercase snake_case; the loader's termText()
39
+ // turns "/c/en/hash_table" into "hash table". Keep terms <= 3 words (the tier-1
40
+ // quality-filter rule) — curated data is clean by construction.
41
+ export const CORPUSES = {
42
+ aws: {
43
+ kind: "domain",
44
+ description: "Amazon Web Services core services and primitives (S3, Lambda, DynamoDB, EC2, IAM, SQS) mapped to general cloud/CS concepts.",
45
+ facts: [
46
+ ["aws", "/r/IsA", "cloud_platform"],
47
+ ["aws", "/r/IsA", "cloud"],
48
+ ["aws", "/r/CapableOf", "host_applications"],
49
+ // S3
50
+ ["s3", "/r/IsA", "object_storage"],
51
+ ["s3", "/r/IsA", "storage_service"],
52
+ ["s3", "/r/PartOf", "aws"],
53
+ ["s3", "/r/HasA", "bucket"],
54
+ ["s3", "/r/UsedFor", "storing_files"],
55
+ ["s3", "/r/CapableOf", "store_objects"],
56
+ ["bucket", "/r/IsA", "container"],
57
+ ["bucket", "/r/PartOf", "s3"],
58
+ ["bucket", "/r/UsedFor", "storing_objects"],
59
+ // Lambda
60
+ ["lambda", "/r/IsA", "compute_service"],
61
+ ["lambda", "/r/IsA", "function"],
62
+ ["lambda", "/r/PartOf", "aws"],
63
+ ["lambda", "/r/UsedFor", "running_code"],
64
+ ["lambda", "/r/CapableOf", "run_code"],
65
+ ["lambda", "/r/HasProperty", "serverless"],
66
+ // DynamoDB
67
+ ["dynamodb", "/r/IsA", "database"],
68
+ ["dynamodb", "/r/IsA", "nosql_database"],
69
+ ["dynamodb", "/r/PartOf", "aws"],
70
+ ["dynamodb", "/r/HasA", "table"],
71
+ ["dynamodb", "/r/UsedFor", "storing_data"],
72
+ ["dynamodb", "/r/HasProperty", "managed"],
73
+ // EC2
74
+ ["ec2", "/r/IsA", "compute_service"],
75
+ ["ec2", "/r/IsA", "virtual_machine"],
76
+ ["ec2", "/r/PartOf", "aws"],
77
+ ["ec2", "/r/HasA", "instance"],
78
+ ["ec2", "/r/UsedFor", "running_servers"],
79
+ // IAM
80
+ ["iam", "/r/IsA", "access_control"],
81
+ ["iam", "/r/PartOf", "aws"],
82
+ ["iam", "/r/UsedFor", "managing_permissions"],
83
+ ["iam", "/r/HasA", "role"],
84
+ ["iam", "/r/HasA", "policy"],
85
+ // SQS + queue
86
+ ["sqs", "/r/IsA", "message_queue"],
87
+ ["sqs", "/r/IsA", "queue"],
88
+ ["sqs", "/r/PartOf", "aws"],
89
+ ["sqs", "/r/UsedFor", "decoupling_services"],
90
+ ["queue", "/r/IsA", "data_structure"],
91
+ ],
92
+ },
93
+
94
+ python: {
95
+ kind: "language",
96
+ description: "Python language constructs and stdlib types mapped to the shared CS concept vocabulary (list->array, dict->hash table, …).",
97
+ facts: [
98
+ ["python", "/r/IsA", "programming_language"],
99
+ ["python", "/r/HasProperty", "interpreted"],
100
+ ["python", "/r/HasProperty", "dynamically_typed"],
101
+ ["python", "/r/UsedFor", "scripting"],
102
+ // built-in types → shared concepts
103
+ ["list", "/r/IsA", "array"],
104
+ ["list", "/r/IsA", "sequence"],
105
+ ["list", "/r/IsA", "data_structure"],
106
+ ["dict", "/r/IsA", "hash_table"],
107
+ ["dict", "/r/IsA", "dictionary"],
108
+ ["dict", "/r/IsA", "mapping"],
109
+ ["tuple", "/r/IsA", "sequence"],
110
+ ["tuple", "/r/HasProperty", "immutable"],
111
+ ["set", "/r/IsA", "collection"],
112
+ ["set", "/r/HasProperty", "unordered"],
113
+ ["str", "/r/IsA", "string"],
114
+ // language constructs
115
+ ["decorator", "/r/IsA", "function"],
116
+ ["decorator", "/r/UsedFor", "modifying_functions"],
117
+ ["generator", "/r/IsA", "iterator"],
118
+ ["generator", "/r/UsedFor", "lazy_evaluation"],
119
+ ["comprehension", "/r/IsA", "expression"],
120
+ ["comprehension", "/r/UsedFor", "building_collections"],
121
+ ["exception", "/r/IsA", "error"],
122
+ ["module", "/r/IsA", "file"],
123
+ ["package", "/r/IsA", "module"],
124
+ ["method", "/r/IsA", "function"],
125
+ // tooling / runtime
126
+ ["pip", "/r/IsA", "package_manager"],
127
+ ["pip", "/r/UsedFor", "installing_packages"],
128
+ ["gil", "/r/IsA", "lock"],
129
+ ["gil", "/r/PartOf", "interpreter"],
130
+ ["cpython", "/r/IsA", "interpreter"],
131
+ ],
132
+ },
133
+
134
+ java: {
135
+ kind: "language",
136
+ description: "Java language and JVM constructs mapped to the shared CS concept vocabulary (ArrayList->list, HashMap->hash table, …).",
137
+ facts: [
138
+ ["java", "/r/IsA", "programming_language"],
139
+ ["java", "/r/HasProperty", "compiled"],
140
+ ["java", "/r/HasProperty", "statically_typed"],
141
+ ["java", "/r/UsedFor", "building_applications"],
142
+ // types → shared concepts
143
+ ["arraylist", "/r/IsA", "list"],
144
+ ["arraylist", "/r/IsA", "data_structure"],
145
+ ["hashmap", "/r/IsA", "hash_table"],
146
+ ["hashmap", "/r/IsA", "dictionary"],
147
+ ["hashmap", "/r/IsA", "map"],
148
+ ["interface", "/r/IsA", "type"],
149
+ ["interface", "/r/IsA", "contract"],
150
+ ["class", "/r/IsA", "type"],
151
+ ["object", "/r/IsA", "instance"],
152
+ // JVM / runtime
153
+ ["jvm", "/r/IsA", "virtual_machine"],
154
+ ["jvm", "/r/UsedFor", "running_bytecode"],
155
+ ["jvm", "/r/CapableOf", "execute_bytecode"],
156
+ ["bytecode", "/r/IsA", "code"],
157
+ ["garbage_collector", "/r/PartOf", "jvm"],
158
+ ["garbage_collector", "/r/UsedFor", "freeing_memory"],
159
+ // packaging / tooling
160
+ ["jar", "/r/IsA", "archive"],
161
+ ["jar", "/r/IsA", "file"],
162
+ ["jar", "/r/UsedFor", "packaging_classes"],
163
+ ["maven", "/r/IsA", "build_tool"],
164
+ ["maven", "/r/IsA", "package_manager"],
165
+ ["gradle", "/r/IsA", "build_tool"],
166
+ // language features
167
+ ["thread", "/r/IsA", "process"],
168
+ ["thread", "/r/UsedFor", "concurrency"],
169
+ ["generics", "/r/UsedFor", "type_safety"],
170
+ ["annotation", "/r/IsA", "metadata"],
171
+ ["exception", "/r/IsA", "error"],
172
+ ["method", "/r/IsA", "function"],
173
+ ],
174
+ },
175
+ };
176
+
177
+ const conceptUri = (term) => `/c/en/${term}`;
178
+ const humanize = (term) => term.replace(/_/g, " ");
179
+
180
+ /** One curated triple → a tier-1-shaped slice row. */
181
+ export function toRow([subject, rel, concept, weight = 1]) {
182
+ return {
183
+ start: conceptUri(subject),
184
+ rel,
185
+ end: conceptUri(concept),
186
+ weight,
187
+ surfaceText: `[[${humanize(subject)}]] ${rel.replace("/r/", "")} [[${humanize(concept)}]]`,
188
+ };
189
+ }
190
+
191
+ /** Curated corpus id → its JSONL text (deterministic order = authored order). */
192
+ export function corpusJsonl(id) {
193
+ return CORPUSES[id].facts.map((f) => JSON.stringify(toRow(f))).join("\n") + "\n";
194
+ }
195
+
196
+ const sha256 = (text) => createHash("sha256").update(text).digest("hex");
197
+
198
+ // ---- network-fetch path (opt-in, offline-by-default) -----------------------
199
+ // A tier-2 corpus too large to curate by hand is declared in the manifest with
200
+ // { "source": { "kind": "fetch", "url": "...", "sha256": "..." } } and pulled by
201
+ // a Wave-2 fetch step. This helper is the reference implementation — it is NEVER
202
+ // called without an explicit --allow-network flag (the product default is $0,
203
+ // offline). No sample corpus uses it; the three samples are all `curated`.
204
+ export async function fetchCorpus(url, expectedSha) {
205
+ const res = await fetch(url, { headers: { accept: "application/x-ndjson,application/jsonl" } });
206
+ if (!res.ok) throw new Error(`fetch ${url}: HTTP ${res.status}`);
207
+ const text = await res.text();
208
+ const got = sha256(text);
209
+ if (expectedSha && got !== expectedSha) throw new Error(`checksum mismatch for ${url}: ${got} != ${expectedSha}`);
210
+ return text;
211
+ }
212
+
213
+ async function main() {
214
+ const verify = process.argv.includes("--verify");
215
+ const manifest = { version: 1, generated: "by corpus/tier2/generate.mjs", corpuses: [] };
216
+
217
+ for (const [id, spec] of Object.entries(CORPUSES)) {
218
+ const text = corpusJsonl(id);
219
+ await writeFile(join(HERE, `${id}.jsonl`), text);
220
+ manifest.corpuses.push({
221
+ id,
222
+ kind: spec.kind,
223
+ description: spec.description,
224
+ source: { kind: "curated", tool: "corpus/tier2/generate.mjs" },
225
+ file: `${id}.jsonl`,
226
+ facts: spec.facts.length,
227
+ bytes: Buffer.byteLength(text),
228
+ sha256: sha256(text),
229
+ license: "MPL-2.0",
230
+ });
231
+ console.error(` ${id}: ${spec.facts.length} facts, ${Buffer.byteLength(text)} bytes`);
232
+ }
233
+
234
+ const manifestText = JSON.stringify(manifest, null, 2) + "\n";
235
+ await writeFile(join(HERE, "manifest.json"), manifestText);
236
+ console.error(`wrote manifest.json (${manifest.corpuses.length} corpuses)`);
237
+
238
+ if (verify) {
239
+ const { loadSlice, loadMap, toFacts } = await import("../../src/corpus/conceptnet.mjs");
240
+ const map = await loadMap();
241
+ for (const c of manifest.corpuses) {
242
+ const assertions = await loadSlice(join(HERE, c.file));
243
+ const facts = toFacts(assertions, map); // throws on any unmapped rel
244
+ if (assertions.length !== c.facts) throw new Error(`${c.id}: loadSlice count ${assertions.length} != ${c.facts}`);
245
+ if (facts.length !== c.facts) throw new Error(`${c.id}: ${c.facts - facts.length} fact(s) did not seed (ace=none rel?)`);
246
+ console.error(` verify ${c.id}: ${assertions.length} assertions load, all ${facts.length} seed cleanly`);
247
+ }
248
+ console.error("verify: OK — every tier-2 corpus loads and seeds through the tier-1 path");
249
+ }
250
+ }
251
+
252
+ const isMain = process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href;
253
+ if (isMain) await main();
@@ -0,0 +1,31 @@
1
+ {"start":"/c/en/java","rel":"/r/IsA","end":"/c/en/programming_language","weight":1,"surfaceText":"[[java]] IsA [[programming language]]"}
2
+ {"start":"/c/en/java","rel":"/r/HasProperty","end":"/c/en/compiled","weight":1,"surfaceText":"[[java]] HasProperty [[compiled]]"}
3
+ {"start":"/c/en/java","rel":"/r/HasProperty","end":"/c/en/statically_typed","weight":1,"surfaceText":"[[java]] HasProperty [[statically typed]]"}
4
+ {"start":"/c/en/java","rel":"/r/UsedFor","end":"/c/en/building_applications","weight":1,"surfaceText":"[[java]] UsedFor [[building applications]]"}
5
+ {"start":"/c/en/arraylist","rel":"/r/IsA","end":"/c/en/list","weight":1,"surfaceText":"[[arraylist]] IsA [[list]]"}
6
+ {"start":"/c/en/arraylist","rel":"/r/IsA","end":"/c/en/data_structure","weight":1,"surfaceText":"[[arraylist]] IsA [[data structure]]"}
7
+ {"start":"/c/en/hashmap","rel":"/r/IsA","end":"/c/en/hash_table","weight":1,"surfaceText":"[[hashmap]] IsA [[hash table]]"}
8
+ {"start":"/c/en/hashmap","rel":"/r/IsA","end":"/c/en/dictionary","weight":1,"surfaceText":"[[hashmap]] IsA [[dictionary]]"}
9
+ {"start":"/c/en/hashmap","rel":"/r/IsA","end":"/c/en/map","weight":1,"surfaceText":"[[hashmap]] IsA [[map]]"}
10
+ {"start":"/c/en/interface","rel":"/r/IsA","end":"/c/en/type","weight":1,"surfaceText":"[[interface]] IsA [[type]]"}
11
+ {"start":"/c/en/interface","rel":"/r/IsA","end":"/c/en/contract","weight":1,"surfaceText":"[[interface]] IsA [[contract]]"}
12
+ {"start":"/c/en/class","rel":"/r/IsA","end":"/c/en/type","weight":1,"surfaceText":"[[class]] IsA [[type]]"}
13
+ {"start":"/c/en/object","rel":"/r/IsA","end":"/c/en/instance","weight":1,"surfaceText":"[[object]] IsA [[instance]]"}
14
+ {"start":"/c/en/jvm","rel":"/r/IsA","end":"/c/en/virtual_machine","weight":1,"surfaceText":"[[jvm]] IsA [[virtual machine]]"}
15
+ {"start":"/c/en/jvm","rel":"/r/UsedFor","end":"/c/en/running_bytecode","weight":1,"surfaceText":"[[jvm]] UsedFor [[running bytecode]]"}
16
+ {"start":"/c/en/jvm","rel":"/r/CapableOf","end":"/c/en/execute_bytecode","weight":1,"surfaceText":"[[jvm]] CapableOf [[execute bytecode]]"}
17
+ {"start":"/c/en/bytecode","rel":"/r/IsA","end":"/c/en/code","weight":1,"surfaceText":"[[bytecode]] IsA [[code]]"}
18
+ {"start":"/c/en/garbage_collector","rel":"/r/PartOf","end":"/c/en/jvm","weight":1,"surfaceText":"[[garbage collector]] PartOf [[jvm]]"}
19
+ {"start":"/c/en/garbage_collector","rel":"/r/UsedFor","end":"/c/en/freeing_memory","weight":1,"surfaceText":"[[garbage collector]] UsedFor [[freeing memory]]"}
20
+ {"start":"/c/en/jar","rel":"/r/IsA","end":"/c/en/archive","weight":1,"surfaceText":"[[jar]] IsA [[archive]]"}
21
+ {"start":"/c/en/jar","rel":"/r/IsA","end":"/c/en/file","weight":1,"surfaceText":"[[jar]] IsA [[file]]"}
22
+ {"start":"/c/en/jar","rel":"/r/UsedFor","end":"/c/en/packaging_classes","weight":1,"surfaceText":"[[jar]] UsedFor [[packaging classes]]"}
23
+ {"start":"/c/en/maven","rel":"/r/IsA","end":"/c/en/build_tool","weight":1,"surfaceText":"[[maven]] IsA [[build tool]]"}
24
+ {"start":"/c/en/maven","rel":"/r/IsA","end":"/c/en/package_manager","weight":1,"surfaceText":"[[maven]] IsA [[package manager]]"}
25
+ {"start":"/c/en/gradle","rel":"/r/IsA","end":"/c/en/build_tool","weight":1,"surfaceText":"[[gradle]] IsA [[build tool]]"}
26
+ {"start":"/c/en/thread","rel":"/r/IsA","end":"/c/en/process","weight":1,"surfaceText":"[[thread]] IsA [[process]]"}
27
+ {"start":"/c/en/thread","rel":"/r/UsedFor","end":"/c/en/concurrency","weight":1,"surfaceText":"[[thread]] UsedFor [[concurrency]]"}
28
+ {"start":"/c/en/generics","rel":"/r/UsedFor","end":"/c/en/type_safety","weight":1,"surfaceText":"[[generics]] UsedFor [[type safety]]"}
29
+ {"start":"/c/en/annotation","rel":"/r/IsA","end":"/c/en/metadata","weight":1,"surfaceText":"[[annotation]] IsA [[metadata]]"}
30
+ {"start":"/c/en/exception","rel":"/r/IsA","end":"/c/en/error","weight":1,"surfaceText":"[[exception]] IsA [[error]]"}
31
+ {"start":"/c/en/method","rel":"/r/IsA","end":"/c/en/function","weight":1,"surfaceText":"[[method]] IsA [[function]]"}
@@ -0,0 +1,48 @@
1
+ {
2
+ "version": 1,
3
+ "generated": "by corpus/tier2/generate.mjs",
4
+ "corpuses": [
5
+ {
6
+ "id": "aws",
7
+ "kind": "domain",
8
+ "description": "Amazon Web Services core services and primitives (S3, Lambda, DynamoDB, EC2, IAM, SQS) mapped to general cloud/CS concepts.",
9
+ "source": {
10
+ "kind": "curated",
11
+ "tool": "corpus/tier2/generate.mjs"
12
+ },
13
+ "file": "aws.jsonl",
14
+ "facts": 39,
15
+ "bytes": 4802,
16
+ "sha256": "7ab3e656bc4bc717be3c50d97af030e8592d6597cde182b15390a1dfaca9755a",
17
+ "license": "MPL-2.0"
18
+ },
19
+ {
20
+ "id": "python",
21
+ "kind": "language",
22
+ "description": "Python language constructs and stdlib types mapped to the shared CS concept vocabulary (list->array, dict->hash table, …).",
23
+ "source": {
24
+ "kind": "curated",
25
+ "tool": "corpus/tier2/generate.mjs"
26
+ },
27
+ "file": "python.jsonl",
28
+ "facts": 30,
29
+ "bytes": 3794,
30
+ "sha256": "009245d024357bc4285e967fba486e32349ccec8fbe2796ba13011cb2be4c19c",
31
+ "license": "MPL-2.0"
32
+ },
33
+ {
34
+ "id": "java",
35
+ "kind": "language",
36
+ "description": "Java language and JVM constructs mapped to the shared CS concept vocabulary (ArrayList->list, HashMap->hash table, …).",
37
+ "source": {
38
+ "kind": "curated",
39
+ "tool": "corpus/tier2/generate.mjs"
40
+ },
41
+ "file": "java.jsonl",
42
+ "facts": 31,
43
+ "bytes": 3920,
44
+ "sha256": "d089426833c393f6f1574fe75c4ac94a1483ee80f72f79bd7fd32aaaa24a6e44",
45
+ "license": "MPL-2.0"
46
+ }
47
+ ]
48
+ }
@@ -0,0 +1,30 @@
1
+ {"start":"/c/en/python","rel":"/r/IsA","end":"/c/en/programming_language","weight":1,"surfaceText":"[[python]] IsA [[programming language]]"}
2
+ {"start":"/c/en/python","rel":"/r/HasProperty","end":"/c/en/interpreted","weight":1,"surfaceText":"[[python]] HasProperty [[interpreted]]"}
3
+ {"start":"/c/en/python","rel":"/r/HasProperty","end":"/c/en/dynamically_typed","weight":1,"surfaceText":"[[python]] HasProperty [[dynamically typed]]"}
4
+ {"start":"/c/en/python","rel":"/r/UsedFor","end":"/c/en/scripting","weight":1,"surfaceText":"[[python]] UsedFor [[scripting]]"}
5
+ {"start":"/c/en/list","rel":"/r/IsA","end":"/c/en/array","weight":1,"surfaceText":"[[list]] IsA [[array]]"}
6
+ {"start":"/c/en/list","rel":"/r/IsA","end":"/c/en/sequence","weight":1,"surfaceText":"[[list]] IsA [[sequence]]"}
7
+ {"start":"/c/en/list","rel":"/r/IsA","end":"/c/en/data_structure","weight":1,"surfaceText":"[[list]] IsA [[data structure]]"}
8
+ {"start":"/c/en/dict","rel":"/r/IsA","end":"/c/en/hash_table","weight":1,"surfaceText":"[[dict]] IsA [[hash table]]"}
9
+ {"start":"/c/en/dict","rel":"/r/IsA","end":"/c/en/dictionary","weight":1,"surfaceText":"[[dict]] IsA [[dictionary]]"}
10
+ {"start":"/c/en/dict","rel":"/r/IsA","end":"/c/en/mapping","weight":1,"surfaceText":"[[dict]] IsA [[mapping]]"}
11
+ {"start":"/c/en/tuple","rel":"/r/IsA","end":"/c/en/sequence","weight":1,"surfaceText":"[[tuple]] IsA [[sequence]]"}
12
+ {"start":"/c/en/tuple","rel":"/r/HasProperty","end":"/c/en/immutable","weight":1,"surfaceText":"[[tuple]] HasProperty [[immutable]]"}
13
+ {"start":"/c/en/set","rel":"/r/IsA","end":"/c/en/collection","weight":1,"surfaceText":"[[set]] IsA [[collection]]"}
14
+ {"start":"/c/en/set","rel":"/r/HasProperty","end":"/c/en/unordered","weight":1,"surfaceText":"[[set]] HasProperty [[unordered]]"}
15
+ {"start":"/c/en/str","rel":"/r/IsA","end":"/c/en/string","weight":1,"surfaceText":"[[str]] IsA [[string]]"}
16
+ {"start":"/c/en/decorator","rel":"/r/IsA","end":"/c/en/function","weight":1,"surfaceText":"[[decorator]] IsA [[function]]"}
17
+ {"start":"/c/en/decorator","rel":"/r/UsedFor","end":"/c/en/modifying_functions","weight":1,"surfaceText":"[[decorator]] UsedFor [[modifying functions]]"}
18
+ {"start":"/c/en/generator","rel":"/r/IsA","end":"/c/en/iterator","weight":1,"surfaceText":"[[generator]] IsA [[iterator]]"}
19
+ {"start":"/c/en/generator","rel":"/r/UsedFor","end":"/c/en/lazy_evaluation","weight":1,"surfaceText":"[[generator]] UsedFor [[lazy evaluation]]"}
20
+ {"start":"/c/en/comprehension","rel":"/r/IsA","end":"/c/en/expression","weight":1,"surfaceText":"[[comprehension]] IsA [[expression]]"}
21
+ {"start":"/c/en/comprehension","rel":"/r/UsedFor","end":"/c/en/building_collections","weight":1,"surfaceText":"[[comprehension]] UsedFor [[building collections]]"}
22
+ {"start":"/c/en/exception","rel":"/r/IsA","end":"/c/en/error","weight":1,"surfaceText":"[[exception]] IsA [[error]]"}
23
+ {"start":"/c/en/module","rel":"/r/IsA","end":"/c/en/file","weight":1,"surfaceText":"[[module]] IsA [[file]]"}
24
+ {"start":"/c/en/package","rel":"/r/IsA","end":"/c/en/module","weight":1,"surfaceText":"[[package]] IsA [[module]]"}
25
+ {"start":"/c/en/method","rel":"/r/IsA","end":"/c/en/function","weight":1,"surfaceText":"[[method]] IsA [[function]]"}
26
+ {"start":"/c/en/pip","rel":"/r/IsA","end":"/c/en/package_manager","weight":1,"surfaceText":"[[pip]] IsA [[package manager]]"}
27
+ {"start":"/c/en/pip","rel":"/r/UsedFor","end":"/c/en/installing_packages","weight":1,"surfaceText":"[[pip]] UsedFor [[installing packages]]"}
28
+ {"start":"/c/en/gil","rel":"/r/IsA","end":"/c/en/lock","weight":1,"surfaceText":"[[gil]] IsA [[lock]]"}
29
+ {"start":"/c/en/gil","rel":"/r/PartOf","end":"/c/en/interpreter","weight":1,"surfaceText":"[[gil]] PartOf [[interpreter]]"}
30
+ {"start":"/c/en/cpython","rel":"/r/IsA","end":"/c/en/interpreter","weight":1,"surfaceText":"[[cpython]] IsA [[interpreter]]"}
@@ -62,6 +62,8 @@
62
62
  {"id":"conversational-farewell","class":"conversational","register":"friendly","template":"Bye — flushing the session log. Come back with a question any time."}
63
63
  {"id":"orientation-friendly","class":"orientation","register":"friendly","template":"I answer questions about THIS codebase's structure — imports, calls, definitions,\nhistory and counts. For example:\n which modules import walk.mjs\n what calls buildContextBundle\n how many classes are there\n/help for commands, /stats for an overview of the graph."}
64
64
  {"id":"miss-no-previous-answer","class":"miss","register":"friendly","template":"No previous answer to expand yet — ask me a question first, then say \"why\" or \"say more\"."}
65
+ {"id":"conversational-greeting-empty","class":"conversational","register":"friendly","template":"Hi. There's no code graph loaded here yet — point me at your code with `--repo <path>` or run `tmct init`. Meanwhile I know some general vocabulary — try \"what is a cache\". /help for commands."}
66
+ {"id":"orientation-empty","class":"orientation","register":"friendly","template":"There's no code graph loaded here, so I can't answer structure questions (imports, calls, definitions) yet.\nPoint me at your code with `--repo <path>` or run `tmct init` to index this repo.\nI do know some general vocabulary — try \"what is a cache\". /help for commands, /memory for what I remember."}
65
67
  {"id":"technical-density","class":"count","register":"technical","template":"{subject} carries {count} {noun} across {scope} — a concentration well above what a codebase of this size typically sustains ({provenance})."}
66
68
  {"id":"technical-comparison","class":"count","register":"technical","template":"At {count} {noun}, {subject} sits {comparison} the comparable-project baseline, a divergence that reflects deliberate structure rather than measurement noise ({provenance})."}
67
69
  {"id":"technical-superlative","class":"count","register":"technical","template":"No {noun} in {scope} is more {metric} than {subject}; it leads the next candidate by a clear margin of {count} ({provenance})."}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
@@ -48,7 +48,12 @@
48
48
  "./relationKind": "./src/codegraph.mjs",
49
49
  "./impactClosure": "./src/codegraph.mjs",
50
50
  "./dispatchTool": "./src/server.mjs",
51
- "./fetchEntities": "./src/source.mjs"
51
+ "./fetchEntities": "./src/source.mjs",
52
+ "./repository-interface": "./src/repository-interface.mjs",
53
+ "./graph-service": "./src/providers/graph-service.mjs",
54
+ "./providers/fixture": "./src/providers/fixture.mjs",
55
+ "./providers/bootstrap": "./src/providers/bootstrap.mjs",
56
+ "./conformance": "./src/conformance.mjs"
52
57
  },
53
58
  "files": [
54
59
  "bin/",
@@ -72,6 +77,9 @@
72
77
  "scripts": {
73
78
  "test": "node --test \"test/**/*.test.mjs\"",
74
79
  "chat": "node bin/tmct.mjs",
80
+ "chat:repo": "node bin/tmct.mjs chat --repo",
81
+ "example:mini": "node bin/tmct.mjs chat --repo examples/mini-webapp",
82
+ "example:polyglot": "node bin/tmct.mjs chat --repo examples/polyglot",
75
83
  "chatbench:run": "node chatbench/run.mjs",
76
84
  "chatbench:judge": "node chatbench/judge.mjs",
77
85
  "audit": "npm audit --audit-level=high",
package/src/ask-vocab.mjs CHANGED
@@ -529,7 +529,14 @@ export const AGGREGATE_TRIGGERS = Object.freeze([
529
529
  // CONTENT_VOCAB and blocks the article's own noise-strip)
530
530
  "how many", "how much", "how many of", "number of",
531
531
  "total number of", "quantity of",
532
- // neutral / imperative
532
+ // "<measure> of <kind>" cardinality forms (widened net, cycle W2P): count = sum = total
533
+ // = tally = number of. The bare single words (sum/total/tally) stay CASCADE_SYNONYMS-
534
+ // mapped to "count" (identifier-fragment risk without the "of" anchor — see that table's
535
+ // note); the multi-word "of" forms are safe to promote to direct triggers because the
536
+ // trailing "of <kind>" pins them to a cardinality question, not a stray identifier.
537
+ "tally of", "sum of", "total of", "amount of",
538
+ // neutral / imperative (bare "tally"/"sum"/"total" stay CASCADE_SYNONYMS-mapped so the
539
+ // "tally the classes" relaxation path — pinned by a cascade test — is preserved).
533
540
  "count", "count up", "count of", "tot up",
534
541
  ]);
535
542
 
@@ -593,6 +600,17 @@ export const EDGE_NOUN_TO_METRIC = Object.freeze({
593
600
  connections: { kind: "*", dir: "both" },
594
601
  edges: { kind: "*", dir: "both" },
595
602
  connected: { kind: "*", dir: "both" },
603
+ // participle degree-nouns (widened net, cycle W2P): "the most imported / most
604
+ // depended-on / most used <module>" ranks by IN-degree — how many things import/depend
605
+ // on/use it — the ARGMAX-by-degree intent a developer expresses with a passive
606
+ // participle rather than the noun ("importers"). "depended" catches "depended-on" /
607
+ // "depended on" (both tokenize to a bare "depended"); "used" folds the symbol-grain
608
+ // callsSymbol callers in alongside importers so "most used" reads as most-relied-upon.
609
+ imported: { kind: "imports", dir: "in" },
610
+ "depended-on": { kind: "imports", dir: "in" },
611
+ depended: { kind: "imports", dir: "in" },
612
+ used: { kind: "imports", dir: "in", sibling: "callsSymbol" },
613
+ called: { kind: "calls", dir: "in", sibling: "callsSymbol" },
596
614
  });
597
615
 
598
616
  /** Anaphora triggers over the PREVIOUS result set (ask()'s `prev` id array):
package/src/ask.mjs CHANGED
@@ -1471,18 +1471,18 @@ export function traverse(graph, parsed, { contextId = null, prev = null } = {})
1471
1471
 
1472
1472
  // reverse: "which <entityType> R <objMatch>". GRAIN-AWARE (Cycle 5, lever 3): a kind
1473
1473
  // that carries a symbol-grain sibling reads off the SIBLING when a fine SUBJECT grain
1474
- // was asked for ("which functions call X" → callsSymbol). Additionally, for `touches`
1475
- // specifically, when the RESOLVED OBJECT is itself a fine symbol the answer MUST read off
1476
- // touchesSymbol: touches is Commit→Module (module-coarse) and can NEVER point at a
1477
- // symbol, so "how many commits touched Widget.render" used to scan the module-grain
1478
- // edges and return a false 0 the count belongs at symbol grain. This object-driven
1479
- // switch is scoped to touches on purpose: calls/callsSymbol already resolve a fine
1480
- // object through the fine-entityType branch, and a null-entityType "what calls <fn>"
1481
- // deliberately keeps its module-coarse `calls` receipt (the honest-empty showcase pins
1482
- // it), so widening the switch to calls would silently change that answer.
1474
+ // was asked for ("which functions call X" → callsSymbol). It ALSO reads off the sibling
1475
+ // when the RESOLVED OBJECT is itself a fine symbol, for EVERY kind with a sibling — not
1476
+ // only touches: the module-coarse edge (calls Module→Module, touches Commit→Module) can
1477
+ // NEVER point at a function/method, so a bare "what calls fnAlpha" scanning the coarse
1478
+ // `calls` edges returned a FALSE empty ("No modules found …") while the graph records a
1479
+ // real symbol-level caller (Widget.render --callsSymbol--> fnAlpha). The honest answer
1480
+ // reads off callsSymbol at symbol grain; a truly-uncalled symbol still renders the
1481
+ // honest empty, now with the accurate callsSymbol receipt. (Previously scoped to touches
1482
+ // only, which left this exact callsSymbol caller invisible a genuine correctness bug.)
1483
1483
  const symbolKind = SYMBOL_GRAIN_SIBLING[kind];
1484
- const objIsTouchedSymbol = kind === "touches" && !!(objMatch.class && FINE_ENTITY_TYPES.has(objMatch.class));
1485
- if (symbolKind && (FINE_ENTITY_TYPES.has(entityType) || objIsTouchedSymbol)) {
1484
+ const objIsFineSymbol = !!(objMatch.class && FINE_ENTITY_TYPES.has(objMatch.class));
1485
+ if (symbolKind && (FINE_ENTITY_TYPES.has(entityType) || objIsFineSymbol)) {
1486
1486
  const edges = edgesOfKind(graph, symbolKind).filter((e) => e.object === objMatch.id);
1487
1487
  const subjects = uniqueById(edges.map((e) => graph.byId.get(e.subject)).filter(Boolean));
1488
1488
  const matches = (!entityType || entityType === "Change") ? subjects : subjects.filter((i) => i.class === entityType);