@polycode-projects/the-mechanical-code-talker 1.8.12 → 1.8.14
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 +2 -10
- 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 +149 -6
- 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
|
@@ -157,11 +157,7 @@ about tmct as of this writing.
|
|
|
157
157
|
|
|
158
158
|
Every message runs through **multiple concurrent interpretation strategies**:
|
|
159
159
|
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.
|
|
160
|
+
results are grouped by class.
|
|
165
161
|
|
|
166
162
|
One of the strategies is an **ACE-inspired controlled grammar**: when your
|
|
167
163
|
text fits the controlled fragment, tmct emits OWL-labelled triples from it.
|
|
@@ -244,11 +240,7 @@ it gives you more: "give me a detailed summary of how X works" (or "explain
|
|
|
244
240
|
in detail how X works", or "...detailed overview/explanation of X") gets a
|
|
245
241
|
longer, multi-sentence account instead of one line. Every sentence in it is
|
|
246
242
|
lifted from a real graph edge, attribute, or taught fact — never generated
|
|
247
|
-
free text
|
|
248
|
-
clears its own relevance bar.
|
|
249
|
-
|
|
250
|
-
From chat, a real run against the shipped `examples/mini-webapp` fixture
|
|
251
|
-
(banner lines trimmed):
|
|
243
|
+
free text.
|
|
252
244
|
|
|
253
245
|
```
|
|
254
246
|
$ node bin/tmct.mjs chat --repo examples/mini-webapp --ephemeral
|
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.14",
|
|
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
|