@polycode-projects/the-mechanical-code-talker 2.7.9 → 2.7.11
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.11",
|
|
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.",
|
|
@@ -502,7 +502,15 @@ const VERB_SYNONYMS = new Map([
|
|
|
502
502
|
// consumed whole before any shorter prefix inside it gets a chance to match.
|
|
503
503
|
const VERB_SYNONYM_MAX_PREFIX = Math.max(...[...VERB_SYNONYMS.keys()].map((k) => k.split(" ").length));
|
|
504
504
|
|
|
505
|
-
|
|
505
|
+
// Every leading SURFACE word the exact tiers below recognise as the start of
|
|
506
|
+
// a command: a bare IMPERATIVE_VERBS word, or the first token of any
|
|
507
|
+
// VERB_SYNONYMS key (single- or multi-word alike — "pick", "have", "chat" are
|
|
508
|
+
// each only ever a key's own first word, never a key on their own, so they'd
|
|
509
|
+
// otherwise have nothing to fuzzy-repair a typo onto). Corrects the SURFACE
|
|
510
|
+
// word, not the canonical verb, so the existing exact-match tiers below (in
|
|
511
|
+
// particular the multi-word prefix loop) still resolve the rest of the phrase
|
|
512
|
+
// after repair — "hav a look at" -> "have a look at" -> examine, not just "look".
|
|
513
|
+
const VERB_SURFACE_WORDS = [...new Set([...IMPERATIVE_VERBS, ...[...VERB_SYNONYMS.keys()].map((k) => k.split(" ")[0])])];
|
|
506
514
|
const IMPERATIVE_FUZZY_BOUND = 1; // this project's existing distance-1 tolerance elsewhere (interpret/fuzzy.mjs)
|
|
507
515
|
|
|
508
516
|
// A small, hand-checked exemption (same idiom as this file's own
|
|
@@ -513,31 +521,48 @@ const IMPERATIVE_FUZZY_BOUND = 1; // this project's existing distance-1 toleranc
|
|
|
513
521
|
// Checked against this repo's own WordNet corpus, not guessed.
|
|
514
522
|
const NEVER_FUZZY_VERB = new Set(["walk", "wake", "make"]);
|
|
515
523
|
|
|
516
|
-
/**
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
|
|
523
|
-
* for a two-word synonym phrase); `corrected` is non-null only for the
|
|
524
|
-
* fuzzy tier. Null when nothing at all recognises the leading token(s). */
|
|
525
|
-
function resolveImperativeVerb(toks) {
|
|
524
|
+
/** The three deterministic (non-fuzzy) tiers: a 2+-token VERB_SYNONYMS prefix
|
|
525
|
+
* FIRST (so "talk to"/"look at" resolve as themselves rather than as the
|
|
526
|
+
* bare exact verbs "talk"/"look" they'd otherwise short-circuit on, leaving
|
|
527
|
+
* their "to"/"at" stranded in the object phrase), then an exact
|
|
528
|
+
* IMPERATIVE_VERBS match, then the 1-token VERB_SYNONYMS form. `consumed` is
|
|
529
|
+
* how many leading tokens the match ate. Null when none of the three match. */
|
|
530
|
+
function exactImperativeVerb(toks) {
|
|
526
531
|
const first = toks[0].toLowerCase();
|
|
527
532
|
const lower = toks.map((t) => t.toLowerCase());
|
|
528
533
|
for (let n = Math.min(VERB_SYNONYM_MAX_PREFIX, toks.length); n >= 2; n -= 1) {
|
|
529
534
|
const hit = VERB_SYNONYMS.get(lower.slice(0, n).join(" "));
|
|
530
|
-
if (hit) return { verb: hit, consumed: n
|
|
535
|
+
if (hit) return { verb: hit, consumed: n };
|
|
531
536
|
}
|
|
532
|
-
if (IMPERATIVE_VERBS.has(first)) return { verb: first, consumed: 1
|
|
537
|
+
if (IMPERATIVE_VERBS.has(first)) return { verb: first, consumed: 1 };
|
|
533
538
|
const oneHit = VERB_SYNONYMS.get(first);
|
|
534
|
-
if (oneHit) return { verb: oneHit, consumed: 1
|
|
535
|
-
if (NEVER_FUZZY_VERB.has(first)) return null;
|
|
536
|
-
const fuzzyHit = fuzzyMatchInSet(first, VERB_FUZZY_CANDIDATES, IMPERATIVE_FUZZY_BOUND);
|
|
537
|
-
if (fuzzyHit) return { verb: fuzzyHit, consumed: 1, corrected: { from: first, to: fuzzyHit } };
|
|
539
|
+
if (oneHit) return { verb: oneHit, consumed: 1 };
|
|
538
540
|
return null;
|
|
539
541
|
}
|
|
540
542
|
|
|
543
|
+
/** Resolve the leading verb token(s) to a canonical, recognised verb: the
|
|
544
|
+
* three exact tiers above, failing that a bounded fuzzy TYPO repair
|
|
545
|
+
* (distance <= 1, via the existing interpret/fuzzy.mjs matcher) of the
|
|
546
|
+
* leading token against every recognised surface word, single- or
|
|
547
|
+
* multi-word idiom alike, then the exact tiers retried against the
|
|
548
|
+
* corrected token — so a typo'd idiom lead word ("hav a look at") is
|
|
549
|
+
* repaired to its real word first ("have a look at") and still resolves the
|
|
550
|
+
* whole idiom, not just a bare-verb guess. Only the fuzzy tier is a
|
|
551
|
+
* correction of an ERROR rather than a recognised alternate wording, so only
|
|
552
|
+
* it is reported back on the command (`corrected`) for the caller to name in
|
|
553
|
+
* its response — a synonym executes silently, a typo fix does not. */
|
|
554
|
+
function resolveImperativeVerb(toks) {
|
|
555
|
+
const exact = exactImperativeVerb(toks);
|
|
556
|
+
if (exact) return { ...exact, corrected: null };
|
|
557
|
+
const first = toks[0].toLowerCase();
|
|
558
|
+
if (NEVER_FUZZY_VERB.has(first)) return null;
|
|
559
|
+
const fixedFirst = fuzzyMatchInSet(first, VERB_SURFACE_WORDS, IMPERATIVE_FUZZY_BOUND);
|
|
560
|
+
if (!fixedFirst) return null;
|
|
561
|
+
const retried = exactImperativeVerb([fixedFirst, ...toks.slice(1)]);
|
|
562
|
+
if (!retried) return null;
|
|
563
|
+
return { ...retried, corrected: { from: first, to: fixedFirst } };
|
|
564
|
+
}
|
|
565
|
+
|
|
541
566
|
/** Resolve one imperative object phrase to its bare lexicon term. */
|
|
542
567
|
function imperativeNP(lexicon, tokens) {
|
|
543
568
|
const np = resolveNP(lexicon, tokens);
|
|
@@ -91,6 +91,15 @@ async function openAdventure(opening, { planHolder, memoryDir, sessionId, env, c
|
|
|
91
91
|
note: "ADVENTURE — opening declined: the world's shard failed to load",
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
+
// A world sharing this pack but shaped for a different game entirely (e.g.
|
|
95
|
+
// spider-fly, which has no player-controlled entity at all — its board is
|
|
96
|
+
// reusable static content, and player/spider/fly individuals are minted
|
|
97
|
+
// fresh by that game's own opener, never shipped in the pack) has no
|
|
98
|
+
// starting player placement. Decline cleanly rather than load a broken
|
|
99
|
+
// adventure session — the same "not necessarily an adventure ask at all"
|
|
100
|
+
// fallthrough an unrecognized name already gets, so whichever lane DOES
|
|
101
|
+
// own this name gets a chance to claim it instead.
|
|
102
|
+
if (!payload.facts.some((f) => f.subject === "player")) return null;
|
|
94
103
|
|
|
95
104
|
const tag = worldProvenanceTag(world);
|
|
96
105
|
await appendFacts(memoryDir, payload.facts.map((f) => ({
|
|
@@ -26,12 +26,14 @@ import { appendFacts, appendRule, loadMemory, readFactRows } from "../adapters/m
|
|
|
26
26
|
// ---- recognizers: the closed opening/stop/tick/address set -------------------
|
|
27
27
|
|
|
28
28
|
// The opener names the game without requiring a specific phrasing order —
|
|
29
|
-
// "watch the spider and the fly" / "play spider and fly" / "
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
29
|
+
// "watch the spider and the fly" / "play spider and fly" / "play spider fly"
|
|
30
|
+
// (the "and" is optional — playtests/PLAYTEST_LOG_006.md found it's a
|
|
31
|
+
// natural drop) / "start the spider game" all match. Closed vocabulary only
|
|
32
|
+
// (watch/play/start/begin, spider, fly, game) — no general "start X"
|
|
33
|
+
// grammar, matching this project's standing preference and adventure.mjs's
|
|
34
|
+
// own opener style.
|
|
33
35
|
const SPIDER_FLY_OPEN_RE =
|
|
34
|
-
/^(?:let'?s\s+)?(?:watch|play|start|begin)\s+(?:the\s+)?spider(?:\s+and\s+(?:the\s+)?fly)?(?:\s+game)?[.!?\s]*$/i;
|
|
36
|
+
/^(?:let'?s\s+)?(?:watch|play|start|begin)\s+(?:the\s+)?spider(?:\s+(?:and\s+)?(?:the\s+)?fly)?(?:\s+game)?[.!?\s]*$/i;
|
|
35
37
|
// "stop watching" is this game's own stop word (there's nothing to "play" in
|
|
36
38
|
// the sense of typing moves — you watch, or address an agent), kept
|
|
37
39
|
// alongside "stop playing" so either reads naturally depending on how the
|
|
@@ -21709,7 +21709,7 @@ ${codeblock}`, options);
|
|
|
21709
21709
|
});
|
|
21710
21710
|
|
|
21711
21711
|
// src/domain/grammar/ace.mjs
|
|
21712
|
-
var PATTERN_SUB_CLASS_OF, PATTERN_TYPE_ASSERTION, PATTERN_RELATION, PATTERN_SOME_VALUES_FROM, PATTERN_CARDINALITY, PATTERN_DISJOINT_WITH, PATTERN_POSSESSIVE, PATTERN_ADJECTIVE, PATTERN_CAPABILITY, PATTERNS, IMPERATIVE_VERBS, VERB_SYNONYMS, VERB_SYNONYM_MAX_PREFIX,
|
|
21712
|
+
var PATTERN_SUB_CLASS_OF, PATTERN_TYPE_ASSERTION, PATTERN_RELATION, PATTERN_SOME_VALUES_FROM, PATTERN_CARDINALITY, PATTERN_DISJOINT_WITH, PATTERN_POSSESSIVE, PATTERN_ADJECTIVE, PATTERN_CAPABILITY, PATTERNS, IMPERATIVE_VERBS, VERB_SYNONYMS, VERB_SYNONYM_MAX_PREFIX, VERB_SURFACE_WORDS;
|
|
21713
21713
|
var init_ace2 = __esm({
|
|
21714
21714
|
"src/domain/grammar/ace.mjs"() {
|
|
21715
21715
|
init_lexicon();
|
|
@@ -21758,7 +21758,7 @@ ${codeblock}`, options);
|
|
|
21758
21758
|
["shut", "close"]
|
|
21759
21759
|
]);
|
|
21760
21760
|
VERB_SYNONYM_MAX_PREFIX = Math.max(...[...VERB_SYNONYMS.keys()].map((k) => k.split(" ").length));
|
|
21761
|
-
|
|
21761
|
+
VERB_SURFACE_WORDS = [.../* @__PURE__ */ new Set([...IMPERATIVE_VERBS, ...[...VERB_SYNONYMS.keys()].map((k) => k.split(" ")[0])])];
|
|
21762
21762
|
}
|
|
21763
21763
|
});
|
|
21764
21764
|
|