@polycode-projects/the-mechanical-code-talker 1.10.13 → 1.11.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.
- package/README.md +115 -101
- package/ROADMAP.md +17 -4
- package/bin/tmct.mjs +115 -9
- package/data/games/crates.txt +24 -0
- package/data/games/hanoi-3.txt +30 -0
- package/package.json +1 -1
- package/src/ask-browser.bundle.js +129 -398
- package/src/chat.mjs +613 -13
- package/src/domain.mjs +271 -0
- package/src/import-file.mjs +86 -0
- package/src/init.mjs +46 -1
- package/src/ledger-viz.mjs +613 -0
- package/src/memory/core.mjs +80 -16
- package/src/memory/shacl.mjs +17 -7
- package/src/memory-ask-browser-entry.mjs +4 -2
- package/src/memory-ask-browser.bundle.js +5157 -1165
- package/src/plan-viz.mjs +410 -0
- package/src/router/guardrail.mjs +5 -0
- package/src/router/registry.mjs +55 -11
- package/src/router/taught.mjs +73 -0
- package/src/sentences.mjs +19 -0
- package/src/viz-theme.mjs +50 -0
- package/src/viz.mjs +2 -2
- package/src/wink-model.mjs +12 -6
package/README.md
CHANGED
|
@@ -114,6 +114,23 @@ tmct> /exit
|
|
|
114
114
|
**[Try it live in your browser →](https://polycode-projects.gitlab.io/the-mechanical-code-talker/)**
|
|
115
115
|
is a real, interactive chat demo running client-side. Your browser runs the
|
|
116
116
|
actual query engine against a small example codebase, no server, no install.
|
|
117
|
+
The page leads with the **memory ledger**: every fact as a readable sentence,
|
|
118
|
+
drill by clicking the terms inside them, and an in-page chat whose answers
|
|
119
|
+
focus the ledger.
|
|
120
|
+
|
|
121
|
+
Two more surfaces, both generated by tmct itself:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx tmct viz --ledger # ledger.html — your own memory as the same
|
|
125
|
+
# readable, self-contained explorer
|
|
126
|
+
npx tmct init
|
|
127
|
+
npx tmct import --file .tmct/imports/games/hanoi-3.txt
|
|
128
|
+
npx tmct chat --prompt 'disk-1 rests on disk-2. disk-2 rests on disk-3.
|
|
129
|
+
disk-3 rests on peg-a. the goal is that every disk rests on peg-c. solve it.' \
|
|
130
|
+
--render blocks --output plan.html # an animated replay of the solved plan
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
More on the game file and the planner under "Teach it a game" below.
|
|
117
134
|
|
|
118
135
|
## How it interprets you
|
|
119
136
|
|
|
@@ -127,65 +144,38 @@ Those triples are statements it can store, retrieve, and answer from later.
|
|
|
127
144
|
Text that doesn't fit the grammar still gets the tolerant strategies. Nothing
|
|
128
145
|
is rejected for being loose, fuzzy, or misspelled.
|
|
129
146
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
or attribute from the graph, never a hand-written diff.
|
|
159
|
-
|
|
160
|
-
**Up-refining to a containing module.** A class rarely has its own
|
|
161
|
-
symbol-precise commit or import record. "who touched TaskController" answers
|
|
162
|
-
from its containing module's real history instead of a confident-looking but
|
|
163
|
-
wrong "nothing touched it".
|
|
164
|
-
|
|
165
|
-
**Following a list.** tmct remembers the last list it gave you. After
|
|
166
|
-
"which modules import src/core/model.mjs", "which of those are tested" or
|
|
167
|
-
"how many of those" resolves "those"/"them" against that list, not a fresh,
|
|
168
|
-
unresolved pronoun.
|
|
169
|
-
|
|
170
|
-
**Synonyms and everyday phrasing.** tmct matches many of the words people
|
|
171
|
-
actually use for the same idea, from a curated synonym list plus a filtered
|
|
172
|
-
ConceptNet slice. A slightly different word for the same concept still
|
|
173
|
-
resolves. It also follows a few common sentence shapes: clauses starting with
|
|
174
|
-
*because/although/while*, and conditionals ("if X were removed, what
|
|
175
|
-
breaks"). It flags a question whose premise doesn't hold, too: "why does X
|
|
176
|
-
still import Y" when it no longer does.
|
|
177
|
-
|
|
178
|
-
**Response finishing.** Before an answer is printed it is segmented into typed
|
|
179
|
-
spans: prose versus *protected* entities, paths, numbers, code, provenance, and
|
|
180
|
-
receipts. A small data-driven grammar pass then runs on the prose spans only,
|
|
181
|
-
under a guard that proves the protected spans came through byte-for-byte. Today
|
|
182
|
-
that pass fixes the a/an article defect. Broader voice and agreement rules are
|
|
183
|
-
implemented but parked until they earn their place on the benchmark.
|
|
147
|
+
On top of that base, tmct reads the shapes people actually use, and each one
|
|
148
|
+
resolves to a real graph traversal or declines honestly:
|
|
149
|
+
|
|
150
|
+
- everyday question forms: "what is Commit", "what's model.mjs for",
|
|
151
|
+
"recent commits" as real dated history;
|
|
152
|
+
- polite or indirect framing: "I'd like you to remember X" teaches like bare
|
|
153
|
+
"remember X";
|
|
154
|
+
- negation as a bounded **set complement**: "which modules do *not* import
|
|
155
|
+
X?", with an empty result staying a miss, never a fabricated list;
|
|
156
|
+
- reversible passives: "what is imported by Y" and "what does Y import" are
|
|
157
|
+
opposite edges, not the same one;
|
|
158
|
+
- finding by description: "find me the payment class" checks the type and
|
|
159
|
+
its subclasses first, and says so plainly when it widens;
|
|
160
|
+
- comparison: "compare TaskController and UserController" lines up both
|
|
161
|
+
entities' real edges side by side, never a hand-written diff;
|
|
162
|
+
- list follow-ups: after "which modules import src/core/model.mjs",
|
|
163
|
+
"which of those are tested" resolves against that list;
|
|
164
|
+
- curated synonyms plus a filtered ConceptNet slice, clause openers
|
|
165
|
+
(*because/although/while*), conditionals, and false-premise flags ("why
|
|
166
|
+
does X still import Y" when it no longer does).
|
|
167
|
+
|
|
168
|
+
The full catalog with measured coverage lives in `CAPABILITIES_1.7.3.md` and
|
|
169
|
+
the `BENCHMARK_*.md` reports.
|
|
170
|
+
|
|
171
|
+
**Response finishing.** Before an answer prints, it is segmented into typed
|
|
172
|
+
spans: prose versus *protected* entities, paths, numbers, code, provenance,
|
|
173
|
+
and receipts. A small data-driven grammar pass runs on the prose spans only,
|
|
174
|
+
under a guard that proves the protected spans came through byte-for-byte.
|
|
184
175
|
|
|
185
176
|
A frozen regression suite plays out full multi-turn dialogues built from these
|
|
186
|
-
phrasings,
|
|
187
|
-
|
|
188
|
-
in `HANDOVER.md` and `ROADMAP.md`.
|
|
177
|
+
phrasings, from a single question up to a messy, typo-ridden real user.
|
|
178
|
+
Tier-by-tier detail is in `HANDOVER.md` and `ROADMAP.md`.
|
|
189
179
|
|
|
190
180
|
## How it guides you
|
|
191
181
|
|
|
@@ -250,13 +240,7 @@ const memory = await loadMemory(dir);
|
|
|
250
240
|
const graphService = createCompletionsGraphAdapter(graph, memory);
|
|
251
241
|
|
|
252
242
|
const { text } = await generateCompletion(dir, "Store", { query: "Store", graph, memory, graphService });
|
|
253
|
-
console.log(text);
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
Attribute: prose_tokens = memory record store [mgx:hasProseTokens]. Attribute:
|
|
258
|
-
doc = In-memory record store. [seon:hasDoc]. Other matches: src/core/store.mjs
|
|
259
|
-
(Module), loadStore (Function), saveStore (Function), testLoadStore (Function).
|
|
243
|
+
console.log(text); // prints the same text as the chat answer above
|
|
260
244
|
```
|
|
261
245
|
|
|
262
246
|
Full pipeline design in `archive/PLAN_COMPLETIONS.md`.
|
|
@@ -328,6 +312,45 @@ proof chain, composed answer) for a caller that wants to consume this
|
|
|
328
312
|
programmatically rather than read the report. `tmct plan --help` has the full
|
|
329
313
|
flag reference.
|
|
330
314
|
|
|
315
|
+
## Teach it a game, then ask it to plan
|
|
316
|
+
|
|
317
|
+
The planner above works over a fixed toolset. This one works over rules you
|
|
318
|
+
teach. A game definition is a plain-text file of controlled English — the
|
|
319
|
+
classes, the pieces, the ordering, and the legal moves as taught action
|
|
320
|
+
rules — with `#` comment lines carrying example prompts. `tmct init`
|
|
321
|
+
scaffolds one at `.tmct/imports/games/hanoi-3.txt`, and
|
|
322
|
+
`tmct import --file` teaches it sentence by sentence, reporting every line
|
|
323
|
+
and refusing (exit 1) if any sentence declines.
|
|
324
|
+
|
|
325
|
+
Then one message states the board and the goal, and "solve it" searches the
|
|
326
|
+
taught rules for the shortest move sequence:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
tmct> disk-1 rests on disk-2. disk-2 rests on disk-3. disk-3 rests on peg-a.
|
|
330
|
+
the goal is that every disk rests on peg-c. solve it.
|
|
331
|
+
plan found — 7 moves (shortest):
|
|
332
|
+
1. move disk-1 onto peg-c
|
|
333
|
+
2. move disk-2 onto peg-b
|
|
334
|
+
3. move disk-1 onto disk-2
|
|
335
|
+
4. move disk-3 onto peg-c
|
|
336
|
+
5. move disk-1 onto peg-a
|
|
337
|
+
6. move disk-2 onto disk-3
|
|
338
|
+
7. move disk-1 onto disk-2
|
|
339
|
+
|
|
340
|
+
because — you taught me the "move onto" rule and 3 ordering facts. Say "next" to make move 1, or ask "what moves are legal now".
|
|
341
|
+
|
|
342
|
+
Goal (inferred): Plan a move sequence from the current state to the goal (7 moves).
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
"next" executes one move at a time, writing each board state into memory as
|
|
346
|
+
facts; the final step re-reads those facts and confirms the goal from them,
|
|
347
|
+
never assuming success. The search is genuine and domain-general: the test
|
|
348
|
+
suite teaches Towers of Hanoi purely as sentences for 1 to 8 disks and
|
|
349
|
+
asserts the plan is exactly 2^n − 1 moves every time, and a second game
|
|
350
|
+
(`crates.txt`, stacking crates with different rules and a two-goal
|
|
351
|
+
conjunction) solves with zero interpreter changes. `--render blocks` writes
|
|
352
|
+
the plan as a self-contained animated page (see "Two more surfaces" above).
|
|
353
|
+
|
|
331
354
|
## How it remembers
|
|
332
355
|
|
|
333
356
|
tmct's memory has two layers, both fed by every parsed request and response and
|
|
@@ -359,38 +382,21 @@ reasoning). Design detail and the full fact-count tables are in `archive/PLAN_SE
|
|
|
359
382
|
The default memory backend writes an OWL-labelled JSON file under `.tmct/`.
|
|
360
383
|
Two more exist: `memory` keeps taught facts in the process only, nothing
|
|
361
384
|
written to disk; `sqlite` persists them to a local SQLite file instead
|
|
362
|
-
(`.tmct/memory/graph.sqlite`).
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
```bash
|
|
369
|
-
tmct init --memory-backend sqlite # writes [memory] backend = "sqlite" to tmct.toml
|
|
370
|
-
tmct chat # picks it up automatically
|
|
371
|
-
tmct chat --memory-backend memory # override for just this session
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
Precedence is `--memory-backend` flag > `TMCT_MEMORY_BACKEND` env > tmct.toml's
|
|
375
|
-
`[memory] backend` > the default. A library caller sets the same thing
|
|
376
|
-
directly: `runChat({ memoryBackend: "sqlite" })`.
|
|
385
|
+
(`.tmct/memory/graph.sqlite`). `tmct init --memory-backend sqlite` writes the
|
|
386
|
+
choice into `tmct.toml` and every later `tmct chat` in that repo picks it up.
|
|
387
|
+
Precedence is `--memory-backend` flag > `TMCT_MEMORY_BACKEND` env >
|
|
388
|
+
tmct.toml's `[memory] backend` > the default. A library caller sets the same
|
|
389
|
+
thing directly: `runChat({ memoryBackend: "sqlite" })`.
|
|
377
390
|
|
|
378
391
|
Teaching isn't limited to the ACE grammar's fixed shapes. Tell tmct an
|
|
379
392
|
arbitrary fact, like "margo really eats ribs", and it mints a fact you can
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
nudges you to ground one side first. Quantified teaching works too: "some
|
|
388
|
-
functions are risky" stores the quantifier, and a later "how many functions
|
|
389
|
-
are risky" answers "A few."
|
|
390
|
-
|
|
391
|
-
Once you've taught a few facts, "how many facts are there" counts them back.
|
|
392
|
-
That's the same count phrasing a code graph answers "how many classes are
|
|
393
|
-
there" with, just now reading tmct's own memory.
|
|
393
|
+
ask about directly: "what does margo eat". New vocabulary compounds as you
|
|
394
|
+
teach: "redis is a cache" mints "redis" even though it was never in the
|
|
395
|
+
built-in lexicon, as long as one side of the sentence is already grounded —
|
|
396
|
+
tmct never mints a fact between two totally ungrounded terms; it declines and
|
|
397
|
+
nudges you to ground one side first. Quantified teaching stores the
|
|
398
|
+
quantifier ("some functions are risky" … "how many functions are risky" →
|
|
399
|
+
"A few."), and "how many facts are there" counts the store back.
|
|
394
400
|
|
|
395
401
|
### Provenance and trust
|
|
396
402
|
|
|
@@ -410,10 +416,8 @@ provenance** rather than silently picking a winner.
|
|
|
410
416
|
|
|
411
417
|
`tmct syllogise [--depth n] [--budget n]` is an offline, bounded, deterministic
|
|
412
418
|
batch that forward-chains the memory's `rdfs:subClassOf` closure into new
|
|
413
|
-
**entailed** facts
|
|
414
|
-
|
|
415
|
-
**low-trust and retractable** (never outranking a stated fact) and this never runs
|
|
416
|
-
on the chat's hot path.
|
|
419
|
+
**entailed** facts. They are low-trust and retractable, never outranking a
|
|
420
|
+
stated fact, and this never runs on the chat's hot path.
|
|
417
421
|
|
|
418
422
|
## Install & use
|
|
419
423
|
|
|
@@ -461,6 +465,11 @@ Usage:
|
|
|
461
465
|
see src/graph-merge.mjs); wins over --repo/TMCT_GRAPH_FILE/tmct.toml
|
|
462
466
|
[--config <path>] an alternate tmct.toml location (a file or a directory)
|
|
463
467
|
[--ephemeral] read the graph but write nothing back (demo/read-only)
|
|
468
|
+
[--prompt "<text>"] one-shot: run the prompt's sentences as turns and print
|
|
469
|
+
the final answer (teach state first, trigger last)
|
|
470
|
+
[--render blocks] with --prompt: when the final turn produced a plan,
|
|
471
|
+
write it as a self-contained animated page
|
|
472
|
+
[--output <path>] the rendered page's path (default plan.html)
|
|
464
473
|
[--narrate] start with narrate mode on — a verbose, developer-facing
|
|
465
474
|
trace of decision points/matched pattern/results/goal per
|
|
466
475
|
turn, appended under a "--- narrate ---" marker (also
|
|
@@ -518,6 +527,9 @@ already set up. Its `--graph` flag works differently from the others: it appends
|
|
|
518
527
|
[--ontology <name|path>] DIFFERENT operation from the others: it APPENDS to
|
|
519
528
|
[--lexicon <name|path>] tmct.toml's graph_files array (multi-graph growth),
|
|
520
529
|
[--graph <path>] never an extensions-bundle activation.
|
|
530
|
+
[--file <definition.txt>] teach a plain-text definition file sentence by
|
|
531
|
+
sentence (# lines are comments); any declined
|
|
532
|
+
sentence exits non-zero with the sentence named
|
|
521
533
|
[--memory-backend <default|memory|sqlite>] same knob as `tmct init`
|
|
522
534
|
[--config <path>]
|
|
523
535
|
```
|
|
@@ -544,10 +556,12 @@ inference" above:
|
|
|
544
556
|
|
|
545
557
|
```
|
|
546
558
|
tmct viz [--repo <abs>] write one self-contained, navigable HTML file rendering the
|
|
547
|
-
[--
|
|
548
|
-
[--
|
|
549
|
-
[--
|
|
550
|
-
[--
|
|
559
|
+
[--ledger] memory graph: pan/zoom, click a node for its label/class/
|
|
560
|
+
[--focus <id>] timestamps. Seeds from the most recently created individual
|
|
561
|
+
[--term <word>] by default (--focus <id> or --term <word> override it);
|
|
562
|
+
[--depth <n>] --output defaults to graph.html in the cwd. With --ledger,
|
|
563
|
+
[--limit <n>] a readable fact ledger around one focus term instead
|
|
564
|
+
(writes ledger.html; --limit caps the embedded fact rows).
|
|
551
565
|
[--hub-degree <n>] --depth = max arcs (hops) from the focus node (default 3);
|
|
552
566
|
[--edge-kind <mode>] --limit = spiral length, total nodes walked (default 300);
|
|
553
567
|
[--output <path>] --hub-degree = stop expanding THROUGH a node above N
|
package/ROADMAP.md
CHANGED
|
@@ -10,7 +10,10 @@ open items, session-scoped), see `HANDOVER.md` instead — this file doesn't dup
|
|
|
10
10
|
A tolerant, ELIZA/PARRY-style chat surface over a codebase, obsessed with software the way PARRY was
|
|
11
11
|
obsessed with the mafia — deterministic, zero-cost, **no LLM anywhere in the product path**. Guides a
|
|
12
12
|
user toward precision queries rather than guessing; every answer is grounded, restates every genuine
|
|
13
|
-
reading it finds in full, or is an honest miss when nothing grounds it at all.
|
|
13
|
+
reading it finds in full, or is an honest miss when nothing grounds it at all. Its visual surfaces —
|
|
14
|
+
the ledger explorer with its in-browser chat (`tmct viz --ledger`), the animated plan page
|
|
15
|
+
(`chat --prompt … --render blocks`), and the Pages homepage hero — are the same graph read out loud:
|
|
16
|
+
same engine, same provenance, no LLM.
|
|
14
17
|
|
|
15
18
|
## Ambition
|
|
16
19
|
|
|
@@ -48,9 +51,19 @@ getting silently traded away by inherited caution:
|
|
|
48
51
|
and an NPC turn scheduler. Design-only.
|
|
49
52
|
- **`PLAN_SYLLOGIST.md`** — retraction-aware consistency checking under a hard budget and trust
|
|
50
53
|
tiers, the one open piece of the reasoning engine's research horizon. Design-only.
|
|
51
|
-
- **`
|
|
52
|
-
`
|
|
53
|
-
|
|
54
|
+
- **`PLAN_HANOI.md` follow-ups** — the plan itself shipped (taught game domains, the chat plan
|
|
55
|
+
lane, `tmct import --file` + the `.tmct/imports/` scaffold, the animated plan page; see its
|
|
56
|
+
implementation addendum). Remaining: river-crossing's two missing frames plus the multi-effect
|
|
57
|
+
interpreter extension, and planner-side consumption of the `taught:` capability records the
|
|
58
|
+
registry bridge now registers.
|
|
59
|
+
- **`PLAN_GUESS_NUMBER.md`** — the closed-loop (observe-and-replan) planning domain for the same
|
|
60
|
+
kernels. Design-only.
|
|
61
|
+
- **`PLAN_VIZ_LEDGER.md` follow-ups** — phases 1-4 shipped (the ledger explorer, its chat dock,
|
|
62
|
+
the Pages hero, this README pass). Remaining: whether the ledger becomes the default viz
|
|
63
|
+
surface; a `goal` field on `factAnswer`'s returns so the dock can carry the chat's goal line
|
|
64
|
+
(needs operator sign-off — it touches the ask engine); a predicate-cardinality question for
|
|
65
|
+
`findContradictions` (multi-valued `has`/`can` facts group under the same contract as genuine
|
|
66
|
+
disagreements); bundle weight if the ledger page outgrows its measured ~533 KB.
|
|
54
67
|
- **`PLAN_CODE.md`** — small JS-function and HTML/CSS-fragment synthesis via a sandboxed headless
|
|
55
68
|
browser (Track 1, program synthesis, already shipped). Blocked on a sandbox dependency decision.
|
|
56
69
|
- **`PLAN_AGENTS.md`** — the governing plan for tmct's broader multi-repo arc (marginalia, seonix,
|
package/bin/tmct.mjs
CHANGED
|
@@ -38,6 +38,11 @@ Usage:
|
|
|
38
38
|
see src/graph-merge.mjs); wins over --repo/TMCT_GRAPH_FILE/tmct.toml
|
|
39
39
|
[--config <path>] an alternate tmct.toml location (a file or a directory)
|
|
40
40
|
[--ephemeral] read the graph but write nothing back (demo/read-only)
|
|
41
|
+
[--prompt "<text>"] one-shot: run the prompt's sentences as turns and print
|
|
42
|
+
the final answer (teach state first, trigger last)
|
|
43
|
+
[--render blocks] with --prompt: when the final turn produced a plan,
|
|
44
|
+
write it as a self-contained animated page
|
|
45
|
+
[--output <path>] the rendered page's path (default plan.html)
|
|
41
46
|
[--narrate] start with narrate mode on — a verbose, developer-facing
|
|
42
47
|
trace of decision points/matched pattern/results/goal per
|
|
43
48
|
turn, appended under a "--- narrate ---" marker (also
|
|
@@ -77,6 +82,9 @@ Usage:
|
|
|
77
82
|
[--ontology <name|path>] DIFFERENT operation from the others: it APPENDS to
|
|
78
83
|
[--lexicon <name|path>] tmct.toml's graph_files array (multi-graph growth),
|
|
79
84
|
[--graph <path>] never an extensions-bundle activation.
|
|
85
|
+
[--file <definition.txt>] teach a plain-text definition file sentence by
|
|
86
|
+
sentence (# lines are comments); any declined
|
|
87
|
+
sentence exits non-zero with the sentence named
|
|
80
88
|
[--memory-backend <default|memory|sqlite>] same knob as \`tmct init\`
|
|
81
89
|
[--config <path>]
|
|
82
90
|
tmct extend --validate <dir> validate a third-party extension pack's declared
|
|
@@ -86,10 +94,12 @@ Usage:
|
|
|
86
94
|
[--depth <n>] [--budget <n>] chain the memory's rdfs:subClassOf closure, materialising
|
|
87
95
|
[--config <path>] bounded, low-trust, retractable entailed facts (never on the chat path)
|
|
88
96
|
tmct viz [--repo <abs>] write one self-contained, navigable HTML file rendering the
|
|
89
|
-
[--
|
|
90
|
-
[--
|
|
91
|
-
[--
|
|
92
|
-
[--
|
|
97
|
+
[--ledger] memory graph: pan/zoom, click a node for its label/class/
|
|
98
|
+
[--focus <id>] timestamps. Seeds from the most recently created individual
|
|
99
|
+
[--term <word>] by default (--focus <id> or --term <word> override it);
|
|
100
|
+
[--depth <n>] --output defaults to graph.html in the cwd. With --ledger,
|
|
101
|
+
[--limit <n>] a readable fact ledger around one focus term instead
|
|
102
|
+
(writes ledger.html; --limit caps the embedded fact rows).
|
|
93
103
|
[--hub-degree <n>] --depth = max arcs (hops) from the focus node (default 3);
|
|
94
104
|
[--edge-kind <mode>] --limit = spiral length, total nodes walked (default 300);
|
|
95
105
|
[--output <path>] --hub-degree = stop expanding THROUGH a node above N
|
|
@@ -596,6 +606,65 @@ async function main() {
|
|
|
596
606
|
if (graphPaths.length) extra.graphPaths = graphPaths;
|
|
597
607
|
if (configPath) extra.configPath = configPath;
|
|
598
608
|
if (memoryBackend) extra.memoryBackend = memoryBackend;
|
|
609
|
+
// `--prompt "<text>"` — one-shot mode: each sentence of the prompt runs as
|
|
610
|
+
// its own turn (state teaches first, the goal/solve trigger last), the
|
|
611
|
+
// FINAL turn's answer prints to stdout, and the process exits. Piped stdin
|
|
612
|
+
// stays the interactive fallback.
|
|
613
|
+
const prompt = strFlag(rest, ["--prompt"]);
|
|
614
|
+
// `--render <archetype> [--output <path>]` — after a one-shot --prompt whose
|
|
615
|
+
// final turn produced a plan, write the self-contained animated plan page
|
|
616
|
+
// (src/plan-viz.mjs). Requires --prompt: interactive chat has no single
|
|
617
|
+
// final turn to render.
|
|
618
|
+
let renderArchetype;
|
|
619
|
+
try {
|
|
620
|
+
renderArchetype = enumFlag(rest, ["--render"], ["blocks"]);
|
|
621
|
+
} catch (e) {
|
|
622
|
+
process.stderr.write(`tmct: ${e?.message || e}\n`);
|
|
623
|
+
process.exit(2);
|
|
624
|
+
}
|
|
625
|
+
if (renderArchetype && !prompt) {
|
|
626
|
+
process.stderr.write("tmct: --render needs --prompt (a one-shot turn whose plan it renders)\n");
|
|
627
|
+
process.exit(1);
|
|
628
|
+
}
|
|
629
|
+
if (prompt) {
|
|
630
|
+
const { createSession } = await import("../src/chat.mjs");
|
|
631
|
+
const { splitSentences } = await import("../src/sentences.mjs");
|
|
632
|
+
const session = await createSession({ repoPath, ephemeral, narrate, ...extra });
|
|
633
|
+
let finalAnswer = "";
|
|
634
|
+
let finalPlan = null;
|
|
635
|
+
let parts;
|
|
636
|
+
try { parts = splitSentences(prompt); } catch { parts = null; }
|
|
637
|
+
const sentences = parts && parts.length ? parts : [prompt];
|
|
638
|
+
for (const s of sentences) {
|
|
639
|
+
const t = await session.turn(s);
|
|
640
|
+
finalAnswer = t?.answer ?? "";
|
|
641
|
+
finalPlan = t?.plan ?? null;
|
|
642
|
+
}
|
|
643
|
+
await session.close();
|
|
644
|
+
process.stdout.write(finalAnswer + "\n");
|
|
645
|
+
if (renderArchetype) {
|
|
646
|
+
if (!finalPlan) {
|
|
647
|
+
process.stderr.write("the final turn produced no plan — nothing to render\n");
|
|
648
|
+
process.exitCode = 1;
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const { renderPlanHtml } = await import("../src/plan-viz.mjs");
|
|
652
|
+
const { writeFile } = await import("node:fs/promises");
|
|
653
|
+
const { resolve: resolvePath } = await import("node:path");
|
|
654
|
+
const outPath = resolvePath(process.cwd(), strFlag(rest, ["--output", "--out"], "plan.html"));
|
|
655
|
+
const rendersAs = finalPlan.domain?.renderHints ?? {};
|
|
656
|
+
const sizeOrder = (finalPlan.domain?.ordering ?? [])
|
|
657
|
+
.filter((row) => /-than$/.test(String(row.predicate || "")))
|
|
658
|
+
.map((row) => [row.subject, row.object]);
|
|
659
|
+
const html = renderPlanHtml({
|
|
660
|
+
plan: finalPlan, rendersAs, sizeOrder,
|
|
661
|
+
title: finalPlan.goal?.text || "tmct plan",
|
|
662
|
+
});
|
|
663
|
+
await writeFile(outPath, html, "utf8");
|
|
664
|
+
process.stdout.write(`wrote ${outPath} (${finalPlan.actions.length} moves, ${finalPlan.states.length} snapshots)\n`);
|
|
665
|
+
}
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
599
668
|
// The shell gate: a real terminal gets the full-screen Ink TUI; `--plain` or a
|
|
600
669
|
// non-TTY stream (pipes, scripts, the test suite) gets the readline shell. Both
|
|
601
670
|
// drive the same createSession sink — only the drawing differs.
|
|
@@ -867,8 +936,9 @@ async function main() {
|
|
|
867
936
|
process.exit(2);
|
|
868
937
|
}
|
|
869
938
|
|
|
870
|
-
|
|
871
|
-
|
|
939
|
+
const fileVal = strFlag(rest, ["--file"]);
|
|
940
|
+
if (!corpusVal && !ontologyVal && !lexiconVal && !graphFlags.length && !memoryBackendVal && !fileVal) {
|
|
941
|
+
process.stderr.write("tmct import: needs at least one of --corpus/--ontology/--lexicon/--graph/--memory-backend/--file\n");
|
|
872
942
|
process.exit(2);
|
|
873
943
|
}
|
|
874
944
|
|
|
@@ -914,6 +984,22 @@ async function main() {
|
|
|
914
984
|
process.exit(1);
|
|
915
985
|
}
|
|
916
986
|
}
|
|
987
|
+
|
|
988
|
+
// `--file <definition.txt>` — teach a plain-text definition file, one
|
|
989
|
+
// sentence per turn, through the same recognizers the live chat uses.
|
|
990
|
+
// Any declined sentence exits non-zero: a half-taught game plans wrongly
|
|
991
|
+
// or not at all with no visible cause otherwise.
|
|
992
|
+
if (fileVal) {
|
|
993
|
+
const { importDefinitionFile } = await import("../src/import-file.mjs");
|
|
994
|
+
try {
|
|
995
|
+
const result = await importDefinitionFile(repoRoot, fileVal);
|
|
996
|
+
process.stdout.write(result.report + "\n");
|
|
997
|
+
if (result.declined.length > 0) process.exit(1);
|
|
998
|
+
} catch (e) {
|
|
999
|
+
process.stderr.write(`tmct import: ${e?.message || e}\n`);
|
|
1000
|
+
process.exit(1);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
917
1003
|
return;
|
|
918
1004
|
}
|
|
919
1005
|
|
|
@@ -1012,7 +1098,7 @@ async function main() {
|
|
|
1012
1098
|
// depth/age falloff. Same repo resolution as `memory`/`syllogise` —
|
|
1013
1099
|
// resolveRuntimeConfig: --repo > git root > cwd.
|
|
1014
1100
|
const rest = process.argv.slice(3);
|
|
1015
|
-
const { strFlag, resolveRuntimeConfig } = await import("../src/cli-args.mjs");
|
|
1101
|
+
const { strFlag, boolFlag, resolveRuntimeConfig } = await import("../src/cli-args.mjs");
|
|
1016
1102
|
const { computeVizGraph, renderVizHtml, readAskBundle, readMemoryAskBundle } = await import("../src/viz.mjs");
|
|
1017
1103
|
const { writeFile } = await import("node:fs/promises");
|
|
1018
1104
|
const { resolve } = await import("node:path");
|
|
@@ -1021,15 +1107,35 @@ async function main() {
|
|
|
1021
1107
|
const v = j !== -1 ? Number(rest[j + 1]) : NaN;
|
|
1022
1108
|
return Number.isFinite(v) ? v : undefined;
|
|
1023
1109
|
};
|
|
1110
|
+
const ledger = boolFlag(rest, ["--ledger"]);
|
|
1024
1111
|
const focus = strFlag(rest, ["--focus"]);
|
|
1025
1112
|
const term = strFlag(rest, ["--term"]); // PLAN_VIZ_MEMORY.md: seed via normFactTerm-matched Fact(s), alongside --focus
|
|
1026
1113
|
const depth = numFlag("--depth"); // max arcs (hops) from the focus node
|
|
1027
|
-
const nodeLimit = numFlag("--limit"); // spiral length: total nodes walked
|
|
1114
|
+
const nodeLimit = numFlag("--limit"); // spiral length: total nodes walked (ledger mode: fact-row cap)
|
|
1028
1115
|
const hubDegree = numFlag("--hub-degree"); // stop expanding THROUGH a node above N connections
|
|
1029
1116
|
const edgeKindModeRaw = strFlag(rest, ["--edge-kind"]);
|
|
1030
1117
|
const edgeKindMode = ["meta", "relation", "both"].includes(edgeKindModeRaw) ? edgeKindModeRaw : undefined;
|
|
1031
|
-
const outPath = resolve(process.cwd(), strFlag(rest, ["--output", "--out"], "graph.html"));
|
|
1118
|
+
const outPath = resolve(process.cwd(), strFlag(rest, ["--output", "--out"], ledger ? "ledger.html" : "graph.html"));
|
|
1032
1119
|
const { repo } = await resolveRuntimeConfig({ argv: rest });
|
|
1120
|
+
if (ledger) {
|
|
1121
|
+
const { computeLedgerData, renderLedgerHtml } = await import("../src/ledger-viz.mjs");
|
|
1122
|
+
const data = await computeLedgerData(repo, {
|
|
1123
|
+
...(focus ? { focus } : {}),
|
|
1124
|
+
...(!focus && term ? { term } : {}),
|
|
1125
|
+
...(nodeLimit != null ? { rowLimit: nodeLimit } : {}),
|
|
1126
|
+
});
|
|
1127
|
+
const memoryAskBundle = await readMemoryAskBundle();
|
|
1128
|
+
await writeFile(outPath, renderLedgerHtml({ ...data, memoryAskBundle }), "utf8");
|
|
1129
|
+
process.stdout.write(
|
|
1130
|
+
`tmct viz --ledger — wrote ${data.meta.shown} fact row(s) around ${data.focus ? `'${data.focus}'` : "no focus"} to ${outPath}\n`,
|
|
1131
|
+
);
|
|
1132
|
+
if (data.meta.truncated) {
|
|
1133
|
+
process.stdout.write(
|
|
1134
|
+
`showing ${data.meta.shown} of ${data.meta.total} rows — narrow with --focus <term> or raise --limit\n`,
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1033
1139
|
const vizGraph = await computeVizGraph(repo, {
|
|
1034
1140
|
...(focus ? { focus } : {}),
|
|
1035
1141
|
...(!focus && term ? { term } : {}), // --focus takes precedence when both are given
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# crates — a second taught game, same closed frames as hanoi-3.
|
|
2
|
+
# Lines starting with # are skipped by import.
|
|
3
|
+
# No size order here: stacking is legal onto any clear crate or pallet, so this
|
|
4
|
+
# domain exercises the comparator precondition being optional. Goals can be a
|
|
5
|
+
# conjunction — teach several goal sentences before "solve it".
|
|
6
|
+
#
|
|
7
|
+
# After `tmct import --file crates.txt`, try this in `tmct chat` (or via --prompt):
|
|
8
|
+
#
|
|
9
|
+
# crate-c rests on crate-a. crate-a rests on pallet-1. crate-b rests on pallet-2.
|
|
10
|
+
# the goal is that crate-a rests on crate-b. the goal is that crate-b rests on pallet-2.
|
|
11
|
+
# solve it.
|
|
12
|
+
#
|
|
13
|
+
# (2 moves: clear crate-a, then stack it — the solver may route crate-c to
|
|
14
|
+
# pallet-3 or onto crate-b; both are optimal.)
|
|
15
|
+
a crate is a kind of container.
|
|
16
|
+
a pallet is a kind of place.
|
|
17
|
+
crate-a is a crate. crate-b is a crate. crate-c is a crate.
|
|
18
|
+
pallet-1 is a pallet. pallet-2 is a pallet. pallet-3 is a pallet.
|
|
19
|
+
you can stack a crate onto a pallet.
|
|
20
|
+
you can stack a crate onto a crate.
|
|
21
|
+
to stack a crate onto a target, nothing may rest on the crate.
|
|
22
|
+
to stack a crate onto a target, nothing may rest on the target.
|
|
23
|
+
stacking a crate onto a target makes the crate rest on the target.
|
|
24
|
+
a crate renders as a block. a pallet renders as a slot.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# hanoi-3 — a taught game definition. Lines starting with # are skipped by import.
|
|
2
|
+
# After `tmct import --file hanoi-3.txt`, try this in `tmct chat` (or via --prompt):
|
|
3
|
+
#
|
|
4
|
+
# disk-1 rests on disk-2. disk-2 rests on disk-3. disk-3 rests on peg-a.
|
|
5
|
+
# the goal is that every disk rests on peg-c. solve it.
|
|
6
|
+
#
|
|
7
|
+
# Variations, each stretching a different direction:
|
|
8
|
+
# scale — first teach: "disk-4 is a disk. disk-3 is smaller than disk-4."
|
|
9
|
+
# start all four on peg-a, same goal. 15 moves (2^4 - 1).
|
|
10
|
+
# any start — disk-1 rests on peg-b. disk-2 rests on peg-c. disk-3 rests on peg-a.
|
|
11
|
+
# the goal is that every disk rests on peg-c. solve it.
|
|
12
|
+
# other goal — same start, "the goal is that every disk rests on peg-b. solve it."
|
|
13
|
+
# partial — "the goal is that disk-3 rests on peg-c. solve it."
|
|
14
|
+
# (a one-fact goal; exercises the non-universal goal frame)
|
|
15
|
+
# legality — "what moves are legal now?" (findReachableSet, one ply, no plan)
|
|
16
|
+
# read-back — "what rests on disk-2?" / "is disk-1 clear?" (plain fact questions,
|
|
17
|
+
# no planning involved)
|
|
18
|
+
a disk is a kind of game piece.
|
|
19
|
+
a peg is a kind of place.
|
|
20
|
+
disk-1 is a disk. disk-2 is a disk. disk-3 is a disk.
|
|
21
|
+
peg-a is a peg. peg-b is a peg. peg-c is a peg.
|
|
22
|
+
disk-1 is smaller than disk-2. disk-1 is smaller than disk-3.
|
|
23
|
+
disk-2 is smaller than disk-3.
|
|
24
|
+
you can move a disk onto a peg.
|
|
25
|
+
you can move a disk onto a disk.
|
|
26
|
+
to move a disk onto a target, nothing may rest on the disk.
|
|
27
|
+
to move a disk onto a target, nothing may rest on the target.
|
|
28
|
+
to move a disk onto a disk, the disk must be smaller than the target.
|
|
29
|
+
moving a disk onto a target makes the disk rest on the target.
|
|
30
|
+
a disk renders as a block. a peg renders as a slot.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.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.",
|