@polycode-projects/the-mechanical-code-talker 1.10.14 → 1.11.5
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 +121 -114
- package/ROADMAP.md +14 -4
- package/bin/tmct.mjs +167 -77
- package/data/games/crates.txt +24 -0
- package/data/games/hanoi-3.txt +30 -0
- package/data/games/river.txt +33 -0
- package/package.json +1 -1
- package/src/ask.mjs +119 -6
- package/src/chat.mjs +1515 -52
- package/src/codegraph.mjs +24 -11
- package/src/domain.mjs +350 -0
- package/src/import-file.mjs +86 -0
- package/src/init.mjs +46 -1
- package/src/interpret/normalize.mjs +46 -0
- package/src/interpret/strategies/keywords.mjs +13 -9
- package/src/ledger-viz.mjs +635 -0
- package/src/memory/core.mjs +100 -17
- package/src/memory/shacl.mjs +20 -7
- package/src/memory-ask-browser-entry.mjs +9 -7
- package/src/memory-ask-browser.bundle.js +5648 -1177
- package/src/plan-viz.mjs +409 -0
- package/src/router/drive.mjs +122 -13
- package/src/router/guardrail.mjs +5 -0
- package/src/router/registry.mjs +55 -11
- package/src/router/resolver.mjs +13 -0
- package/src/router/taught.mjs +84 -0
- package/src/sentences.mjs +19 -0
- package/src/syllogise.mjs +154 -38
- package/src/viz-theme.mjs +66 -0
- package/src/wink-model.mjs +12 -6
- package/src/ask-browser-entry.mjs +0 -19
- package/src/ask-browser.bundle.js +0 -5411
- package/src/viz.mjs +0 -959
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
|
|
@@ -85,20 +93,14 @@ Usage:
|
|
|
85
93
|
tmct syllogise [--repo <abs>] speculative inference (offline maintenance job): forward-
|
|
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
|
-
tmct viz [--repo <abs>] write one self-contained
|
|
89
|
-
[--focus <
|
|
90
|
-
[--term <word>]
|
|
91
|
-
[--
|
|
92
|
-
[--
|
|
93
|
-
[--
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
[--config <path>] connections, still shows it (default 40); --edge-kind =
|
|
97
|
-
meta|relation|both (default both) — which edge kinds the
|
|
98
|
-
walk follows (provenance-only, concept-relations-only, or
|
|
99
|
-
both — see the page's own edge-kind toggle to change this
|
|
100
|
-
live); --term <word> resolves to the Fact(s) whose subject/
|
|
101
|
-
object normalizes to that word and seeds from there.
|
|
96
|
+
tmct viz [--repo <abs>] write one self-contained HTML page: the memory graph as a
|
|
97
|
+
[--focus <term>] readable ledger of fact-sentences around one focus term,
|
|
98
|
+
[--term <word>] with segments, a two-hop minimap, and an in-page chat dock
|
|
99
|
+
[--limit <n>] that answers from the embedded graph. Focuses on the newest
|
|
100
|
+
[--output <path>] taught fact's subject by default (--focus <term> or
|
|
101
|
+
[--config <path>] --term <word> override it); --output defaults to
|
|
102
|
+
ledger.html in the cwd; --limit caps the embedded fact
|
|
103
|
+
rows; --term resolves via the same normalization chat uses.
|
|
102
104
|
tmct serve [--repo <abs>] run the Anthropic Messages API-compatible endpoint
|
|
103
105
|
[--host <h>] [--port <n>] (POST /v1/messages) over the graph — a deterministic,
|
|
104
106
|
[--graph <path>] no-LLM "model" a tool-loop client can call; $0 usage.
|
|
@@ -596,6 +598,65 @@ async function main() {
|
|
|
596
598
|
if (graphPaths.length) extra.graphPaths = graphPaths;
|
|
597
599
|
if (configPath) extra.configPath = configPath;
|
|
598
600
|
if (memoryBackend) extra.memoryBackend = memoryBackend;
|
|
601
|
+
// `--prompt "<text>"` — one-shot mode: each sentence of the prompt runs as
|
|
602
|
+
// its own turn (state teaches first, the goal/solve trigger last), the
|
|
603
|
+
// FINAL turn's answer prints to stdout, and the process exits. Piped stdin
|
|
604
|
+
// stays the interactive fallback.
|
|
605
|
+
const prompt = strFlag(rest, ["--prompt"]);
|
|
606
|
+
// `--render <archetype> [--output <path>]` — after a one-shot --prompt whose
|
|
607
|
+
// final turn produced a plan, write the self-contained animated plan page
|
|
608
|
+
// (src/plan-viz.mjs). Requires --prompt: interactive chat has no single
|
|
609
|
+
// final turn to render.
|
|
610
|
+
let renderArchetype;
|
|
611
|
+
try {
|
|
612
|
+
renderArchetype = enumFlag(rest, ["--render"], ["blocks"]);
|
|
613
|
+
} catch (e) {
|
|
614
|
+
process.stderr.write(`tmct: ${e?.message || e}\n`);
|
|
615
|
+
process.exit(2);
|
|
616
|
+
}
|
|
617
|
+
if (renderArchetype && !prompt) {
|
|
618
|
+
process.stderr.write("tmct: --render needs --prompt (a one-shot turn whose plan it renders)\n");
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
if (prompt) {
|
|
622
|
+
const { createSession } = await import("../src/chat.mjs");
|
|
623
|
+
const { splitSentences } = await import("../src/sentences.mjs");
|
|
624
|
+
const session = await createSession({ repoPath, ephemeral, narrate, ...extra });
|
|
625
|
+
let finalAnswer = "";
|
|
626
|
+
let finalPlan = null;
|
|
627
|
+
let parts;
|
|
628
|
+
try { parts = splitSentences(prompt); } catch { parts = null; }
|
|
629
|
+
const sentences = parts && parts.length ? parts : [prompt];
|
|
630
|
+
for (const s of sentences) {
|
|
631
|
+
const t = await session.turn(s);
|
|
632
|
+
finalAnswer = t?.answer ?? "";
|
|
633
|
+
finalPlan = t?.plan ?? null;
|
|
634
|
+
}
|
|
635
|
+
await session.close();
|
|
636
|
+
process.stdout.write(finalAnswer + "\n");
|
|
637
|
+
if (renderArchetype) {
|
|
638
|
+
if (!finalPlan) {
|
|
639
|
+
process.stderr.write("the final turn produced no plan — nothing to render\n");
|
|
640
|
+
process.exitCode = 1;
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const { renderPlanHtml } = await import("../src/plan-viz.mjs");
|
|
644
|
+
const { writeFile } = await import("node:fs/promises");
|
|
645
|
+
const { resolve: resolvePath } = await import("node:path");
|
|
646
|
+
const outPath = resolvePath(process.cwd(), strFlag(rest, ["--output", "--out"], "plan.html"));
|
|
647
|
+
const rendersAs = finalPlan.domain?.renderHints ?? {};
|
|
648
|
+
const sizeOrder = (finalPlan.domain?.ordering ?? [])
|
|
649
|
+
.filter((row) => /-than$/.test(String(row.predicate || "")))
|
|
650
|
+
.map((row) => [row.subject, row.object]);
|
|
651
|
+
const html = renderPlanHtml({
|
|
652
|
+
plan: finalPlan, rendersAs, sizeOrder,
|
|
653
|
+
title: finalPlan.goal?.text || "tmct plan",
|
|
654
|
+
});
|
|
655
|
+
await writeFile(outPath, html, "utf8");
|
|
656
|
+
process.stdout.write(`wrote ${outPath} (${finalPlan.actions.length} moves, ${finalPlan.states.length} snapshots)\n`);
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
599
660
|
// The shell gate: a real terminal gets the full-screen Ink TUI; `--plain` or a
|
|
600
661
|
// non-TTY stream (pipes, scripts, the test suite) gets the readline shell. Both
|
|
601
662
|
// drive the same createSession sink — only the drawing differs.
|
|
@@ -867,8 +928,9 @@ async function main() {
|
|
|
867
928
|
process.exit(2);
|
|
868
929
|
}
|
|
869
930
|
|
|
870
|
-
|
|
871
|
-
|
|
931
|
+
const fileVal = strFlag(rest, ["--file"]);
|
|
932
|
+
if (!corpusVal && !ontologyVal && !lexiconVal && !graphFlags.length && !memoryBackendVal && !fileVal) {
|
|
933
|
+
process.stderr.write("tmct import: needs at least one of --corpus/--ontology/--lexicon/--graph/--memory-backend/--file\n");
|
|
872
934
|
process.exit(2);
|
|
873
935
|
}
|
|
874
936
|
|
|
@@ -914,6 +976,22 @@ async function main() {
|
|
|
914
976
|
process.exit(1);
|
|
915
977
|
}
|
|
916
978
|
}
|
|
979
|
+
|
|
980
|
+
// `--file <definition.txt>` — teach a plain-text definition file, one
|
|
981
|
+
// sentence per turn, through the same recognizers the live chat uses.
|
|
982
|
+
// Any declined sentence exits non-zero: a half-taught game plans wrongly
|
|
983
|
+
// or not at all with no visible cause otherwise.
|
|
984
|
+
if (fileVal) {
|
|
985
|
+
const { importDefinitionFile } = await import("../src/import-file.mjs");
|
|
986
|
+
try {
|
|
987
|
+
const result = await importDefinitionFile(repoRoot, fileVal);
|
|
988
|
+
process.stdout.write(result.report + "\n");
|
|
989
|
+
if (result.declined.length > 0) process.exit(1);
|
|
990
|
+
} catch (e) {
|
|
991
|
+
process.stderr.write(`tmct import: ${e?.message || e}\n`);
|
|
992
|
+
process.exit(1);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
917
995
|
return;
|
|
918
996
|
}
|
|
919
997
|
|
|
@@ -1006,53 +1084,50 @@ async function main() {
|
|
|
1006
1084
|
}
|
|
1007
1085
|
|
|
1008
1086
|
if (mode === "viz") {
|
|
1009
|
-
// `tmct viz` — one self-contained
|
|
1010
|
-
// memory graph
|
|
1011
|
-
//
|
|
1012
|
-
//
|
|
1013
|
-
//
|
|
1087
|
+
// `tmct viz` — the ledger explorer: one self-contained HTML page rendering
|
|
1088
|
+
// the memory graph as readable fact-sentences around a focus term, with
|
|
1089
|
+
// the in-browser chat dock (PLAN_VIZ_LEDGER.md). Same repo resolution as
|
|
1090
|
+
// `memory`/`syllogise` — resolveRuntimeConfig: --repo > git root > cwd.
|
|
1091
|
+
// `--ledger` is accepted as a no-op: the ledger IS the viz surface now.
|
|
1014
1092
|
const rest = process.argv.slice(3);
|
|
1093
|
+
const retiredFlags = ["--depth", "--hub-degree", "--edge-kind"].filter((f) => rest.includes(f));
|
|
1094
|
+
if (retiredFlags.length) {
|
|
1095
|
+
process.stderr.write(
|
|
1096
|
+
`tmct viz: ${retiredFlags.join(", ")} belonged to the retired node-link graph page and no longer exist${retiredFlags.length === 1 ? "s" : ""}.\n`
|
|
1097
|
+
+ "The ledger view takes: --repo <abs>, --focus <term>, --term <word>, --limit <n>, --output <path>.\n",
|
|
1098
|
+
);
|
|
1099
|
+
process.exitCode = 1;
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1015
1102
|
const { strFlag, resolveRuntimeConfig } = await import("../src/cli-args.mjs");
|
|
1016
|
-
const {
|
|
1103
|
+
const { computeLedgerData, renderLedgerHtml, readMemoryAskBundle } = await import("../src/ledger-viz.mjs");
|
|
1017
1104
|
const { writeFile } = await import("node:fs/promises");
|
|
1018
1105
|
const { resolve } = await import("node:path");
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
return Number.isFinite(v) ? v : undefined;
|
|
1023
|
-
};
|
|
1106
|
+
const limitIdx = rest.indexOf("--limit");
|
|
1107
|
+
const limitRaw = limitIdx !== -1 ? Number(rest[limitIdx + 1]) : NaN;
|
|
1108
|
+
const rowLimit = Number.isFinite(limitRaw) ? limitRaw : undefined;
|
|
1024
1109
|
const focus = strFlag(rest, ["--focus"]);
|
|
1025
|
-
const term = strFlag(rest, ["--term"]); //
|
|
1026
|
-
const
|
|
1027
|
-
const nodeLimit = numFlag("--limit"); // spiral length: total nodes walked
|
|
1028
|
-
const hubDegree = numFlag("--hub-degree"); // stop expanding THROUGH a node above N connections
|
|
1029
|
-
const edgeKindModeRaw = strFlag(rest, ["--edge-kind"]);
|
|
1030
|
-
const edgeKindMode = ["meta", "relation", "both"].includes(edgeKindModeRaw) ? edgeKindModeRaw : undefined;
|
|
1031
|
-
const outPath = resolve(process.cwd(), strFlag(rest, ["--output", "--out"], "graph.html"));
|
|
1110
|
+
const term = strFlag(rest, ["--term"]); // seeds via normFactTerm; --focus wins when both are given
|
|
1111
|
+
const outPath = resolve(process.cwd(), strFlag(rest, ["--output", "--out"], "ledger.html"));
|
|
1032
1112
|
const { repo } = await resolveRuntimeConfig({ argv: rest });
|
|
1033
|
-
const
|
|
1113
|
+
const data = await computeLedgerData(repo, {
|
|
1034
1114
|
...(focus ? { focus } : {}),
|
|
1035
|
-
...(!focus && term ? { term } : {}),
|
|
1036
|
-
...(
|
|
1037
|
-
...(nodeLimit != null ? { nodeLimit } : {}),
|
|
1038
|
-
...(hubDegree != null ? { hubDegree } : {}),
|
|
1039
|
-
...(edgeKindMode ? { edgeKindMode } : {}),
|
|
1115
|
+
...(!focus && term ? { term } : {}),
|
|
1116
|
+
...(rowLimit != null ? { rowLimit } : {}),
|
|
1040
1117
|
});
|
|
1041
|
-
//
|
|
1042
|
-
// the
|
|
1043
|
-
//
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
// the page (e.g. a fresh checkout before the bundles' first build).
|
|
1047
|
-
const [askBundle, memoryAskBundle] = await Promise.all([readAskBundle(), readMemoryAskBundle()]);
|
|
1048
|
-
const html = renderVizHtml({ ...vizGraph, askBundle, memoryAskBundle });
|
|
1049
|
-
await writeFile(outPath, html, "utf8");
|
|
1050
|
-
const chatNote = askBundle || memoryAskBundle
|
|
1051
|
-
? ` (with the embedded ask-the-graph chat panel${askBundle && memoryAskBundle ? "s" : ""})`
|
|
1052
|
-
: " (no chat panel — run `npm run build:ask-bundle` first)";
|
|
1118
|
+
// readMemoryAskBundle never throws; an empty string renders the page with
|
|
1119
|
+
// an honest "chat unavailable" note instead of the dock (e.g. a fresh
|
|
1120
|
+
// checkout before the bundle's first build).
|
|
1121
|
+
const memoryAskBundle = await readMemoryAskBundle();
|
|
1122
|
+
await writeFile(outPath, renderLedgerHtml({ ...data, memoryAskBundle }), "utf8");
|
|
1053
1123
|
process.stdout.write(
|
|
1054
|
-
`tmct viz — wrote ${
|
|
1124
|
+
`tmct viz — wrote ${data.meta.shown} fact row(s) around ${data.focus ? `'${data.focus}'` : "no focus"} to ${outPath}\n`,
|
|
1055
1125
|
);
|
|
1126
|
+
if (data.meta.truncated) {
|
|
1127
|
+
process.stdout.write(
|
|
1128
|
+
`showing ${data.meta.shown} of ${data.meta.total} rows — narrow with --focus <term> or raise --limit\n`,
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1056
1131
|
return;
|
|
1057
1132
|
}
|
|
1058
1133
|
|
|
@@ -1158,7 +1233,7 @@ async function main() {
|
|
|
1158
1233
|
process.stderr.write("tmct plan: needs a request, e.g. `tmct plan \"of the modules impacted by X, which are untested\"`\n");
|
|
1159
1234
|
process.exit(2);
|
|
1160
1235
|
}
|
|
1161
|
-
const { config } = await resolveRuntimeConfig({ argv: rest });
|
|
1236
|
+
const { repo, config } = await resolveRuntimeConfig({ argv: rest });
|
|
1162
1237
|
const { buildCapabilityPlanCtx, runCapabilityPlan, declaredCapabilityNames } = await import("../src/router/drive.mjs");
|
|
1163
1238
|
const declared = declaredCapabilityNames();
|
|
1164
1239
|
let tools = declared;
|
|
@@ -1172,34 +1247,49 @@ async function main() {
|
|
|
1172
1247
|
}
|
|
1173
1248
|
let ctx;
|
|
1174
1249
|
try {
|
|
1175
|
-
ctx = await buildCapabilityPlanCtx({ config });
|
|
1250
|
+
ctx = await buildCapabilityPlanCtx({ config, memoryDir: repo });
|
|
1176
1251
|
} catch (e) {
|
|
1177
1252
|
process.stderr.write(`tmct plan: could not load the graph — ${e?.message || e}\n`);
|
|
1178
1253
|
process.exit(1);
|
|
1179
1254
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
process.stdout.write(`(the direct router also declined: ${Array.isArray(result.c1Why) ? result.c1Why.join("; ") : result.c1Why})\n`);
|
|
1255
|
+
// The default (no --tools) toolset is re-read AFTER the ctx build: the
|
|
1256
|
+
// memory store's taught: capability records only register there, and a
|
|
1257
|
+
// world goal refuses when its taught record is outside the toolset.
|
|
1258
|
+
if (!toolsFlag) tools = declaredCapabilityNames();
|
|
1259
|
+
try {
|
|
1260
|
+
const result = await runCapabilityPlan(request, tools, ctx);
|
|
1261
|
+
if (jsonFlag) {
|
|
1262
|
+
process.stdout.write(JSON.stringify({ request, ...result }, null, 2) + "\n");
|
|
1263
|
+
process.exit(result.refused ? 1 : 0);
|
|
1190
1264
|
}
|
|
1191
|
-
process.
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
process.stdout.write(`
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1265
|
+
process.stdout.write(`tmct plan: "${request}"\n`);
|
|
1266
|
+
if (result.refused) {
|
|
1267
|
+
process.stdout.write(`no plan found — ${Array.isArray(result.why) ? result.why.join("; ") : result.why}\n`);
|
|
1268
|
+
if (result.c1Why) {
|
|
1269
|
+
process.stdout.write(`(the direct router also declined: ${Array.isArray(result.c1Why) ? result.c1Why.join("; ") : result.c1Why})\n`);
|
|
1270
|
+
}
|
|
1271
|
+
process.exit(1);
|
|
1272
|
+
}
|
|
1273
|
+
process.stdout.write(`driver: ${result.driver}\n\nsteps:\n`);
|
|
1274
|
+
for (let i = 0; i < result.calls.length; i += 1) {
|
|
1275
|
+
const c = result.calls[i];
|
|
1276
|
+
let text = "";
|
|
1277
|
+
// taught: records are simulated, never dispatchable — dispatching one
|
|
1278
|
+
// would print a misleading "unknown tool" under an honest plan step.
|
|
1279
|
+
if (c.name.startsWith("taught:")) text = "(simulated over the taught rules — execute it in chat with \"next\")";
|
|
1280
|
+
else {
|
|
1281
|
+
try { text = await ctx.dispatch(c.name, c.input || {}).then((r) => (r.ok ? r.text : `(unresolved: ${r.error})`)); }
|
|
1282
|
+
catch (e) { text = `(error: ${e?.message || e})`; }
|
|
1283
|
+
}
|
|
1284
|
+
process.stdout.write(` ${i + 1}. ${c.name} ${JSON.stringify(c.input || {})}\n ${String(text).split("\n").join("\n ")}\n`);
|
|
1285
|
+
}
|
|
1286
|
+
if (result.composed !== undefined && result.composed !== null) {
|
|
1287
|
+
process.stdout.write(`\ncomposed answer (${result.composed.length}): ${result.composed.length ? result.composed.join(", ") : "(empty set)"}\n`);
|
|
1288
|
+
} else if (result.observed) {
|
|
1289
|
+
process.stdout.write(`\n${result.observed}\n`);
|
|
1290
|
+
}
|
|
1291
|
+
} finally {
|
|
1292
|
+
for (const dispose of ctx.disposers || []) dispose();
|
|
1203
1293
|
}
|
|
1204
1294
|
return;
|
|
1205
1295
|
}
|
|
@@ -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.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# river — the wolf/goat/cabbage crossing, taught with the same closed frames
|
|
2
|
+
# as hanoi-3 plus two new families: a co-travel effect (the farmer rides every
|
|
3
|
+
# crossing) and the "may not be with ... without ..." constraint.
|
|
4
|
+
# Lines starting with # are skipped by import.
|
|
5
|
+
#
|
|
6
|
+
# After `tmct import --file river.txt`, try this in `tmct chat` (or via --prompt):
|
|
7
|
+
#
|
|
8
|
+
# wolf-1 stands on bank-east. goat-1 stands on bank-east.
|
|
9
|
+
# cabbage-1 stands on bank-east. farmer-1 stands on bank-east.
|
|
10
|
+
# the goal is that every passenger stands on bank-west. solve it.
|
|
11
|
+
#
|
|
12
|
+
# The classic optimum is 7 crossings: goat over, farmer back, wolf over, goat
|
|
13
|
+
# back, cabbage over, farmer back, goat over. From the opening position exactly
|
|
14
|
+
# 1 move is legal (ferry the goat) — every other crossing leaves the wolf with
|
|
15
|
+
# the goat, or the goat with the cabbage, without the farmer.
|
|
16
|
+
#
|
|
17
|
+
# Other things to try:
|
|
18
|
+
# legality — "what moves are legal now?" (one ply, no plan; shows the
|
|
19
|
+
# constraint pruning: 1 legal move at the start)
|
|
20
|
+
# read-back — "what stands on bank-east?" (plain fact question, no planning)
|
|
21
|
+
a passenger is a kind of game piece.
|
|
22
|
+
a bank is a kind of place.
|
|
23
|
+
wolf-1 is a wolf. wolf-1 is a passenger.
|
|
24
|
+
goat-1 is a goat. goat-1 is a passenger.
|
|
25
|
+
cabbage-1 is a cabbage. cabbage-1 is a passenger.
|
|
26
|
+
farmer-1 is a farmer.
|
|
27
|
+
bank-east is a bank. bank-west is a bank.
|
|
28
|
+
you can ferry a passenger onto a bank.
|
|
29
|
+
you can ferry a farmer onto a bank.
|
|
30
|
+
ferrying a passenger onto a bank makes the passenger stand on the target.
|
|
31
|
+
ferrying a passenger onto a bank makes the farmer stand on the target.
|
|
32
|
+
to ferry a passenger onto a bank, the wolf may not be with the goat without the farmer.
|
|
33
|
+
to ferry a passenger onto a bank, the goat may not be with the cabbage without the farmer.
|
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.5",
|
|
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.",
|