@polycode-projects/the-mechanical-code-talker 2.6.0 → 2.6.1

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.
@@ -103,7 +103,9 @@ export function upsertSession(entities, record) {
103
103
 
104
104
  const started = record.started || "";
105
105
  const ended = record.ended || turns.at(-1)?.ts || started;
106
- const queries = turns.map((t) => String(t?.query || "")).filter(Boolean);
106
+ // The session's recorded questions quote the user VERBATIM (`input`) when
107
+ // the turn carried a rewrite; `query` is the fallback for older sidecars.
108
+ const queries = turns.map((t) => String(t?.input || t?.query || "")).filter(Boolean);
107
109
  const label = sessionLabel(record.id);
108
110
  entities.individuals.push({
109
111
  id: sid, label, class: SESSION_CLASS,
@@ -206,6 +208,9 @@ async function recordSessionMemory(graphFile, record, repoDirOverride = null) {
206
208
  const utterances = [];
207
209
  for (const t of record.turns || []) {
208
210
  const query = String(t?.query || "");
211
+ // The visitor utterance quotes the user VERBATIM; `query` (possibly a
212
+ // rewrite) stays the transcript-matching key below, unchanged.
213
+ const spoken = String(t?.input || t?.query || "");
209
214
  const ts = String(t?.ts || "");
210
215
  if (!query || !ts) continue;
211
216
  // the structured parse the turn produced — stored on the visitor utterance
@@ -216,7 +221,7 @@ async function recordSessionMemory(graphFile, record, repoDirOverride = null) {
216
221
  if (t.miss) parsed.miss = true;
217
222
  if (t.via) parsed.via = t.via; // answer provenance (W1) — carried into memory
218
223
  utterances.push({
219
- role: "visitor", text: query, ts, sessionId: record.id, sessionStarted: record.started || "",
224
+ role: "visitor", text: spoken, ts, sessionId: record.id, sessionStarted: record.started || "",
220
225
  ...(Object.keys(parsed).length ? { parsed } : {}),
221
226
  });
222
227
  const answer = answers.get(turnKey(ts, query));
@@ -258,6 +263,9 @@ export function parseSessionJsonl(text) {
258
263
  else if (rec?.type === "turn") {
259
264
  turns.push({
260
265
  ts: String(rec.ts || ""), query: String(rec.query || ""),
266
+ // the VERBATIM user line, recorded beside any rewritten query — the
267
+ // recall surfaces quote this form, never the rewrite.
268
+ ...(rec.input ? { input: String(rec.input) } : {}),
261
269
  resolvedIds: arr(rec.resolvedIds), answeredIds: arr(rec.answeredIds), miss: !!rec.miss,
262
270
  // preserved for the memory fold (memory/fold.mjs): slash-command turns and
263
271
  // conversational filler are recorded but never folded into the corpus.
@@ -1399,7 +1399,7 @@
1399
1399
  });
1400
1400
  if (truncatedStructural.length) {
1401
1401
  lines.push(
1402
- "warning: partial edge lists (" + truncatedStructural.map((t) => `${t.predicate}: ${t.shown}/${t.count}`).join(", ") + ") \u2014 this closure may be missing edges. Cross-check critical results with tmct_search."
1402
+ "warning: partial edge lists (" + truncatedStructural.map((t) => `${t.predicate}: ${t.shown}/${t.count}`).join(", ") + ") \u2014 this closure may be missing edges. Cross-check critical results with a lexical search (/find <term>)."
1403
1403
  );
1404
1404
  }
1405
1405
  return lines.join("\n");
@@ -2877,7 +2877,7 @@ ${shown.join("\n")}${tail}`;
2877
2877
  GREETING_PREAMBLE_RE = /^(?:hi|hiya|hello|hey|yo|howdy|g'?day|yeah\s+nah|good\s+(?:morning|afternoon|evening|day)|greetings|salutations)(?:\s+(?:there|pardner|folks|friend|mate))?\s*[,.—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
2878
2878
  THANKS_PREAMBLE_RE = /^(?:thanks|thank\s+you|many\s+thanks|thx|ty|cheers)(?:\s+(?:so\s+much|a\s+lot|very\s+much|a\s+bunch))?\s*[,—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
2879
2879
  ACK_PREAMBLE_RE = /^(?:(?:ok(?:ay)?|aight|cool|alright|sure|right|fine|great|nice|got it|gotcha|sounds good|no worries|no problem)[\s,]+)+(.+)$/i;
2880
- BROWSING_PREAMBLE_RE = /^(?:just\s+(?:poking\s+around|looking\s+around|browsing|exploring|checking\s+(?:this|it)\s+out)|first\s+time\s+(?:trying\s+this\s+out|using\s+this|here))\s*[,.—–-]\s*(.+)$/i;
2880
+ BROWSING_PREAMBLE_RE = /^(?:just\s+(?:poking\s+around|looking\s+around|browsing|exploring|checking\s+(?:this|it)\s+out)|first\s+time\s+(?:trying\s+this\s+out|using\s+this|here)|i'?m\s+new\s+(?:here|around\s+here|to\s+(?:this|all\s+this)(?:\s+(?:repo|codebase|project|app|tool|thing))?))\s*[,.—–-]\s*(.+)$/i;
2881
2881
  HEDGE_ADVERB_PREAMBLE_RE = /^(?:(?:maybe|possibly|perhaps)\s+)+(.+)$/i;
2882
2882
  TROUBLE_ASIDE_RE = /,?\s*if\s+(?:it'?s|it\s+is|that'?s|that\s+is)\s+not\s+too\s+much\s+(?:trouble|bother|hassle)\s*,?\s*/i;
2883
2883
  MODAL_WRAPPER_RE = /^(?:can|could|would|will)\s+you\s+(?:please\s+)?(.+?)(?:[,\s]+please)?\??$/i;
@@ -3783,7 +3783,7 @@ ${shown.join("\n")}${tail}`;
3783
3783
  function parseComposite(text, nlp) {
3784
3784
  const w = splitWords(text);
3785
3785
  const lc = w.map((x) => x.toLowerCase());
3786
- return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseUniversal(w, lc, nlp) || parseNegation(text, nlp, 0) || parseNegatedAsk(w, lc) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || parseCommitFilter(w, lc) || parseAnaphora(w, lc, nlp) || parseAggregate(w, lc, nlp) || parseSuperlative(w, lc, nlp) || parseFind(w, lc, nlp, 0) || parseList(w, lc, nlp, 0) || parseNested(w, lc, nlp, 0) || parsePluralAnaphoraObject(w, lc, nlp) || parseRelationalOrQualified(w, lc, nlp, 0);
3786
+ return parseExistence(w, lc) || parseQualifierCheck(w, lc) || parseUniversal(w, lc, nlp) || parseNegation(text, nlp, 0) || parseNegatedAsk(w, lc, nlp) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || parseCommitFilter(w, lc) || parseAnaphora(w, lc, nlp) || parseAggregate(w, lc, nlp) || parseSuperlative(w, lc, nlp) || parseFind(w, lc, nlp, 0) || parseList(w, lc, nlp, 0) || parseNested(w, lc, nlp, 0) || parsePluralAnaphoraObject(w, lc, nlp) || parseRelationalOrQualified(w, lc, nlp, 0);
3787
3787
  }
3788
3788
  function complementAst(entityType, diffAtom) {
3789
3789
  return {
@@ -4110,11 +4110,11 @@ ${shown.join("\n")}${tail}`;
4110
4110
  if (!term) return { node: "miss", reason: `"is/are <qualifier>" needs a named thing to check first` };
4111
4111
  return { node: "qualCheck", term, qualifier: lc[qualIdx], negated, scope: tail?.scope || null };
4112
4112
  }
4113
- function parseNegatedAsk(w, lc) {
4114
- if (lc[0] !== "do" && lc[0] !== "does" && lc[0] !== "did") return null;
4113
+ function parseNegatedAsk(w, lc, nlp) {
4114
+ if (!["do", "does", "did", "is", "are", "was", "were"].includes(lc[0])) return null;
4115
4115
  const notIdx = lc.indexOf("not", 1);
4116
4116
  if (notIdx < 0) return null;
4117
- const positive = parseAnchored(w.filter((_, i) => i !== notIdx).join(" "));
4117
+ const positive = parseSimpleClause(w.filter((_, i) => i !== notIdx).join(" "), nlp);
4118
4118
  if (!positive || positive.shape !== "ask") return null;
4119
4119
  return { ...positive, negated: true };
4120
4120
  }
@@ -4917,7 +4917,11 @@ ${shown.join("\n")}${tail}`;
4917
4917
  return { compositeKind: "temporal", matches: commits, entityType: ast.entityType, innerCount: inner.length };
4918
4918
  }
4919
4919
  function evalSuperlative(graph, ast) {
4920
- const pool = graph.individuals.filter((i) => i.class === ast.entityType);
4920
+ let pool = graph.individuals.filter((i) => i.class === ast.entityType);
4921
+ if (ast.metric?.kind === "tests" && ast.entityType === "Module") {
4922
+ const testSubjects = new Set(edgesOfKind2(graph, "tests").map((e) => e.subject));
4923
+ pool = pool.filter((i) => !testSubjects.has(i.id) && !isTestPath(String(i.label).toLowerCase()));
4924
+ }
4921
4925
  const scored = pool.map((ind) => ({ ind, score: degreeMetric(graph, ind, ast.metric) })).sort((a, z) => ast.extreme === "most" ? z.score - a.score : a.score - z.score);
4922
4926
  if (!scored.length) return { compositeKind: "superlative", entityType: ast.entityType, matches: [] };
4923
4927
  const best = scored[0].score;
@@ -5498,7 +5502,7 @@ ${shown.join("\n")}${tail}`;
5498
5502
  }
5499
5503
  return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
5500
5504
  }
5501
- return resolveObjectCore(graph, term, opts);
5505
+ return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
5502
5506
  }
5503
5507
  function resolveTermOrContext(graph, term, contextId) {
5504
5508
  if (CONTEXT_PRONOUNS.includes(String(term || "").trim().toLowerCase())) {
@@ -5771,7 +5775,7 @@ ${shown.join("\n")}${tail}`;
5771
5775
  if (entityType && entityType !== "Change") {
5772
5776
  const wantClasses = classesForKinds(graph, fwdKinds);
5773
5777
  const siblingClass = FINE_CLASS_SIBLING[entityType];
5774
- if (!wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
5778
+ if (wantClasses.size && !wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
5775
5779
  return {
5776
5780
  matches: [],
5777
5781
  objMatch,
@@ -6144,13 +6148,44 @@ ${options2}
6144
6148
  };
6145
6149
  }
6146
6150
  if (result.ambiguous) {
6147
- const pool = [result.objMatch, ...result.candidates || []].filter(Boolean);
6151
+ let pool = [result.objMatch, ...result.candidates || []].filter(Boolean);
6152
+ let branches = result.branches;
6153
+ if (parsed.kind && parsed.kind !== "touches" && !pool.every((i) => i.class === "Commit")) {
6154
+ const grainOk = (i) => !["Commit", "Session", "Source", "Utterance"].includes(i?.class);
6155
+ const kept = pool.filter(grainOk);
6156
+ if (kept.length) {
6157
+ pool = kept;
6158
+ if (branches?.length) branches = branches.filter((b) => grainOk(b.candidate));
6159
+ }
6160
+ }
6161
+ if (graph && !/[/.]/.test(String(parsed.object || ""))) {
6162
+ const tLc = String(parsed.object || "").toLowerCase();
6163
+ const termWords = new Set(splitIdentifierWords(String(parsed.object || "")).filter((w) => w.length >= 3));
6164
+ const already = new Set(pool.map((i) => i.id));
6165
+ let nearest = null;
6166
+ let bestShared = 0;
6167
+ let bestD = Infinity;
6168
+ if (termWords.size) {
6169
+ for (const i of graph.individuals) {
6170
+ if (!["Function", "Method", "Class", "GlobalVariable", "Attribute"].includes(i.class) || already.has(i.id)) continue;
6171
+ const shared = splitIdentifierWords(String(i.label)).filter((w) => termWords.has(w)).length;
6172
+ if (!shared || shared < bestShared) continue;
6173
+ const d = editDistance(String(i.label).toLowerCase(), tLc, 8);
6174
+ if (shared > bestShared || d < bestD) {
6175
+ nearest = i;
6176
+ bestShared = shared;
6177
+ bestD = d;
6178
+ }
6179
+ }
6180
+ }
6181
+ if (nearest) pool = [...pool, nearest];
6182
+ }
6148
6183
  const noun = pool.length && pool.every((i) => i.class === "Commit") ? "commit" : "module";
6149
6184
  const shown = pool.slice(0, OVERFLOW_CAP).map((i) => i.label);
6150
6185
  const extra2 = pool.length > OVERFLOW_CAP ? `, \u2026and ${pool.length - OVERFLOW_CAP} more` : "";
6151
6186
  const lead = `"${parsed.object}" matches more than one ${noun} ambiguously \u2014 did you mean ${listJoin(shown)}${extra2}? Try one of those. If you're not sure, narrow it to one name.`;
6152
- const content = result.branches && result.branches.length ? `${lead}
6153
- ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.content}`).join("\n")}` : lead;
6187
+ const content = branches && branches.length ? `${lead}
6188
+ ${branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.content}`).join("\n")}` : lead;
6154
6189
  return {
6155
6190
  content,
6156
6191
  miss: false,
@@ -23094,7 +23129,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23094
23129
  signature: { tool: "tmct_signature", arg: "symbol", help: "a symbol's signature only" },
23095
23130
  members: { tool: "tmct_members", arg: "class", help: "the methods/attributes of a class" },
23096
23131
  subclasses: { tool: "tmct_subclasses", arg: "class", help: "the subclasses of a class" },
23097
- impact: { tool: "tmct_impact", arg: "module", help: "what a change to this module reaches (impact closure)" },
23132
+ impact: { tool: "tmct_impact", arg: "module", help: "what a change to this module or symbol reaches (impact closure)" },
23098
23133
  callers: { tool: "tmct_callers", arg: "symbol", help: "functions that call this symbol" },
23099
23134
  callees: { tool: "tmct_callees", arg: "symbol", help: "functions this symbol calls" },
23100
23135
  tests: { tool: "tmct_tests_for", arg: "symbol", help: "the tests covering this symbol" },
@@ -23391,8 +23426,25 @@ ${JSON.stringify(envelope, null, 2)}`;
23391
23426
  }
23392
23427
  return { predicate, object: String(objectRaw || "").trim() };
23393
23428
  }
23394
- var TEACH_PRONOUNS = Object.freeze(["you", "i", "it", "they", "he", "she", "we"]);
23429
+ var TEACH_PRONOUNS = Object.freeze([
23430
+ "you're",
23431
+ "i'm",
23432
+ "it's",
23433
+ "they're",
23434
+ "he's",
23435
+ "she's",
23436
+ "we're",
23437
+ "you",
23438
+ "i",
23439
+ "it",
23440
+ "they",
23441
+ "he",
23442
+ "she",
23443
+ "we"
23444
+ ]);
23395
23445
  var TEACH_PRONOUN_RE = new RegExp(`^(?:every\\s+|each\\s+|all\\s+|some\\s+|a few\\s+|a\\s+|an\\s+)?(${TEACH_PRONOUNS.join("|")})\\s+\\S+`, "i");
23446
+ var BOARD_TEACH_LOCATIVE_RE = new RegExp(`^([\\w-]+)\\s+([a-z]+)s\\s+(${PREP_SRC})\\s+([\\w-]+)$`, "i");
23447
+ var RETRACT_FORGET_LOCATIVE_RE = new RegExp(`^forget\\s+(?:that\\s+)?([\\w-]+)\\s+([a-z]+)s\\s+(${PREP_SRC})\\s+([\\w-]+)$`, "i");
23396
23448
  var TRAILING_ADVERB_RE = "(?:\\s+(?:exactly|really|actually|anyway))?";
23397
23449
  var MODULE_ORIENT_RE = new RegExp(`^what\\s+does\\s+(.+?)\\s+do${TRAILING_ADVERB_RE}\\??$`, "i");
23398
23450
  var MODULE_ORIENT_SVO_RE = new RegExp(`^what\\s+(.+?)\\s+does${TRAILING_ADVERB_RE}\\??$`, "i");
@@ -23520,6 +23572,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23520
23572
  if (f.provenance.includes("ace:chat") || f.provenance.includes("teach:chat")) return `you told me: ${factPhrase(f)}${cite}`;
23521
23573
  if (f.provenance.includes("corpus-weak:")) return `possibly: ${factPhrase(f)}${cite}`;
23522
23574
  if (f.provenance.includes("corpus:")) return `${factPhrase(f)}${cite}`;
23575
+ if (f.provenance.includes("reference:")) return `${factPhrase(f)}${cite}`;
23523
23576
  return `i learned: ${factPhrase(f)}${cite}`;
23524
23577
  }
23525
23578
  function indefiniteArticleFor(term) {
@@ -24125,12 +24178,19 @@ ${shown.map(renderFactLine).join("\n")}`,
24125
24178
  );
24126
24179
  const hit2 = hasHit(subj);
24127
24180
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
24128
- const isaStep = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && subj.has(f.subject));
24129
- if (isaStep) {
24130
- const lifted = hasHit(factTermVariants(normFactTerm2, isaStep.object));
24181
+ let liftFrontier = subj;
24182
+ const liftChain = [];
24183
+ const liftSeen = /* @__PURE__ */ new Set();
24184
+ for (let hop = 0; hop < 4; hop += 1) {
24185
+ const step = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && liftFrontier.has(f.subject) && !liftSeen.has(f.object));
24186
+ if (!step) break;
24187
+ liftSeen.add(step.object);
24188
+ liftChain.push(step);
24189
+ const lifted = hasHit(factTermVariants(normFactTerm2, step.object));
24131
24190
  if (lifted) {
24132
- return { text: `yes \u2014 ${renderFactLine(isaStep)}; ${renderFactLine(lifted)}`, replace: true };
24191
+ return { text: `yes \u2014 ${[...liftChain.map(renderFactLine), renderFactLine(lifted)].join("; ")}`, replace: true };
24133
24192
  }
24193
+ liftFrontier = factTermVariants(normFactTerm2, step.object);
24134
24194
  }
24135
24195
  return null;
24136
24196
  }
@@ -24390,6 +24450,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
24390
24450
  const directObjVariants = factTermVariants(normFactTerm2, stripTrailingDiscourseTag(directIsaAsk[2]));
24391
24451
  const directSup = inheritsChain(graph, ent.id).find((sup) => [...factTermVariants(normFactTerm2, sup.label)].some((v) => directObjVariants.has(v)));
24392
24452
  if (directSup) return { text: `yes \u2014 the code graph says ${ent.label} inherits ${directSup.label}.`, replace: true };
24453
+ const objEnt = await resolveEntity(graph, stripTrailingDiscourseTag(directIsaAsk[2]));
24454
+ if (objEnt && inheritsChain(graph, objEnt.id).some((sup) => sup.id === ent.id)) {
24455
+ return {
24456
+ text: `I can't confirm that \u2014 the code graph's stored direction runs the other way: ${objEnt.label} inherits ${ent.label}. An inheritance doesn't reverse.`,
24457
+ replace: true,
24458
+ miss: true
24459
+ };
24460
+ }
24393
24461
  }
24394
24462
  }
24395
24463
  }