@polycode-projects/the-mechanical-code-talker 2.11.5 → 2.11.6

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.11.5",
3
+ "version": "2.11.6",
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.",
@@ -6904,6 +6904,7 @@ const WHAT_IS_PREP_FACT_RE = new RegExp(`^what(?:'s|\\s+is|\\s+are)\\s+(${PREP_S
6904
6904
  // the named kind via a direct isa-family fact.
6905
6905
  const DO_VERB_ASK_RE = /^(?:do|does)\s+(all\s+|every\s+)?(?:an?\s+|the\s+)?([\w'-]+(?:\s+[\w'-]+)*?)\s+([a-z-]+)[?.!\s]*$/i;
6906
6906
  const WHAT_CAN_VERB_RE = /^what\s+can\s+(?!be\s)(.+?)[?.!\s]*$/i;
6907
+ const WHAT_CANNOT_VERB_RE = /^what\s+(?:cannot|can't|cant|can\s+not)\s+(.+?)[?.!\s]*$/i;
6907
6908
  const WHICH_KIND_CAN_RE = /^(?:which|what)\s+([\w'-]+(?:\s+[\w'-]+)*?)\s+can\s+(.+?)[?.!\s]*$/i;
6908
6909
 
6909
6910
  /** The negative surface of a yes/no question asks the SAME question as its
@@ -6924,13 +6925,22 @@ const WHICH_KIND_CAN_RE = /^(?:which|what)\s+([\w'-]+(?:\s+[\w'-]+)*?)\s+can\s+(
6924
6925
  * trying the raw question first would take a garbage bind over the good one. */
6925
6926
  function positiveQuestionSurface(q) {
6926
6927
  const s = String(q || "")
6927
- .replace(/^(?:can't|cannot|can not)\s+/i, "can ")
6928
+ .replace(/^(?:can't|cannot|can not|cant)\s+/i, "can ")
6928
6929
  .replace(/^(?:doesn't|does not)\s+/i, "does ")
6929
6930
  .replace(/^(?:don't|do not)\s+/i, "do ")
6930
6931
  .replace(/^(?:didn't|did not)\s+/i, "did ")
6931
6932
  .replace(/\s+(?:not|never)\s+/i, " ");
6932
6933
  return s.replace(/\s+/g, " ").trim();
6933
6934
  }
6935
+ /** A negated surface ("can a dog not bark") is answered by the positive
6936
+ * reader (see positiveQuestionSurface's docblock), but a bare yes/no lead
6937
+ * then reads as agreeing with the asked polarity — drop the lead and let
6938
+ * the cited fact carry the real polarity on its own. */
6939
+ function withoutPolarityLead(reply) {
6940
+ const text = String(reply.text || "").replace(/^(?:yes|no) — /i, "");
6941
+ return text === reply.text ? reply : { ...reply, text };
6942
+ }
6943
+ const collapsedSurface = (q) => String(q || "").replace(/\s+/g, " ").trim();
6934
6944
 
6935
6945
  /** Cite an isa chain the way (b3b) already cites one — each step as its own
6936
6946
  * phrase plus verbatim source. Shared so the inherited-capability answers and
@@ -7607,11 +7617,13 @@ async function factAnswerReaders(memoryDir, query, envelope, miss, biasByBundle
7607
7617
  // capabilityReply). Mirrors the ISA_ASK_RE block just above on the "never a
7608
7618
  // guessed no" discipline: a "no" here is a REMEMBERED negative, never the
7609
7619
  // absence of a positive.
7610
- const can = positiveQuestionSurface(q).match(CAN_ASK_RE);
7620
+ const surfacedCan = positiveQuestionSurface(q);
7621
+ const can = surfacedCan.match(CAN_ASK_RE);
7611
7622
  if (can) {
7612
7623
  const facts = await factRows(memoryDir, cache);
7613
7624
  const canUniversal = can[1];
7614
- const reply = capabilityReply(can[2], can[3], facts);
7625
+ let reply = capabilityReply(can[2], can[3], facts);
7626
+ if (reply && surfacedCan !== collapsedSurface(q)) reply = withoutPolarityLead(reply);
7615
7627
  // Quantified ("can all/every X ..."): the stored facts are generic, and a
7616
7628
  // bare "yes" would claim universality the memory can't support — the same
7617
7629
  // hedge the do-support surface applies, echoing the quantifier as typed.
@@ -7711,7 +7723,8 @@ async function factAnswerReaders(memoryDir, query, envelope, miss, biasByBundle
7711
7723
  // (falls through instead): the shape is looser than (b2)'s, so a do-lead
7712
7724
  // question some later reader owns must keep its turn. The can't-confirm
7713
7725
  // branch is additionally miss-gated for the same reason.
7714
- const doAsk = positiveQuestionSurface(q).match(DO_VERB_ASK_RE);
7726
+ const surfacedDo = positiveQuestionSurface(q);
7727
+ const doAsk = surfacedDo.match(DO_VERB_ASK_RE);
7715
7728
  if (doAsk) {
7716
7729
  const facts = await factRows(memoryDir, cache);
7717
7730
  const universal = !!doAsk[1];
@@ -7719,7 +7732,8 @@ async function factAnswerReaders(memoryDir, query, envelope, miss, biasByBundle
7719
7732
  const obj = factTermVariants(normFactTerm, doAsk[3]);
7720
7733
  // the SAME resolver (b2) answers through, so "do penguins fly" and "can a
7721
7734
  // penguin fly" can never disagree in one session
7722
- const reply = capabilityReply(doAsk[2], doAsk[3], facts);
7735
+ let reply = capabilityReply(doAsk[2], doAsk[3], facts);
7736
+ if (reply && surfacedDo !== collapsedSurface(q)) reply = withoutPolarityLead(reply);
7723
7737
  if (reply && universal) {
7724
7738
  // Echo the quantifier as typed ("every dog", "all dogs") — "all dog"
7725
7739
  // for a singular every-question is a garbled echo.
@@ -7838,6 +7852,28 @@ async function factAnswerReaders(memoryDir, query, envelope, miss, biasByBundle
7838
7852
  }
7839
7853
  }
7840
7854
 
7855
+ // (b3c-neg) "what cannot fly" — the negative twin of (b3c): every stored
7856
+ // mgxneg:capableOf fact whose OBJECT matches. A matched shape with NO
7857
+ // stored negatives returns a definitive memory miss rather than falling
7858
+ // through — the conversational catch-all downstream misread this surface
7859
+ // as small talk and answered with the identity card.
7860
+ const cannotVerb = q.match(WHAT_CANNOT_VERB_RE);
7861
+ if (cannotVerb && cannotVerb[1].trim().split(/\s+/).at(-1)?.toLowerCase() !== "do") {
7862
+ const negVariants = factTermVariants(normFactTerm, cannotVerb[1]);
7863
+ const negHits = (await factRows(memoryDir, cache)).filter(
7864
+ (f) => f.predicate === "mgxneg:capableOf" && negVariants.has(f.object),
7865
+ );
7866
+ if (negHits.length) {
7867
+ const ranked = rankByBiasThenTrust(uniqueFacts(negHits), biasByBundle);
7868
+ const lines = ranked.map(renderFactLine);
7869
+ const shown = lines.slice(0, FACT_ANSWER_CAP);
7870
+ const rest = lines.slice(FACT_ANSWER_CAP);
7871
+ const extra = rest.length ? `\n…and ${rest.length} more — say 'more' to see them.` : "";
7872
+ return { text: shown.join("\n") + extra, replace: true, ...(rest.length ? { pending: { items: rest, noun: "facts" } } : {}) };
7873
+ }
7874
+ return { text: `nothing I remember says anything cannot ${cannotVerb[1].trim()}.`, replace: true, miss: true };
7875
+ }
7876
+
7841
7877
  // (b3c) "what can fly" — the unrestricted reverse-by-verb sibling of (b3b):
7842
7878
  // every capableOf fact whose OBJECT matches. The "… do" tail is (b3)'s
7843
7879
  // shape, guarded out so a zero-hit "what can a cat do" never gets misread
@@ -12218,7 +12254,8 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
12218
12254
  // same divert-only-on-a-real-hit treatment as the reverse predicates
12219
12255
  // above.
12220
12256
  const capabilityAskShape = CAN_ASK_RE.test(gateQuery) || WHAT_CAN_DO_RE.test(gateQuery)
12221
- || DO_VERB_ASK_RE.test(gateQuery) || WHICH_KIND_CAN_RE.test(gateQuery) || WHAT_CAN_VERB_RE.test(gateQuery);
12257
+ || DO_VERB_ASK_RE.test(gateQuery) || WHICH_KIND_CAN_RE.test(gateQuery) || WHAT_CAN_VERB_RE.test(gateQuery)
12258
+ || WHAT_CANNOT_VERB_RE.test(gateQuery);
12222
12259
  // A bare "who is/was <name>" (no relational tail) is as short as the
12223
12260
  // vocabulary openers above and trips isConversational's word-count catch-all
12224
12261
  // the same way — factReadBack's bare-who reader surfaces the person's stored