@polycode-projects/the-mechanical-code-talker 1.8.12 → 1.8.15
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 +35 -11
- package/ROADMAP.md +7 -136
- package/data/templates/grammar-rules.toml +1 -1
- package/package.json +1 -1
- package/src/ask-browser-entry.mjs +0 -8
- package/src/ask-browser.bundle.js +68 -4
- package/src/ask-nlp.mjs +0 -9
- package/src/ask.mjs +155 -24
- package/src/chat.mjs +200 -8
- package/src/codegraph.mjs +75 -0
- package/src/interpret/normalize.mjs +45 -7
- package/src/memory/core.mjs +63 -0
- package/src/paraphrase.mjs +131 -0
- package/src/router/planner.mjs +0 -4
- package/src/server.mjs +8 -1
- package/src/source-slice.mjs +1 -2
- package/src/syllogise.mjs +160 -1
package/README.md
CHANGED
|
@@ -79,6 +79,12 @@ the connecting sentence itself, citing both sources. The `source: ace:chat:…`
|
|
|
79
79
|
part is a real provenance receipt. Every fact tmct stores records where it
|
|
80
80
|
came from and when (more on that below).
|
|
81
81
|
|
|
82
|
+
The first `tell()` call above replies too: `noted — remembered 1 fact:
|
|
83
|
+
controller rdfs:subClassOf handler (controller is a type of handler)`. The
|
|
84
|
+
part in parentheses is a paraphrase. tmct generates it and checks it against
|
|
85
|
+
its own inference rules before showing it, so it never just guesses at
|
|
86
|
+
prose.
|
|
87
|
+
|
|
82
88
|
```
|
|
83
89
|
$ tmct
|
|
84
90
|
tmct> what talks to the payment module?
|
|
@@ -157,11 +163,7 @@ about tmct as of this writing.
|
|
|
157
163
|
|
|
158
164
|
Every message runs through **multiple concurrent interpretation strategies**:
|
|
159
165
|
a grammar parse, keyword picking, noise-word removal, fuzzy matching. Their
|
|
160
|
-
results are grouped by class
|
|
161
|
-
|
|
162
|
-
- results of the **same class merge** into one ranked answer;
|
|
163
|
-
- results of **distinct classes** are surrounded with an explicit
|
|
164
|
-
*"if you mean X then …"* so ambiguity is shown, never silently resolved.
|
|
166
|
+
results are grouped by class.
|
|
165
167
|
|
|
166
168
|
One of the strategies is an **ACE-inspired controlled grammar**: when your
|
|
167
169
|
text fits the controlled fragment, tmct emits OWL-labelled triples from it.
|
|
@@ -192,6 +194,23 @@ exactly. It checks the type itself and its subclasses first. Only if nothing
|
|
|
192
194
|
matches there does it widen to a related type, and when it does, it says so
|
|
193
195
|
plainly rather than presenting the looser match as exact.
|
|
194
196
|
|
|
197
|
+
**Comparing two things.** "compare TaskController and UserController" or "how
|
|
198
|
+
is TaskController different from UserController" lines up both entities'
|
|
199
|
+
shared and differing edges side by side: *"Comparing TaskController and
|
|
200
|
+
UserController (both Class): inherits [seon:hasSuperType]: TaskController (1)
|
|
201
|
+
-> Controller; UserController (1) -> Controller"*. Every row is a real edge
|
|
202
|
+
or attribute from the graph, never a hand-written diff.
|
|
203
|
+
|
|
204
|
+
**Up-refining to a containing module.** A class rarely has its own
|
|
205
|
+
symbol-precise commit or import record. "who touched TaskController" answers
|
|
206
|
+
from its containing module's real history instead of a confident-looking but
|
|
207
|
+
wrong "nothing touched it".
|
|
208
|
+
|
|
209
|
+
**Following a list.** tmct remembers the last list it gave you. After
|
|
210
|
+
"which modules import src/core/model.mjs", "which of those are tested" or
|
|
211
|
+
"how many of those" resolves "those"/"them" against that list, not a fresh,
|
|
212
|
+
unresolved pronoun.
|
|
213
|
+
|
|
195
214
|
**Synonyms and everyday phrasing.** tmct matches many of the words people
|
|
196
215
|
actually use for the same idea, from a curated synonym list plus a filtered
|
|
197
216
|
ConceptNet slice. A slightly different word for the same concept still
|
|
@@ -244,11 +263,12 @@ it gives you more: "give me a detailed summary of how X works" (or "explain
|
|
|
244
263
|
in detail how X works", or "...detailed overview/explanation of X") gets a
|
|
245
264
|
longer, multi-sentence account instead of one line. Every sentence in it is
|
|
246
265
|
lifted from a real graph edge, attribute, or taught fact — never generated
|
|
247
|
-
free text
|
|
248
|
-
clears its own relevance bar.
|
|
266
|
+
free text.
|
|
249
267
|
|
|
250
|
-
|
|
251
|
-
|
|
268
|
+
The wording varies a little too. A small, curated, deterministic pool swaps a
|
|
269
|
+
handful of connector words, like "defined in", "located in", or "found in".
|
|
270
|
+
The same fact doesn't read identically for every entity, but the same
|
|
271
|
+
question against the same entity always renders the same way.
|
|
252
272
|
|
|
253
273
|
```
|
|
254
274
|
$ node bin/tmct.mjs chat --repo examples/mini-webapp --ephemeral
|
|
@@ -322,8 +342,8 @@ from the environment. There's no CLI flag yet — this is a library-level
|
|
|
322
342
|
option for now, newer and less exercised than the default backend.
|
|
323
343
|
|
|
324
344
|
Teaching isn't limited to the ACE grammar's fixed shapes. Tell tmct an
|
|
325
|
-
arbitrary fact, like "margo eats ribs", and it mints a fact you can
|
|
326
|
-
about directly: "what does margo eat", or "does margo eat ribs".
|
|
345
|
+
arbitrary fact, like "margo really eats ribs", and it mints a fact you can
|
|
346
|
+
later ask about directly: "what does margo eat", or "does margo eat ribs".
|
|
327
347
|
|
|
328
348
|
New vocabulary compounds as you teach it. "redis is a cache" mints "redis" as
|
|
329
349
|
a class-level concept even though it was never in the built-in lexicon, and a
|
|
@@ -334,6 +354,10 @@ to ground one side first. Quantified teaching works too: "some functions are
|
|
|
334
354
|
risky" stores the quantifier, and a later "how many functions are risky"
|
|
335
355
|
answers "A few."
|
|
336
356
|
|
|
357
|
+
Once you've taught a few facts, "how many facts are there" counts them back.
|
|
358
|
+
That's the same count phrasing a code graph answers "how many classes are
|
|
359
|
+
there" with, just now reading tmct's own memory.
|
|
360
|
+
|
|
337
361
|
### Provenance and trust
|
|
338
362
|
|
|
339
363
|
Every fact and text block records **where it came from and when**. Sources are
|
package/ROADMAP.md
CHANGED
|
@@ -17,94 +17,18 @@ reading it finds in full, or is an honest miss when nothing grounds it at all.
|
|
|
17
17
|
Declared, forward-looking goals — not yet achieved, stated here so they steer future work instead of
|
|
18
18
|
getting silently traded away by inherited caution:
|
|
19
19
|
|
|
20
|
-
- **Reach for Llama-3-level natural language fluency.**
|
|
21
|
-
(tmct will never do that — see "What tmct will never do"). Instead, by growing rich
|
|
20
|
+
- **Reach for Llama-3-level natural language fluency.** by growing rich
|
|
22
21
|
template/surface-realization variety, so an answer shape has many valid phrasings instead of one
|
|
23
22
|
fixed slot-fill.
|
|
24
23
|
- **Resolve ambiguity breadth-first, always.** Every genuinely valid reading gets its own real answer
|
|
25
24
|
restated in full, never a bare "could mean X or Y — try rephrasing" punt, bounded only by existing
|
|
26
|
-
clipping/pagination limits.
|
|
27
|
-
(`renderCore`'s real-answer resolution, `CAPABILITIES_1.7.3.md` item 92) and entity-level ties (one
|
|
28
|
-
term matching several real graph individuals — every fuzzy-match tie, every noise-strip alt-object
|
|
29
|
-
collision, `PLAN_BREADTH_FIRST_NLU.md` §1). CEFR-confirmed: `BENCHMARK_CEFR_ENGLISH_1.8.0.md`'s
|
|
30
|
-
`ambiguity`-tagged cell moved 1.438 → 1.875 (+0.437, the largest single-tag move on record), and
|
|
31
|
-
the fix resolved a case pair the `1.7.0` report called permanently unfixable (`am-meta-imports` vs
|
|
32
|
-
`g-a1-naming-9` — same input, previously-incompatible expectations; both now score well against
|
|
33
|
-
one breadth-first answer). A dedicated audit found the two generic top-level bail-out hints
|
|
34
|
-
(`rephraseHint`/`compositionalHint`) are provably unavoidable at their actual miss sites, not
|
|
35
|
-
unwired — nothing left to generalize there.
|
|
25
|
+
clipping/pagination limits. L
|
|
36
26
|
- **Paraphrase alongside the original, verified, never instead of it.** A surface-realization variant
|
|
37
27
|
sits next to the literal grounded answer, never replacing it, and its accuracy is checked, not
|
|
38
28
|
assumed — by running tmct's own deterministic inference/consistency machinery (`src/syllogise.mjs`)
|
|
39
29
|
against both the original and the paraphrase: they must entail the same conclusions, and neither may
|
|
40
|
-
contradict the other sentence-by-sentence
|
|
41
|
-
|
|
42
|
-
accuracy, not the generation mechanism itself.
|
|
43
|
-
|
|
44
|
-
These sit alongside, not against, the zero-fabrication discipline: an answer with no grounding is
|
|
45
|
-
still an honest miss, and breadth-first resolution means showing every real answer a genuine reading
|
|
46
|
-
produces, never inventing one to fill a gap.
|
|
47
|
-
|
|
48
|
-
## Current capability surface
|
|
49
|
-
|
|
50
|
-
- **Grammar & parsing** (`src/grammar/`): an ACE-inspired controlled fragment (~8 sentence
|
|
51
|
-
patterns), plus multi-candidate ambiguity resolution — when a sentence has genuinely more than one
|
|
52
|
-
valid reading, every surviving interpretation is surfaced instead of one being guessed
|
|
53
|
-
(`archive/PLAN_DID_YOU_SEE_HER_DUCK.md`).
|
|
54
|
-
- **Compositional queries** (`src/ask.mjs`): recursive-descent over relative clauses, boolean
|
|
55
|
-
set-algebra (and/or/but-not), qualifiers, aggregates, superlatives, anaphora. Includes real
|
|
56
|
-
two-hop object-relative composition ("which modules import something that X depends on" —
|
|
57
|
-
`parseNested` → `reverseSet`/`forwardSet`, nesting to depth ≥2), confirmed still working via a
|
|
58
|
-
live-tested example (`TOO_HARD_AUDIT.md` U2) after a stale benchmark write-up called it
|
|
59
|
-
"known-hard territory" — it was always built and tested, just never re-checked.
|
|
60
|
-
- **Memory** (`src/memory/`): an OWL-labelled JSON graph on disk. Three persistence backends: flat
|
|
61
|
-
JSON (default), pure in-memory (zero disk I/O), SQLite (cached, incrementally-patched reads).
|
|
62
|
-
- **Reasoning** (`src/syllogise.mjs`): an OWL 2 RL-grounded rule ladder (subclass transitivity,
|
|
63
|
-
disjointness, someValuesFrom subsumption, cardinality, consistency checking), plus taught-relation
|
|
64
|
-
rules learned through ordinary chat (alias/union, fixed-hop composition, property-filtered
|
|
65
|
-
composition, recursive/reachability) — none of it hardcoded per domain.
|
|
66
|
-
- **Default persona**: a general-knowledge "human-world" vocabulary seeded by default (three size
|
|
67
|
-
tiers, `--persona-size small|medium|large`), sourced from Open English WordNet and Schema.org.
|
|
68
|
-
Code-domain vocabulary (SEON/ConceptNet) is opt-in (`--with-persona code`). Query coverage
|
|
69
|
-
includes forward/reverse CapableOf, reverse-HasA, and reverse-inherits/subClassOf shapes (`"can a
|
|
70
|
-
dog bark"`, `"what has a tail"`, `"what inherits from horse"`) against both corpus-seeded and
|
|
71
|
-
freshly-taught facts.
|
|
72
|
-
- **Genuine multi-reading ambiguity resolves and answers, not just describes**: when a sentence has
|
|
73
|
-
two-plus valid readings — whether the ambiguity is in how the sentence PARSES or in which real graph
|
|
74
|
-
ENTITY a term names — tmct traverses and renders each one's real answer inline (not just a one-line
|
|
75
|
-
label), so the same input always reproduces the same full, useful answer.
|
|
76
|
-
- **Every answer carries a canonical restatement of what was understood**: an English gloss in tmct's
|
|
77
|
-
own preferred phrasing plus the same fact in a compact, machine-parsable notation
|
|
78
|
-
(`shape(kind, args...)` for a query, `fact(subject, predicate, object)` for a taught fact) —
|
|
79
|
-
landed for the ask/query and teach/assert lanes; other chat lanes (conversational, commands) don't
|
|
80
|
-
have a real canonical form yet.
|
|
81
|
-
- **Graph traversal and provenance timestamps extend to the memory graph, now with a real viewer AND
|
|
82
|
-
a live embedded chat**: the hub-avoiding `spiralExpand` walk (previously code-graph/Module-only)
|
|
83
|
-
generalizes to any graph via a caller-supplied class predicate and id-normalizer; edges carry a
|
|
84
|
-
`createdAt` stamp and nodes get a derived `updatedAt`. `tmct viz [--focus <id>] [--output graph.html]`
|
|
85
|
-
renders it as one self-contained, locally-navigable HTML file (pan/zoom, click-to-inspect, a depth
|
|
86
|
-
stepper, per-class visibility filters, no server, no external deps) — `npm run viz -- --output
|
|
87
|
-
graph.html && open graph.html`. The page embeds a real "Ask the graph" chat panel running tmct's
|
|
88
|
-
OWN `ask.mjs` engine client-side (bundled via esbuild, adapter-less — no wink model, ~220KB): a
|
|
89
|
-
query resolves against the full graph and re-centres the view on the answer (focus-follows-answer),
|
|
90
|
-
and a node's class/label are click-to-query affordances.
|
|
91
|
-
- **Completions** (`src/completions/`): extractive, multi-sentence answers for broad "how does X
|
|
92
|
-
work" questions, grounded and source-cited — never invents a fact beyond what's retrieved, though
|
|
93
|
-
see "Ambition" above for growing the phrasing variety around what's retrieved.
|
|
94
|
-
- **Capability router** (`src/router/`): a deterministic, closed-toolset agentic router behind an
|
|
95
|
-
Anthropic-compatible API — measured by `AGENTBENCH`, not general function-calling. An ambiguous tool
|
|
96
|
-
argument stays an honest refusal (never a guess) but, since every registered capability is
|
|
97
|
-
read-only, now additionally carries each tied candidate's real dispatched result alongside it.
|
|
98
|
-
The C1 resolver defers a ranking/superlative request (a declared `SUPERLATIVE_EXTREMES` cue, e.g.
|
|
99
|
-
"what MOST needs a test") to the C2 goal-reasoner's keystone-argmax arbitration instead of
|
|
100
|
-
half-answering it with a flat unranked list — AGENTBENCH C2 is 11/11, 100% plan- and
|
|
101
|
-
result-complete (`TOO_HARD_AUDIT.md` M2, fixed).
|
|
102
|
-
- **Interfaces**: the `tmct` CLI, a documented library `exports` surface, and a Repository Interface
|
|
103
|
-
for downstream consumers (seonix).
|
|
104
|
-
|
|
105
|
-
Measured state for all of the above: the four `BENCHMARK_<TYPE>_<version>.md` reports
|
|
106
|
-
(`AGENT`/`CEFR_ENGLISH`/`CONVERSATION`/`INFERENCE`) and the periodic `CAPABILITIES_<version>.md`
|
|
107
|
-
audit — always check the latest-dated one, not this file, for real numbers.
|
|
30
|
+
contradict the other sentence-by-sentence..
|
|
31
|
+
|
|
108
32
|
|
|
109
33
|
## What's next (feature-shaped — see `HANDOVER.md` for the current task-level list)
|
|
110
34
|
|
|
@@ -118,51 +42,12 @@ audit — always check the latest-dated one, not this file, for real numbers.
|
|
|
118
42
|
unpopulated), which is met; full population everywhere was always a bigger, separately-scoped
|
|
119
43
|
follow-on. (b) growing the ACE grammar's free-form coverage past its measured 0/2,949-sentence
|
|
120
44
|
baseline — §6's own stated non-goal was a harness + baseline + first generated batch, not closing
|
|
121
|
-
the gap itself, which is met
|
|
122
|
-
own remaining scope (needs more grammar patterns or vocabulary, not tooling). What's genuinely
|
|
123
|
-
still open: (c) the paraphrase-verified-via-`syllogise.mjs` piece of "Ambition" — not started;
|
|
124
|
-
(d) a real "list/count all X of class Y" query shape for memory-graph classes — live testing
|
|
125
|
-
during the viz chat panel's build confirmed no such shape exists via `ask.mjs` alone (only
|
|
126
|
-
`chat.mjs`'s heavier `factAnswer` cascade has it, out of the browser bundle's scope), so the viz
|
|
127
|
-
panel's class-badge click currently falls back to a real client-side filter + a "where is X
|
|
128
|
-
mentioned" query rather than a true "list all" — a genuine, now-documented gap, not a silent one.
|
|
129
|
-
- **`archive/PLAN_TEMPLATE_COVERAGE.md`** — archived; the coverage-harness/generation design from
|
|
130
|
-
(b) above, including the real baseline number and the first 17-row generated batch. **Coverage
|
|
131
|
-
growth attempted 2026-07-12**: a residue audit found only 19 words across 2,664 docs sentences
|
|
132
|
-
have pure content-word (vocabulary-only) residue — the rest is genuinely grammar-structure
|
|
133
|
-
residue (coordination/negation/relative clauses), unfixable by vocabulary alone. Added 9 real,
|
|
134
|
-
generalizable words (`danger`/`push`/`refusal`/`tooling`/`legitimate`/`ranked`/`related`/
|
|
135
|
-
`sacred`/`unaffected`) to `src/grammar/lexicon-core.json`; real, honest delta: 0→1 hit (60.2%→
|
|
136
|
-
60.1% residue — a few sentences correctly reclassified residue→miss once their vocabulary gap
|
|
137
|
-
closed and a separate structural gap was exposed). Confirms the ceiling is structural, not a
|
|
138
|
-
vocabulary backlog — closing it further needs real grammar-pattern work (a coordination/relative-
|
|
139
|
-
clause-capable parser), not more lexicon entries. **Second follow-on, wiring output variety into
|
|
140
|
-
the live answer path — done 2026-07-12, scoped down honestly.** Split in two: (1) a dead-end
|
|
141
|
-
example-correctness sweep (`SKILL_AGENT_FAST_LOOP.md`'s "an offered example that itself fails"
|
|
142
|
-
rubric) found and fixed 3 genuinely broken examples across `ask.mjs`/`chat.mjs` (a `touches`-verb
|
|
143
|
-
hint that could never resolve for any input, a count hint that never parses, an unseeded-session
|
|
144
|
-
vocab-hint that lied). (2) surface-variety wiring landed 7 phrasing pools / 14 variants across 8
|
|
145
|
-
template sites (`src/answer-variants.mjs`, deterministic per-key hash selection — never random),
|
|
146
|
-
scoped down from an original ~50-template/~100-variant target after inventory showed most
|
|
147
|
-
candidate templates either name a relation verb (off-limits — never vary `imports`/`calls`/
|
|
148
|
-
`tests`/etc.) or are miss/rephrase-hint templates (handled by (1) instead, also off-limits).
|
|
149
|
-
Deliberately left `ask.mjs`'s two "X is defined in Y" `whereShape` sites unvaried:
|
|
150
|
-
`chatbench/graded-pool-max.jsonl` pins that exact substring as ground truth for 11 cases (2 in
|
|
151
|
-
the always-run promoted subset), and that pool is append-only per its own governing skill doc.
|
|
152
|
-
- **A fresh `CAPABILITIES_1.8.0.md` audit** — `CAPABILITIES_1.7.3.md` is pinned at commit `981c9b2`
|
|
153
|
-
and doesn't cover any of `PLAN_BREADTH_FIRST_NLU.md`'s six tracks; this doc's "Current capability
|
|
154
|
-
surface" above covers them narratively, but no full overlay audit has run since. Not done this
|
|
155
|
-
pass — `BENCHMARK_CEFR_ENGLISH_1.8.0.md` alone was in scope.
|
|
45
|
+
the gap itself, which is met.
|
|
156
46
|
- **`PLAN_ADVENTURE.md`** — a text-adventure architectural stretch: an imperative command grammar,
|
|
157
47
|
mutable turn-by-turn world/player state as ordinary graph nodes (no special player-state store),
|
|
158
48
|
and an NPC turn scheduler. Design-only.
|
|
159
49
|
- **`PLAN_SYLLOGIST.md`** — retraction-aware consistency checking under a hard budget and trust
|
|
160
50
|
tiers, the one open piece of the reasoning engine's research horizon. Design-only.
|
|
161
|
-
- **`archive/PLAN_CONVERSATION.md` Finding 4** — an anaphoric "SUBJECT verb which N" inheritance
|
|
162
|
-
question misroutes into teach-a-fact; needs a discontiguous verb-frame parser, a POS-aware
|
|
163
|
-
mid-sentence interrogative detector, and a union-kind reverse-question fix. Large, three
|
|
164
|
-
sub-problems, not attempted in a single pass — a concrete first-increment sketch exists
|
|
165
|
-
(`HANDOVER.md`), not an undesignable question.
|
|
166
51
|
- **`PLAN_GUESS_NUMBER.md` / `PLAN_HANOI.md`** — closed-loop and open-loop planning domains for the
|
|
167
52
|
`findActionPath`/`findReachableSet` kernels, both already built and proven but not yet wired to
|
|
168
53
|
either domain. Design-only.
|
|
@@ -171,13 +56,7 @@ audit — always check the latest-dated one, not this file, for real numbers.
|
|
|
171
56
|
- **`PLAN_AGENTS.md`** — the governing plan for tmct's broader multi-repo arc (marginalia, seonix,
|
|
172
57
|
a pluggable LLM rung for Claude Code/Bedrock/Copilot). Check its own sequencing table for current
|
|
173
58
|
phase status, not this file.
|
|
174
|
-
|
|
175
|
-
hub-avoiding spiral walk with pseudo-3D depth rendering) shipped and are done. Three real items
|
|
176
|
-
remain, un-staffed: (1) the code-graph timestamp-provider architectural decision (provider-
|
|
177
|
-
populated vs. a new tmct-owned local-git mode); (2) the maintainer-side git-log-corpus generation
|
|
178
|
-
step, wiring seed-time `README.md` ingestion, and a new `Source` "seed" kind; (3) an eager
|
|
179
|
-
session/sessionless anchor individual with a `mode` field (deciding the two-Session-classes
|
|
180
|
-
question at the same time). See the archived doc's own "Next step" for the full detail.
|
|
59
|
+
|
|
181
60
|
|
|
182
61
|
## Research horizon
|
|
183
62
|
|
|
@@ -212,7 +91,7 @@ citations, not stop signs:
|
|
|
212
91
|
(2026-07-11): `"tail"` (Unix process vs. animal body part) collides under `normFactTerm`'s
|
|
213
92
|
cross-corpus flattening, `src/memory/core.mjs:1109-1134`.
|
|
214
93
|
|
|
215
|
-
**Tier-4: learn-on-miss acquisition
|
|
94
|
+
**Tier-4: learn-on-miss acquisition**. The strongest
|
|
216
95
|
miss signal tmct can emit: lexicon term recognized, query built cleanly, zero matches anywhere — the
|
|
217
96
|
question was well-formed and the knowledge is simply absent. Web search on the resolved term → clean
|
|
218
97
|
the fetched text into the ACE-OWL controlled grammar → store with source provenance → answer the
|
|
@@ -220,14 +99,6 @@ original question, citing what was just learned. Strictly opt-in, offline defaul
|
|
|
220
99
|
Prerequisites: the provenance-trust policy must extend to `via:"learned:web"`, never silently
|
|
221
100
|
blending web-sourced facts with graph/operator facts.
|
|
222
101
|
|
|
223
|
-
## What tmct will never do
|
|
224
|
-
|
|
225
|
-
- Run on AWS or maintain a benchmark rig — tmct is a published npm library + CLI with a static
|
|
226
|
-
GitLab Pages home page, nothing more.
|
|
227
|
-
- Publish automatically — a version release always requires a deliberate version-bump commit.
|
|
228
|
-
- Run an MCP server or put an LLM in the product path — a core identity decision, decided once,
|
|
229
|
-
not revisited.
|
|
230
|
-
|
|
231
102
|
## Design docs
|
|
232
103
|
|
|
233
104
|
Every substantial design lives in its own `PLAN_*.md` at the repo root (active) or `archive/`
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
# 1. Article selection — a/an by the following word's phonetic onset. The live
|
|
37
37
|
# defect this fixes: the assert echo "every module is a artifact" -> "an
|
|
38
38
|
# artifact". Reads the next word (whether in-span or the leading token of the
|
|
39
|
-
# following protected span)
|
|
39
|
+
# following protected span).
|
|
40
40
|
[[rule]]
|
|
41
41
|
id = "article-selection"
|
|
42
42
|
kind = "article"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.15",
|
|
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.",
|
|
@@ -14,14 +14,6 @@
|
|
|
14
14
|
// reuses the real spiralExpand instead, so the browser walk is byte-identical
|
|
15
15
|
// to the CLI's).
|
|
16
16
|
//
|
|
17
|
-
// Bundled by scripts/build-ask-bundle.mjs into src/ask-browser.bundle.js (an
|
|
18
|
-
// IIFE), which viz.mjs inlines verbatim into the viewer page's own <script>.
|
|
19
|
-
// Runs adapter-less (no wink model in the browser — ask()'s lemma/POS tier
|
|
20
|
-
// degrades to its curated + fuzzy tiers, exactly the boundary
|
|
21
|
-
// test/ask-nlp.test.mjs's own "viewer bundle without wink" test proves stays
|
|
22
|
-
// answerable) and grammar-lite (no ACE/construction-grammar strategies, both
|
|
23
|
-
// fs-dependent — the plain grammar/keyword-spot/noise-strip strategies still
|
|
24
|
-
// answer every shape tmct viz's own memory-graph queries need).
|
|
25
17
|
import { ask, parseQuery } from "./ask.mjs";
|
|
26
18
|
import {
|
|
27
19
|
parseEntities, spiralExpand, mostRecentIndividual, derivedUpdatedAt, MEMORY_SPIRAL_EXPAND_KINDS,
|
|
@@ -1322,6 +1322,11 @@
|
|
|
1322
1322
|
"a",
|
|
1323
1323
|
"an",
|
|
1324
1324
|
"some",
|
|
1325
|
+
// "what OTHER classes inherit from Controller" — "other" is a vague determiner
|
|
1326
|
+
// like "some", not a qualifying adjective; without this it was misread as a
|
|
1327
|
+
// fuzzy find TERM ("no classes found matching 'other'") instead of falling
|
|
1328
|
+
// through to the ordinary reverse-inherits parse (fast-loop round 6 finding).
|
|
1329
|
+
"other",
|
|
1325
1330
|
// topic lead-in filler — "what about the modules", "how about classes": "about"
|
|
1326
1331
|
// carries no graph meaning here, so stripping it lets the bare kind noun surface for
|
|
1327
1332
|
// the cascade's bare-kind-noun terminal rule (ask.mjs). ("what"/"how" are structural
|
|
@@ -1427,6 +1432,9 @@
|
|
|
1427
1432
|
);
|
|
1428
1433
|
var MISSPELLING_RE = correctionRe(MISSPELLINGS);
|
|
1429
1434
|
var WRONG_WORD_RE = correctionRe(WRONG_WORDS);
|
|
1435
|
+
var W_SLASH_RE = /(?<=^|\s)w\/(?=\s|$)/gi;
|
|
1436
|
+
var FOR_DIGIT_THANKS_RE = /\b(thx|thanks|thank\s+you|many\s+thanks|ty|cheers)\s+4\b/gi;
|
|
1437
|
+
var FOR_DIGIT_EXAMPLE_RE = /\b4\s+(example|instance)\b(?!\s*[a-z])/gi;
|
|
1430
1438
|
var KIND_NOUN_ANAPHORA_RE = /\b(this|that)\s+(class|module|function|method|attribute|variable|file|commit)\b/gi;
|
|
1431
1439
|
var VERB_ALTERNATION = Object.keys(VERB_TO_KIND).sort((a, b) => b.length - a.length).map(escapeRegex).join("|");
|
|
1432
1440
|
var RELATION_VERB_RE = new RegExp(
|
|
@@ -1574,6 +1582,9 @@
|
|
|
1574
1582
|
q = q.replace(CONTRACTION_RE, (m) => CONTRACTIONS[m.toLowerCase()]);
|
|
1575
1583
|
q = q.replace(MISSPELLING_RE, (m) => MISSPELLINGS[m.toLowerCase()]);
|
|
1576
1584
|
q = q.replace(WRONG_WORD_RE, (m) => WRONG_WORDS[m.toLowerCase()]);
|
|
1585
|
+
q = q.replace(W_SLASH_RE, "with");
|
|
1586
|
+
q = q.replace(FOR_DIGIT_THANKS_RE, (_, w) => `${w} for`);
|
|
1587
|
+
q = q.replace(FOR_DIGIT_EXAMPLE_RE, (_, w) => `for ${w}`);
|
|
1577
1588
|
q = q.replace(KIND_NOUN_ANAPHORA_RE, (_, pron) => pron);
|
|
1578
1589
|
q = q.replace(G_DROP, "$1ing");
|
|
1579
1590
|
q = applyPreambleFrames(q);
|
|
@@ -2447,7 +2458,16 @@
|
|
|
2447
2458
|
Commit: ["commit", "commits"],
|
|
2448
2459
|
// "Change" is ask-vocab.mjs's pseudo-type (a wildcard over the touch traversal's
|
|
2449
2460
|
// results, never a node class) — it still needs noun forms for zero-hit templates.
|
|
2450
|
-
Change: ["change", "changes"]
|
|
2461
|
+
Change: ["change", "changes"],
|
|
2462
|
+
// Memory-graph classes (memory/core.mjs) — real noun forms for the dynamic
|
|
2463
|
+
// class count/list fallback (PLAN_BREADTH_FIRST_NLU.md (d), see
|
|
2464
|
+
// dynamicClassQuery below) so "2 facts." reads naturally instead of falling
|
|
2465
|
+
// back to the generic "2 results.".
|
|
2466
|
+
Fact: ["fact", "facts"],
|
|
2467
|
+
Utterance: ["utterance", "utterances"],
|
|
2468
|
+
Session: ["session", "sessions"],
|
|
2469
|
+
Source: ["source", "sources"],
|
|
2470
|
+
Rule: ["rule", "rules"]
|
|
2451
2471
|
};
|
|
2452
2472
|
function nounFor(entityType, n) {
|
|
2453
2473
|
const [s, p] = PLURAL_FORMS[entityType] || ["result", "results"];
|
|
@@ -3765,7 +3785,7 @@
|
|
|
3765
3785
|
if (!result.matches.length) {
|
|
3766
3786
|
return { content: `no ${nounFor(result.entityType, 2)} in this index.`, miss: true, ambiguous: false, matches: [] };
|
|
3767
3787
|
}
|
|
3768
|
-
const scopeable = !["Module", "Commit"].includes(result.entityType);
|
|
3788
|
+
const scopeable = !["Module", "Commit", "Fact", "Utterance", "Session", "Source", "Rule"].includes(result.entityType);
|
|
3769
3789
|
const hint = !result.scoped && scopeable && result.matches.length > OVERFLOW_CAP ? ` \u2014 narrow with "${nounFor(result.entityType, 2)} in <module>"` : "";
|
|
3770
3790
|
return { content: `${compositeList(result.matches)}${hint}.`, miss: false, ambiguous: false, matches: result.matches };
|
|
3771
3791
|
}
|
|
@@ -4996,6 +5016,37 @@ ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.co
|
|
|
4996
5016
|
const bareTrimmed = out.trim().replace(/[?.!]+$/, "");
|
|
4997
5017
|
return BARE_WHEN_COMMIT_RE.test(bareTrimmed) ? `${bareTrimmed} touched` : out;
|
|
4998
5018
|
}
|
|
5019
|
+
function singularCandidates(word) {
|
|
5020
|
+
const w = String(word || "").toLowerCase();
|
|
5021
|
+
const c = /* @__PURE__ */ new Set([w]);
|
|
5022
|
+
if (w.endsWith("ies")) c.add(`${w.slice(0, -3)}y`);
|
|
5023
|
+
if (w.endsWith("ses")) c.add(w.slice(0, -2));
|
|
5024
|
+
else if (w.endsWith("es")) c.add(w.slice(0, -2));
|
|
5025
|
+
if (w.endsWith("s") && w.length > 1) c.add(w.slice(0, -1));
|
|
5026
|
+
return [...c];
|
|
5027
|
+
}
|
|
5028
|
+
function resolveDynamicClass(graph, word) {
|
|
5029
|
+
const cands = singularCandidates(word);
|
|
5030
|
+
for (const ind of graph?.individuals || []) {
|
|
5031
|
+
if (ind?.class && cands.includes(String(ind.class).toLowerCase())) return ind.class;
|
|
5032
|
+
}
|
|
5033
|
+
return null;
|
|
5034
|
+
}
|
|
5035
|
+
var DYNAMIC_LIST_TRIGGER_RE = /^(?:list|show(?:\s+me)?)\s+(?:all\s+|the\s+)?([a-z][a-z'-]*)\s*(.*)$/i;
|
|
5036
|
+
var DYNAMIC_COUNT_TRIGGER_RE = /^(?:how\s+many|number\s+of|count(?:\s+the)?)\s+([a-z][a-z'-]*)\s*(.*)$/i;
|
|
5037
|
+
var DYNAMIC_TAIL_OK_RE = /^(?:are there(?:\s+in\s+total)?|is there|do you know(?:\s+about)?|do you have|exist(?:s)?|are known|in (?:the |a )?(?:graph|memory)|you know(?:\s+about)?)?[?.!\s]*$/i;
|
|
5038
|
+
function dynamicClassQuery(graph, query) {
|
|
5039
|
+
const q = String(query || "").trim();
|
|
5040
|
+
const listM = q.match(DYNAMIC_LIST_TRIGGER_RE);
|
|
5041
|
+
const countM = !listM ? q.match(DYNAMIC_COUNT_TRIGGER_RE) : null;
|
|
5042
|
+
const m = listM || countM;
|
|
5043
|
+
if (!m || !DYNAMIC_TAIL_OK_RE.test(m[2] || "")) return null;
|
|
5044
|
+
if (ENTITY_TO_TYPE[m[1].toLowerCase()]) return null;
|
|
5045
|
+
const entityType = resolveDynamicClass(graph, m[1]);
|
|
5046
|
+
if (!entityType) return null;
|
|
5047
|
+
const base = { node: "allOfClass", entityType };
|
|
5048
|
+
return listM ? { node: "list", entityType, base, scoped: false } : { node: "count", entityType, base };
|
|
5049
|
+
}
|
|
4999
5050
|
function ask(graph, query, { contextId = null, nlp = void 0, prev = null } = {}) {
|
|
5000
5051
|
if (isHelpRequest(query)) {
|
|
5001
5052
|
return {
|
|
@@ -5026,8 +5077,21 @@ ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.co
|
|
|
5026
5077
|
relaxed = { from: r.from, to: r.to, dropped: r.dropped, steps: r.steps };
|
|
5027
5078
|
}
|
|
5028
5079
|
}
|
|
5029
|
-
|
|
5030
|
-
|
|
5080
|
+
let result = traverse(graph, parsed, { contextId, prev });
|
|
5081
|
+
let rendered = render(parsed, result);
|
|
5082
|
+
if (rendered.miss && !rendered.ambiguous) {
|
|
5083
|
+
const dyn = dynamicClassQuery(graph, query);
|
|
5084
|
+
if (dyn) {
|
|
5085
|
+
const dynResult = traverse(graph, dyn, { contextId, prev });
|
|
5086
|
+
const dynRendered = render(dyn, dynResult);
|
|
5087
|
+
if (!dynRendered.miss) {
|
|
5088
|
+
parsed = dyn;
|
|
5089
|
+
result = dynResult;
|
|
5090
|
+
rendered = dynRendered;
|
|
5091
|
+
relaxed = null;
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
}
|
|
5031
5095
|
let content = relaxed && !rendered.miss && relaxed.to !== relaxed.from ? `read as "${relaxed.to}" \u2014 ${rendered.content}` : rendered.content;
|
|
5032
5096
|
if (!relaxed && !rendered.miss && !rendered.ambiguous && directFull.alternates.length) {
|
|
5033
5097
|
const answered = directFull.alternates.map((a) => {
|
package/src/ask-nlp.mjs
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
// ask-nlp.mjs — the OPTIONAL wink-nlp adapter behind ask.mjs's lemma/POS tier.
|
|
2
2
|
//
|
|
3
|
-
// BOUNDARY (hard, do not move): this file is Node-only and is NEVER inlined into
|
|
4
|
-
// the viewer bundle. viz.mjs's askSource() inlines codegraph.mjs + ask-vocab.mjs +
|
|
5
|
-
// ask.mjs ONLY and strips their import lines, so the portable single-file HTML has
|
|
6
|
-
// no wink, no model, and no `nlpAdapter` binding at all — ask.mjs reaches this
|
|
7
|
-
// module exclusively through a `typeof nlpAdapter === "function"` guard and
|
|
8
|
-
// degrades to adapter-less parsing (lemma/POS tiers off; the curated tables and
|
|
9
|
-
// the bounded edit-distance tier still work, browser and Node alike). Keeping the
|
|
10
|
-
// ~1MB CJS model out of the page is the point of the split.
|
|
11
|
-
//
|
|
12
3
|
// wink-nlp and wink-eng-lite-web-model are loaded through the shared leaf loader
|
|
13
4
|
// src/wink-model.mjs (Node `createRequire` fallback + a browser registration seam),
|
|
14
5
|
// so this file no longer carries its own Node-only load block. The load happens
|