@polycode-projects/the-mechanical-code-talker 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +127 -28
  2. package/ROADMAP.md +3 -2
  3. package/bin/tmct.mjs +17 -90
  4. package/corpus/LICENSES.json +19 -4
  5. package/corpus/README.md +48 -0
  6. package/corpus/generated/README.md +24 -9
  7. package/corpus/generated/ace-surface-variants.jsonl +4 -1
  8. package/corpus/generated/manifest.json +4 -4
  9. package/corpus/prose/manifest.json +512 -0
  10. package/corpus/prose/sqlite/LICENSE-NOTICE +53 -0
  11. package/corpus/prose/sqlite/arch.txt +213 -0
  12. package/corpus/prose/sqlite/atomiccommit.txt +1117 -0
  13. package/corpus/prose/sqlite/faq.txt +473 -0
  14. package/corpus/prose/sqlite/fileformat.txt +1589 -0
  15. package/corpus/prose/sqlite/lang_createtable.txt +1339 -0
  16. package/corpus/prose/sqlite/lang_insert.txt +580 -0
  17. package/corpus/prose/sqlite/lang_select.txt +3293 -0
  18. package/corpus/prose/sqlite/optoverview.txt +908 -0
  19. package/corpus/prose/sqlite/queryplanner.txt +447 -0
  20. package/corpus/prose/sqlite/transactional.txt +41 -0
  21. package/corpus/prose/sqlite/wal.txt +567 -0
  22. package/corpus/prose/sqlite/whentouse.txt +300 -0
  23. package/corpus/prose/wikipedia/Apple.txt +4 -0
  24. package/corpus/prose/wikipedia/Attempto_Controlled_English.txt +169 -0
  25. package/corpus/prose/wikipedia/Automated_planning_and_scheduling.txt +67 -0
  26. package/corpus/prose/wikipedia/Bee.txt +7 -0
  27. package/corpus/prose/wikipedia/Bird.txt +8 -0
  28. package/corpus/prose/wikipedia/Bone.txt +4 -0
  29. package/corpus/prose/wikipedia/Book.txt +7 -0
  30. package/corpus/prose/wikipedia/Bread.txt +6 -0
  31. package/corpus/prose/wikipedia/Butterfly.txt +6 -0
  32. package/corpus/prose/wikipedia/Car.txt +1 -0
  33. package/corpus/prose/wikipedia/Cat.txt +1 -0
  34. package/corpus/prose/wikipedia/Child.txt +3 -0
  35. package/corpus/prose/wikipedia/City.txt +2 -0
  36. package/corpus/prose/wikipedia/Clock.txt +2 -0
  37. package/corpus/prose/wikipedia/Cooking.txt +1 -0
  38. package/corpus/prose/wikipedia/Description_logic.txt +660 -0
  39. package/corpus/prose/wikipedia/Doctor.txt +6 -0
  40. package/corpus/prose/wikipedia/Dog.txt +4 -0
  41. package/corpus/prose/wikipedia/Eagle.txt +4 -0
  42. package/corpus/prose/wikipedia/Emotion.txt +9 -0
  43. package/corpus/prose/wikipedia/Eye.txt +5 -0
  44. package/corpus/prose/wikipedia/Family.txt +3 -0
  45. package/corpus/prose/wikipedia/Farm.txt +4 -0
  46. package/corpus/prose/wikipedia/Fear.txt +4 -0
  47. package/corpus/prose/wikipedia/First-order_logic.txt +1518 -0
  48. package/corpus/prose/wikipedia/Fish.txt +10 -0
  49. package/corpus/prose/wikipedia/Flower.txt +3 -0
  50. package/corpus/prose/wikipedia/Food.txt +10 -0
  51. package/corpus/prose/wikipedia/Grass.txt +9 -0
  52. package/corpus/prose/wikipedia/Hand.txt +2 -0
  53. package/corpus/prose/wikipedia/Happiness.txt +3 -0
  54. package/corpus/prose/wikipedia/Heart.txt +4 -0
  55. package/corpus/prose/wikipedia/Horse.txt +4 -0
  56. package/corpus/prose/wikipedia/House.txt +6 -0
  57. package/corpus/prose/wikipedia/Human.txt +4 -0
  58. package/corpus/prose/wikipedia/Insect.txt +6 -0
  59. package/corpus/prose/wikipedia/Interactive_fiction.txt +112 -0
  60. package/corpus/prose/wikipedia/Knowledge.txt +5 -0
  61. package/corpus/prose/wikipedia/Knowledge_representation_and_reasoning.txt +87 -0
  62. package/corpus/prose/wikipedia/LICENSE-NOTICE +94 -0
  63. package/corpus/prose/wikipedia/Language.txt +10 -0
  64. package/corpus/prose/wikipedia/Learning.txt +4 -0
  65. package/corpus/prose/wikipedia/Mammal.txt +3 -0
  66. package/corpus/prose/wikipedia/Memory.txt +5 -0
  67. package/corpus/prose/wikipedia/Milk.txt +1 -0
  68. package/corpus/prose/wikipedia/Mountain.txt +1 -0
  69. package/corpus/prose/wikipedia/Natural_language_processing.txt +211 -0
  70. package/corpus/prose/wikipedia/Ostrich.txt +2 -0
  71. package/corpus/prose/wikipedia/Owl.txt +2 -0
  72. package/corpus/prose/wikipedia/Penguin.txt +2 -0
  73. package/corpus/prose/wikipedia/Plant.txt +5 -0
  74. package/corpus/prose/wikipedia/Rain.txt +1 -0
  75. package/corpus/prose/wikipedia/Resource_Description_Framework.txt +184 -0
  76. package/corpus/prose/wikipedia/River.txt +1 -0
  77. package/corpus/prose/wikipedia/School.txt +8 -0
  78. package/corpus/prose/wikipedia/Sea.txt +1 -0
  79. package/corpus/prose/wikipedia/Semantic_Web.txt +114 -0
  80. package/corpus/prose/wikipedia/Semantic_reasoner.txt +29 -0
  81. package/corpus/prose/wikipedia/Snow.txt +5 -0
  82. package/corpus/prose/wikipedia/Sun.txt +5 -0
  83. package/corpus/prose/wikipedia/Teacher.txt +4 -0
  84. package/corpus/prose/wikipedia/Team.txt +3 -0
  85. package/corpus/prose/wikipedia/Text-based_game.txt +17 -0
  86. package/corpus/prose/wikipedia/Tool.txt +4 -0
  87. package/corpus/prose/wikipedia/Tree.txt +7 -0
  88. package/corpus/prose/wikipedia/Weather.txt +4 -0
  89. package/corpus/prose/wikipedia/Web_Ontology_Language.txt +133 -0
  90. package/corpus/prose/wikipedia/Wind.txt +8 -0
  91. package/corpus/prose/wikipedia/Writing.txt +5 -0
  92. package/corpus/seon/README.md +1 -0
  93. package/corpus/tier2/generate.mjs +18 -18
  94. package/corpus/tier2/manifest.json +3 -3
  95. package/data/games/hanoi-3.txt +8 -2
  96. package/package.json +24 -5
  97. package/src/adapters/corpus-lanes.mjs +13 -0
  98. package/src/adapters/graph-build.mjs +5 -7
  99. package/src/adapters/import-closure.mjs +28 -0
  100. package/src/adapters/memory/blocks.mjs +5 -4
  101. package/src/adapters/memory/core.mjs +78 -5
  102. package/src/adapters/memory/shacl.mjs +12 -0
  103. package/src/adapters/providers/graph-service.mjs +12 -5
  104. package/src/adapters/tracked-files.mjs +17 -0
  105. package/src/domain/ask-vocab.mjs +2 -0
  106. package/src/domain/ask.mjs +225 -13
  107. package/src/domain/cli-verbs.mjs +201 -0
  108. package/src/domain/codegraph.mjs +142 -56
  109. package/src/domain/completions/graph-adapter.mjs +1 -1
  110. package/src/domain/completions/group.mjs +3 -17
  111. package/src/domain/completions/infer.mjs +4 -13
  112. package/src/domain/completions/rank.mjs +6 -19
  113. package/src/domain/grammar/lexicon-core.json +1 -1
  114. package/src/domain/hash.mjs +36 -13
  115. package/src/domain/interpret/fuzzy.mjs +7 -2
  116. package/src/domain/interpret/normalize.mjs +9 -0
  117. package/src/domain/interpret/strategies/keywords.mjs +19 -9
  118. package/src/domain/memory/capability.mjs +22 -3
  119. package/src/domain/memory/touched-facts.mjs +17 -0
  120. package/src/domain/module-paths.mjs +9 -0
  121. package/src/domain/persona/tiers.mjs +1 -1
  122. package/src/domain/planning.mjs +37 -0
  123. package/src/domain/prose.mjs +10 -2
  124. package/src/domain/relative-specifiers.mjs +12 -0
  125. package/src/domain/router/registry.mjs +3 -2
  126. package/src/domain/router/results.mjs +5 -18
  127. package/src/domain/seeded-random.mjs +33 -0
  128. package/src/domain/syllogise.mjs +10 -7
  129. package/src/domain/text-stats.mjs +31 -0
  130. package/src/services/chat.mjs +720 -182
  131. package/src/services/extract-facts.mjs +155 -0
  132. package/src/services/import-file.mjs +2 -2
  133. package/src/services/ledger-viz.mjs +6 -1
  134. package/src/services/sentences.mjs +26 -0
  135. package/src/surfaces/web/memory-ask-browser.bundle.js +11390 -360
  136. package/src/tools/graph-load.mjs +7 -1
  137. package/src/tools/readme-docs.mjs +113 -0
  138. package/src/tools/schema-docs.mjs +2 -2
  139. package/corpus/namenet/generate.mjs +0 -309
  140. package/corpus/wordnet/generate.mjs +0 -332
  141. package/src/adapters/prose-tokens.mjs +0 -98
  142. package/src/adapters/wordnet-source.mjs +0 -70
  143. package/src/domain/corpus-matrix.mjs +0 -87
  144. package/src/domain/inflect.mjs +0 -67
  145. package/src/domain/licences.mjs +0 -68
  146. package/src/domain/markdown-links.mjs +0 -55
  147. package/src/domain/persona/codegen.mjs +0 -123
  148. package/src/domain/publish-gate.mjs +0 -41
  149. package/src/domain/schemaorg/turtle.mjs +0 -25
  150. package/src/domain/semcor/parse.mjs +0 -87
  151. package/src/domain/version-stamp.mjs +0 -36
  152. package/src/domain/wordnet/yaml.mjs +0 -133
@@ -0,0 +1,155 @@
1
+ // `tmct extract` — turn a plain text file into facts by reusing the SAME
2
+ // deterministic recognizer the interactive chat's "teach" lane already has
3
+ // (runTurn, src/services/chat.mjs) — no new NLU, no LLM, no guessing.
4
+ //
5
+ // tmct extract <text-file> [--repo <path>] [--out <file.jsonl>]
6
+ //
7
+ // The text file is named positionally, or with --file, the way `tmct import`
8
+ // names one.
9
+ //
10
+ // How it works: the file is split into sentences with wink-nlp's own
11
+ // sentence-boundary detection (src/adapters/wink-model.mjs — the same leaf loader
12
+ // src/adapters/ask-nlp.mjs/src/adapters/prose-nlp.mjs already use; never a naive regex split).
13
+ // Each sentence is fed through runTurn() exactly as if an operator had typed
14
+ // it into the live chat. A sentence the recognizer turns into a stored fact
15
+ // (record.via === "assert", record.miss === false) is kept; every other
16
+ // sentence — a question, a fragment, a scene-setting clause, anything
17
+ // outside the recognized teach-frame surface — is silently SKIPPED. This is
18
+ // an honest partial extraction, an "attempt", never full NLU: nothing here
19
+ // ever paraphrases or invents a fact the recognizer itself didn't produce.
20
+ //
21
+ // --repo <path> write straight into that repo's own tmct memory (runTurn's
22
+ // normal memoryDir write path — "grow my own tmct memory
23
+ // from a document").
24
+ // (no --repo) nothing on disk is mutated. Each sentence runs against an
25
+ // ephemeral scratch memory dir (deleted when the run ends);
26
+ // whatever gets recognized is printed as ConceptNet/tmct-
27
+ // shape JSONL ({subject, predicate, object, provenance}) —
28
+ // to stdout, or to --out <file.jsonl> if given.
29
+ //
30
+ // Every recognized fact ALSO gets a second, additive provenance tag —
31
+ // extracted:<source-file-basename> — layered on top of whatever the
32
+ // recognizer itself already wrote (ace:chat:…/teach:chat:…), via appendFact's
33
+ // existing provenance UNION (memory/core.mjs). That keeps an extracted fact
34
+ // auditable as "this document evidenced this claim", distinct from live
35
+ // operator speech or a curated corpus, at its own trust-prior tier
36
+ // (SOURCE_PRIOR.extracted, memory/trust.mjs — see that file's comment for
37
+ // the reasoning: same closed-set recognizer as `teach`, but an unvetted
38
+ // source document, so it sits just above `web`).
39
+ //
40
+ // Never claims full coverage: the summary this prints always states how many
41
+ // sentences were found, how many were recognized, and how many were honestly
42
+ // skipped.
43
+
44
+ import { readFile, writeFile, mkdtemp, rm } from "node:fs/promises";
45
+ import { tmpdir } from "node:os";
46
+ import { basename, join, resolve } from "node:path";
47
+
48
+ import { runTurn, uuidv7 } from "./chat.mjs";
49
+ import { splitSentencesPreservingPaths } from "./sentences.mjs";
50
+ import { loadMemory, readFactRows, appendFact } from "../adapters/memory/core.mjs";
51
+ import { loadConfig } from "../adapters/config.mjs";
52
+ import { touchedFactRows } from "../domain/memory/touched-facts.mjs";
53
+
54
+ export const USAGE = "usage: tmct extract <text-file>|--file <text-file> [--repo <path>] [--out <file.jsonl>]";
55
+
56
+ export function parseArgs(argv) {
57
+ const args = { file: null, repo: null, out: null };
58
+ const rest = [];
59
+ for (let i = 0; i < argv.length; i += 1) {
60
+ const a = argv[i];
61
+ if (a === "--repo") args.repo = argv[i += 1];
62
+ else if (a === "--out") args.out = argv[i += 1];
63
+ else if (a === "--file") args.file = argv[i += 1];
64
+ else rest.push(a);
65
+ }
66
+ args.file = args.file || rest[0] || null;
67
+ return args;
68
+ }
69
+
70
+ /**
71
+ * Run one already-split sentence through runTurn against `memoryDir`, and
72
+ * report the Fact rows THIS turn actually touched. Returns { recognized, rows }
73
+ * — `recognized` true iff runTurn's own record called this a stored assertion,
74
+ * `rows` the Fact rows it touched (possibly empty for a Rule-only write).
75
+ */
76
+ async function runSentence(sentence, { config, memoryDir }) {
77
+ const before = readFactRows(await loadMemory(memoryDir));
78
+ const { record } = await runTurn(sentence, { config, memoryDir, sessionId: uuidv7() });
79
+ if (record?.via !== "assert" || record?.miss) return { recognized: false, rows: [] };
80
+ const after = readFactRows(await loadMemory(memoryDir));
81
+ return { recognized: true, rows: touchedFactRows(before, after) };
82
+ }
83
+
84
+ /**
85
+ * `argv` defaults to the real CLI args (process.argv.slice(2)) but takes an
86
+ * explicit array too, so a test can drive this exactly like the CLI does
87
+ * without touching global process.argv. Returns { sentences, recognized,
88
+ * extracted } (the same counts the printed summary reports) so a test can
89
+ * assert on structured results instead of scraping console output.
90
+ */
91
+ export async function main(argv = process.argv.slice(2)) {
92
+ const { file, repo, out } = parseArgs(argv);
93
+ if (!file) {
94
+ console.error(USAGE);
95
+ process.exitCode = 1;
96
+ return { sentences: 0, recognized: 0, extracted: [] };
97
+ }
98
+
99
+ const filePath = resolve(process.cwd(), file);
100
+ const text = await readFile(filePath, "utf8");
101
+ const sourceTag = basename(filePath);
102
+ const sentences = splitSentencesPreservingPaths(text);
103
+
104
+ const ephemeral = !repo;
105
+ const memoryDir = repo ? resolve(process.cwd(), repo) : await mkdtemp(join(tmpdir(), "tmct-extract-"));
106
+ const config = loadConfig(process.env, memoryDir);
107
+
108
+ try {
109
+ const extracted = [];
110
+ let recognizedSentences = 0;
111
+
112
+ for (const sentence of sentences) {
113
+ const { recognized, rows } = await runSentence(sentence, { config, memoryDir });
114
+ if (!recognized || !rows.length) continue; // unrecognized shape, or a Rule (not a Fact) — skip, honest
115
+ recognizedSentences += 1;
116
+ const tag = `extracted:${sourceTag}`;
117
+ for (const row of rows) {
118
+ // Additive: layers the audit tag onto the SAME (subject, predicate,
119
+ // object) the recognizer just stored — appendFact unions provenance
120
+ // by id, so this never duplicates or overwrites the recognizer's own
121
+ // ace:chat:/teach:chat: entry, and re-running this over the same
122
+ // file/fact is idempotent (the union simply dedupes the tag).
123
+ await appendFact(memoryDir, {
124
+ subject: row.subject, predicate: row.predicate, object: row.object,
125
+ provenance: tag, quantifier: row.quantifier || "",
126
+ });
127
+ extracted.push({
128
+ subject: row.subject, predicate: row.predicate, object: row.object,
129
+ provenance: tag, quantifier: row.quantifier || "", sentence,
130
+ });
131
+ }
132
+ }
133
+
134
+ if (out) {
135
+ const body = extracted.map((f) => JSON.stringify(f)).join("\n") + (extracted.length ? "\n" : "");
136
+ await writeFile(resolve(process.cwd(), out), body, "utf8");
137
+ } else if (ephemeral) {
138
+ for (const f of extracted) console.log(JSON.stringify(f));
139
+ }
140
+
141
+ const skipped = sentences.length - recognizedSentences;
142
+ console.error(
143
+ `${sentences.length} sentence${sentences.length === 1 ? "" : "s"} found, `
144
+ + `${recognizedSentences} recognized as fact${recognizedSentences === 1 ? "" : "s"} `
145
+ + `(${extracted.length} fact row${extracted.length === 1 ? "" : "s"}), `
146
+ + `${skipped} skipped — not a recognized declarative shape (an honest, expected gap; this is `
147
+ + `an attempt, not full NLU).`,
148
+ );
149
+ if (repo) console.error(`facts written into ${memoryDir}'s tmct memory, tagged ${sourceTag}`);
150
+ if (out) console.error(`facts written to ${out}`);
151
+ return { sentences: sentences.length, recognized: recognizedSentences, extracted };
152
+ } finally {
153
+ if (ephemeral) await rm(memoryDir, { recursive: true, force: true });
154
+ }
155
+ }
@@ -15,7 +15,7 @@ import { basename, resolve } from "node:path";
15
15
  import { runTurn, uuidv7 } from "./chat.mjs";
16
16
  import { loadMemory, readFactRows, appendFact, openMemoryBackend } from "../adapters/memory/core.mjs";
17
17
  import { loadConfig } from "../adapters/config.mjs";
18
- import { splitSentences } from "./sentences.mjs";
18
+ import { splitSentencesPreservingPaths } from "./sentences.mjs";
19
19
 
20
20
  /**
21
21
  * Teach every sentence of `filePath` into `repoRoot`'s memory store.
@@ -34,7 +34,7 @@ export async function importDefinitionFile(repoRoot, filePath, { env = process.e
34
34
  const lines = text.split("\n");
35
35
  const commentLines = lines.filter((l) => l.trim().startsWith("#"));
36
36
  const body = lines.filter((l) => !l.trim().startsWith("#")).join("\n");
37
- const sentences = splitSentences(body).map((s) => s.trim()).filter(Boolean);
37
+ const sentences = splitSentencesPreservingPaths(body);
38
38
 
39
39
  const { loadTomlConfig } = await import("../adapters/toml-config.mjs");
40
40
  const raw = await loadTomlConfig(root).catch(() => null);
@@ -1,6 +1,11 @@
1
1
  // ledger-viz.mjs — `tmct viz`: the memory graph as a readable ledger of
2
2
  // fact-sentences around one focus term, with the in-page chat dock.
3
3
  //
4
+ // "ledger" is a UI label for this VIEW, not a storage claim. The memory graph
5
+ // underneath is mutable — upsert rewrites a fact in place and removeFacts
6
+ // deletes — so this renders a report over the current graph, not an
7
+ // append-only or immutable log (the ISO 22739 sense of the word does not hold).
8
+ //
4
9
  // Three pure/impure-separated pieces:
5
10
  // - computeLedgerData(repoDir, opts) — I/O (loadMemory) + derivation
6
11
  // - computeLedgerDataFromPayload(payload) — the pure derivation half
@@ -409,7 +414,7 @@ ${hasMemChat ? `<script>\n${bundleStr}\n</script>` : ""}
409
414
  const DAY = 86400000;
410
415
  const facetCounts = ${facetCounts.toString()};
411
416
  const el = (id) => document.getElementById(id);
412
- const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
417
+ const esc = ${escapeHtml.toString()};
413
418
  const FAMS = ["is-a", "has", "can", "used-for", "rests-on", "role", "other"];
414
419
  const FAM_LABEL = { "is-a": "is a kind of", has: "has", can: "can", "used-for": "used for", "rests-on": "rests on", role: "role / property", other: "other" };
415
420
  const PROVS = [["taught", "you taught"], ["corpus", "corpus"], ["entail", "entailed"]];
@@ -17,3 +17,29 @@ export function splitSentences(text) {
17
17
  const doc = nlp.readDoc(raw);
18
18
  return doc.sentences().out().map((s) => s.trim()).filter(Boolean);
19
19
  }
20
+
21
+ /** Does this line actually carry a sentence boundary — a terminator, then
22
+ * whitespace, then the next sentence's first word? wink's own splitter breaks
23
+ * "src/core/store.mjs" into "src/core/store." + "mjs …", so a line naming a
24
+ * file splits into sentences that were never there. Requiring the whitespace
25
+ * keeps a dotted identifier whole, and nothing that holds no boundary is ever
26
+ * handed to the splitter's judgement. */
27
+ export const carriesASentenceBoundary = (line) => /[.!?]\s+\w/.test(String(line));
28
+
29
+ /** Split multi-line text into sentences the way splitSentences does, but never
30
+ * let wink shatter a line that has no real sentence boundary. A bare dotted
31
+ * module path (`src/core/store.mjs`) would otherwise come back as
32
+ * ["src/core/store.", "mjs"]. Each line is only handed to wink when it carries
33
+ * a real boundary; otherwise the trimmed line stands as one sentence. */
34
+ export function splitSentencesPreservingPaths(text) {
35
+ const out = [];
36
+ for (const line of String(text ?? "").split("\n")) {
37
+ if (carriesASentenceBoundary(line)) {
38
+ out.push(...splitSentences(line));
39
+ } else {
40
+ const trimmed = line.trim();
41
+ if (trimmed) out.push(trimmed);
42
+ }
43
+ }
44
+ return out;
45
+ }