@polycode-projects/the-mechanical-code-talker 2.5.4 → 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.
@@ -1388,7 +1388,8 @@
1388
1388
  lines.push(i === 0 ? `depth 1 (${level.length} direct dependents):` : `depth ${i + 1} (${level.length}):`);
1389
1389
  for (const dep of level.slice(0, IMPACT_PER_DEPTH)) {
1390
1390
  const tests = dep.tests.length ? `tests: ${capJoin(dep.tests, IMPACT_TESTS_PER_DEP)}` : "tests: none recorded";
1391
- lines.push(` - ${dep.label} (${dep.via} it) \u2014 ${tests}`);
1391
+ const receipt = i === 0 ? `${dep.via} it` : "reaches it through an intermediary";
1392
+ lines.push(` - ${dep.label} (${receipt}) \u2014 ${tests}`);
1392
1393
  }
1393
1394
  if (level.length > IMPACT_PER_DEPTH) lines.push(` \u2026+${level.length - IMPACT_PER_DEPTH} more at depth ${i + 1}`);
1394
1395
  });
@@ -1398,7 +1399,7 @@
1398
1399
  });
1399
1400
  if (truncatedStructural.length) {
1400
1401
  lines.push(
1401
- "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>)."
1402
1403
  );
1403
1404
  }
1404
1405
  return lines.join("\n");
@@ -2876,7 +2877,7 @@ ${shown.join("\n")}${tail}`;
2876
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;
2877
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;
2878
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;
2879
- 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;
2880
2881
  HEDGE_ADVERB_PREAMBLE_RE = /^(?:(?:maybe|possibly|perhaps)\s+)+(.+)$/i;
2881
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;
2882
2883
  MODAL_WRAPPER_RE = /^(?:can|could|would|will)\s+you\s+(?:please\s+)?(.+?)(?:[,\s]+please)?\??$/i;
@@ -3782,7 +3783,7 @@ ${shown.join("\n")}${tail}`;
3782
3783
  function parseComposite(text, nlp) {
3783
3784
  const w = splitWords(text);
3784
3785
  const lc = w.map((x) => x.toLowerCase());
3785
- 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);
3786
3787
  }
3787
3788
  function complementAst(entityType, diffAtom) {
3788
3789
  return {
@@ -4109,11 +4110,11 @@ ${shown.join("\n")}${tail}`;
4109
4110
  if (!term) return { node: "miss", reason: `"is/are <qualifier>" needs a named thing to check first` };
4110
4111
  return { node: "qualCheck", term, qualifier: lc[qualIdx], negated, scope: tail?.scope || null };
4111
4112
  }
4112
- function parseNegatedAsk(w, lc) {
4113
- 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;
4114
4115
  const notIdx = lc.indexOf("not", 1);
4115
4116
  if (notIdx < 0) return null;
4116
- const positive = parseAnchored(w.filter((_, i) => i !== notIdx).join(" "));
4117
+ const positive = parseSimpleClause(w.filter((_, i) => i !== notIdx).join(" "), nlp);
4117
4118
  if (!positive || positive.shape !== "ask") return null;
4118
4119
  return { ...positive, negated: true };
4119
4120
  }
@@ -4916,7 +4917,11 @@ ${shown.join("\n")}${tail}`;
4916
4917
  return { compositeKind: "temporal", matches: commits, entityType: ast.entityType, innerCount: inner.length };
4917
4918
  }
4918
4919
  function evalSuperlative(graph, ast) {
4919
- 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
+ }
4920
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);
4921
4926
  if (!scored.length) return { compositeKind: "superlative", entityType: ast.entityType, matches: [] };
4922
4927
  const best = scored[0].score;
@@ -5497,7 +5502,7 @@ ${shown.join("\n")}${tail}`;
5497
5502
  }
5498
5503
  return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
5499
5504
  }
5500
- return resolveObjectCore(graph, term, opts);
5505
+ return declineOnUnplacedWords(resolveObjectCore(graph, term, opts), term);
5501
5506
  }
5502
5507
  function resolveTermOrContext(graph, term, contextId) {
5503
5508
  if (CONTEXT_PRONOUNS.includes(String(term || "").trim().toLowerCase())) {
@@ -5770,7 +5775,7 @@ ${shown.join("\n")}${tail}`;
5770
5775
  if (entityType && entityType !== "Change") {
5771
5776
  const wantClasses = classesForKinds(graph, fwdKinds);
5772
5777
  const siblingClass = FINE_CLASS_SIBLING[entityType];
5773
- if (!wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
5778
+ if (wantClasses.size && !wantClasses.has(entityType) && !(siblingClass && wantClasses.has(siblingClass))) {
5774
5779
  return {
5775
5780
  matches: [],
5776
5781
  objMatch,
@@ -6143,13 +6148,44 @@ ${options2}
6143
6148
  };
6144
6149
  }
6145
6150
  if (result.ambiguous) {
6146
- 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
+ }
6147
6183
  const noun = pool.length && pool.every((i) => i.class === "Commit") ? "commit" : "module";
6148
6184
  const shown = pool.slice(0, OVERFLOW_CAP).map((i) => i.label);
6149
6185
  const extra2 = pool.length > OVERFLOW_CAP ? `, \u2026and ${pool.length - OVERFLOW_CAP} more` : "";
6150
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.`;
6151
- const content = result.branches && result.branches.length ? `${lead}
6152
- ${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;
6153
6189
  return {
6154
6190
  content,
6155
6191
  miss: false,
@@ -23093,7 +23129,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23093
23129
  signature: { tool: "tmct_signature", arg: "symbol", help: "a symbol's signature only" },
23094
23130
  members: { tool: "tmct_members", arg: "class", help: "the methods/attributes of a class" },
23095
23131
  subclasses: { tool: "tmct_subclasses", arg: "class", help: "the subclasses of a class" },
23096
- 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)" },
23097
23133
  callers: { tool: "tmct_callers", arg: "symbol", help: "functions that call this symbol" },
23098
23134
  callees: { tool: "tmct_callees", arg: "symbol", help: "functions this symbol calls" },
23099
23135
  tests: { tool: "tmct_tests_for", arg: "symbol", help: "the tests covering this symbol" },
@@ -23390,8 +23426,25 @@ ${JSON.stringify(envelope, null, 2)}`;
23390
23426
  }
23391
23427
  return { predicate, object: String(objectRaw || "").trim() };
23392
23428
  }
23393
- 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
+ ]);
23394
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");
23395
23448
  var TRAILING_ADVERB_RE = "(?:\\s+(?:exactly|really|actually|anyway))?";
23396
23449
  var MODULE_ORIENT_RE = new RegExp(`^what\\s+does\\s+(.+?)\\s+do${TRAILING_ADVERB_RE}\\??$`, "i");
23397
23450
  var MODULE_ORIENT_SVO_RE = new RegExp(`^what\\s+(.+?)\\s+does${TRAILING_ADVERB_RE}\\??$`, "i");
@@ -23519,6 +23572,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23519
23572
  if (f.provenance.includes("ace:chat") || f.provenance.includes("teach:chat")) return `you told me: ${factPhrase(f)}${cite}`;
23520
23573
  if (f.provenance.includes("corpus-weak:")) return `possibly: ${factPhrase(f)}${cite}`;
23521
23574
  if (f.provenance.includes("corpus:")) return `${factPhrase(f)}${cite}`;
23575
+ if (f.provenance.includes("reference:")) return `${factPhrase(f)}${cite}`;
23522
23576
  return `i learned: ${factPhrase(f)}${cite}`;
23523
23577
  }
23524
23578
  function indefiniteArticleFor(term) {
@@ -24124,12 +24178,19 @@ ${shown.map(renderFactLine).join("\n")}`,
24124
24178
  );
24125
24179
  const hit2 = hasHit(subj);
24126
24180
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
24127
- const isaStep = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && subj.has(f.subject));
24128
- if (isaStep) {
24129
- 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));
24130
24190
  if (lifted) {
24131
- return { text: `yes \u2014 ${renderFactLine(isaStep)}; ${renderFactLine(lifted)}`, replace: true };
24191
+ return { text: `yes \u2014 ${[...liftChain.map(renderFactLine), renderFactLine(lifted)].join("; ")}`, replace: true };
24132
24192
  }
24193
+ liftFrontier = factTermVariants(normFactTerm2, step.object);
24133
24194
  }
24134
24195
  return null;
24135
24196
  }
@@ -24389,6 +24450,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
24389
24450
  const directObjVariants = factTermVariants(normFactTerm2, stripTrailingDiscourseTag(directIsaAsk[2]));
24390
24451
  const directSup = inheritsChain(graph, ent.id).find((sup) => [...factTermVariants(normFactTerm2, sup.label)].some((v) => directObjVariants.has(v)));
24391
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
+ }
24392
24461
  }
24393
24462
  }
24394
24463
  }