@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.9",
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
- const VERB_FUZZY_CANDIDATES = [...new Set([...IMPERATIVE_VERBS, ...VERB_SYNONYMS.values()])];
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
- /** Resolve the leading verb token(s) to a canonical, recognised verb: the
517
- * 2-token VERB_SYNONYMS prefix FIRST (so "talk to"/"look at" resolve as
518
- * themselves rather than as the bare exact verbs "talk"/"look" they'd
519
- * otherwise short-circuit on, leaving their "to"/"at" stranded in the object
520
- * phrase), then an exact IMPERATIVE_VERBS match, then the 1-token
521
- * VERB_SYNONYMS form, then a bounded fuzzy typo repair against the same
522
- * vocabulary. `consumed` is how many leading tokens the match ate (1, or 2
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, corrected: null };
535
+ if (hit) return { verb: hit, consumed: n };
531
536
  }
532
- if (IMPERATIVE_VERBS.has(first)) return { verb: first, consumed: 1, corrected: null };
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, corrected: null };
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" / "start the
30
- // spider game" all match. Closed vocabulary only (watch/play/start/begin,
31
- // spider, fly, game) no general "start X" grammar, matching this
32
- // project's standing preference and adventure.mjs's own opener style.
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, VERB_FUZZY_CANDIDATES;
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
- VERB_FUZZY_CANDIDATES = [.../* @__PURE__ */ new Set([...IMPERATIVE_VERBS, ...VERB_SYNONYMS.values()])];
21761
+ VERB_SURFACE_WORDS = [.../* @__PURE__ */ new Set([...IMPERATIVE_VERBS, ...[...VERB_SYNONYMS.keys()].map((k) => k.split(" ")[0])])];
21762
21762
  }
21763
21763
  });
21764
21764