@polycode-projects/the-mechanical-code-talker 1.10.13 → 1.10.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/chat.mjs +50 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/the-mechanical-code-talker",
3
- "version": "1.10.13",
3
+ "version": "1.10.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.",
package/src/chat.mjs CHANGED
@@ -1533,6 +1533,12 @@ const WALL_MISS_ANYWHERE_RE = /couldn't parse this as a graph question\. Try:/;
1533
1533
  // instead of the grammar wall or a silent data loss.
1534
1534
  const TEACH_RE = /^(?:please\s+)?(?:i\s+(?:want|wanted)\s+you\s+to\s+|i(?:'d|\s+would)\s+like\s+you\s+to\s+)?(?:remember|note|keep in mind|jot down|for the record|fyi|learn)\b(?:\s+(?:this|that|also))?[:,]?\s*(?:that\s+)?(.+?)[.?!]*$/i;
1535
1535
  const BARE_DECLARATIVE_RE = /^(?:every |each |all |a |an )?[\w-]+(?: [\w-]+)? (?:is|are) (?:a |an )?[\w-]+$/i;
1536
+ /** "X is <comparative> than Y" — the comparative teach/ask surface. The
1537
+ * comparative slot is closed by SHAPE (-er word, better/worse, or a
1538
+ * more/less + adjective pair), never a hand-list of adjectives. */
1539
+ const COMPARATIVE_SRC = "(?:[a-z]+er|better|worse|(?:more|less)\\s+[a-z]+)";
1540
+ const COMPARATIVE_TEACH_RE = new RegExp(`^(?:the\\s+|an?\\s+)?([\\w'-]+(?:\\s+[\\w'-]+)?)\\s+(?:is|are)\\s+(${COMPARATIVE_SRC})\\s+than\\s+(.+)$`, "i");
1541
+ const COMPARATIVE_ASK_RE = new RegExp(`^(?:is|are)\\s+(.+?)\\s+(${COMPARATIVE_SRC})\\s+than\\s+(.+?)[?.!\\s]*$`, "i");
1536
1542
  /** Interrogative / auxiliary leads that make an "X is a Y"-shaped line a QUESTION
1537
1543
  * ("what is a cache", "is a module a component"), never a teach declarative. */
1538
1544
  const QUESTION_LEAD_RE = /^(?:what|who|which|where|when|why|how|is|are|do|does|did|can|could|should|would|will|has|have)\b/i;
@@ -2879,7 +2885,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
2879
2885
 
2880
2886
  let payload = null;
2881
2887
  if (wrapped && /\b(?:is|are)\b/i.test(wrapped)) payload = wrapped;
2882
- else if (BARE_DECLARATIVE_RE.test(raw) && !QUESTION_LEAD_RE.test(raw) && !(await hasMidSentenceInterrogative(raw))) payload = raw;
2888
+ else if ((BARE_DECLARATIVE_RE.test(raw) || COMPARATIVE_TEACH_RE.test(raw)) && !QUESTION_LEAD_RE.test(raw) && !(await hasMidSentenceInterrogative(raw))) payload = raw;
2883
2889
  if (!payload) {
2884
2890
  // "remember margo eats ribs", re-escaping here through a combination
2885
2891
  // that mechanism's own deliberate subject-shape restriction doesn't
@@ -2897,6 +2903,19 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
2897
2903
  // Try to store it (a live session provides the write target). assertTurn returns
2898
2904
  // the "noted — remembered …" confirmation or null (grammar miss / unknown words).
2899
2905
  if (memoryDir) {
2906
+ // COMPARATIVE frame — "disk-1 is smaller than disk-2" → mgx:smaller-than.
2907
+ // Checked ahead of the ACE candidates: the copula plus "than" is not in
2908
+ // the ACE fragment at all, and letting it fall through produced the
2909
+ // both-sides-ungrounded decline (honest but unactionable — no phrasing
2910
+ // it could suggest would have stored a comparison).
2911
+ const comp = String(payload).trim().match(COMPARATIVE_TEACH_RE);
2912
+ if (comp) {
2913
+ const compPredicate = `mgx:${comp[2].toLowerCase().replace(/\s+/g, "-")}-than`;
2914
+ const stored = await teachFact(memoryDir, sessionId, {
2915
+ subject: comp[1].trim(), predicate: compPredicate, object: comp[3].trim(),
2916
+ });
2917
+ if (stored) return stored;
2918
+ }
2900
2919
  for (const cand of assertCandidates(payload)) {
2901
2920
  // assertTurn ITSELF records the "every" quantifier (point 3) on a plain
2902
2921
  // universal success, so every caller (this loop AND the top-level
@@ -3746,7 +3765,12 @@ function thirdPersonSingularSurface(lemma) {
3746
3765
  }
3747
3766
  function predicatePhrase(predicate) {
3748
3767
  if (FACT_PREDICATE_PHRASES[predicate]) return FACT_PREDICATE_PHRASES[predicate];
3749
- const m = /^mgx:([a-z]+)(?:-([a-z]+))?$/i.exec(String(predicate || ""));
3768
+ const p = String(predicate || "");
3769
+ // a comparative renders as its copula surface: mgx:smaller-than ->
3770
+ // "is smaller than" (never a 3sg fold — "smallers" isn't a word)
3771
+ const comp = /^mgx:([a-z]+(?:-[a-z]+)*)-than$/i.exec(p);
3772
+ if (comp) return `is ${comp[1].replace(/-/g, " ")} than`;
3773
+ const m = /^mgx:([a-z]+)(?:-([a-z]+))?$/i.exec(p);
3750
3774
  if (!m) return predicate;
3751
3775
  // a folded preposition renders back naturally: mgx:rest-on -> "rests on"
3752
3776
  return `${thirdPersonSingularSurface(m[1])}${m[2] ? ` ${m[2]}` : ""}`;
@@ -4385,6 +4409,30 @@ export async function factAnswer(memoryDir, query, envelope, miss, biasByBundle
4385
4409
  }
4386
4410
  if (!miss) return null;
4387
4411
 
4412
+ // (b0-comp) "is disk-1 smaller than disk-2" — yes iff the exact taught
4413
+ // comparative fact exists; otherwise an honest, specific miss whose teach
4414
+ // hint is the EXACT phrasing the comparative teach frame accepts. Never an
4415
+ // inverted guess: "disk-1 is smaller than disk-2" proves nothing here
4416
+ // about "is disk-2 smaller than disk-1" (the frame stores no
4417
+ // antisymmetry), so the reverse question stays a can't-confirm.
4418
+ const compAsk = q.match(COMPARATIVE_ASK_RE);
4419
+ if (compAsk) {
4420
+ const compWord = compAsk[2].toLowerCase().replace(/\s+/g, "-");
4421
+ const compPredicate = `mgx:${compWord}-than`;
4422
+ const facts = await memoryFacts(memoryDir);
4423
+ const subj = factTermVariants(normFactTerm, compAsk[1].replace(/^(?:an?|the)\s+/i, "").trim());
4424
+ const obj = factTermVariants(normFactTerm, compAsk[3].replace(/^(?:an?|the)\s+/i, "").trim());
4425
+ const hit = facts.find((f) => f.predicate === compPredicate && subj.has(f.subject) && obj.has(f.object));
4426
+ if (hit) return { text: `yes — ${renderFactLine(hit)}`, replace: true };
4427
+ const known = facts.filter((f) => f.predicate === compPredicate && (subj.has(f.subject) || subj.has(f.object)));
4428
+ const shown = known.length ? ` I do know: ${known.slice(0, 3).map(renderFactLine).join("; ")}.` : "";
4429
+ return {
4430
+ text: `I can't confirm that — nothing I remember compares them that way.${shown} If it's true, teach me: "${compAsk[1].trim()} is ${compAsk[2].toLowerCase()} than ${compAsk[3].trim()}".`,
4431
+ replace: true,
4432
+ miss: true,
4433
+ };
4434
+ }
4435
+
4388
4436
  // (b0) Derived forward yes/no readers — FORWARD_YESNO_MARKERS, one per
4389
4437
  // renderable relation. Runs BEFORE the isa lane because ISA_ASK_RE's lazy
4390
4438
  // subject otherwise swallows these shapes whole ("is a wheel part of a