@polycode-projects/the-mechanical-code-talker 1.10.14 → 1.11.5

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.
@@ -92,13 +92,14 @@ export function parseKeywordSpot(text, nlp = null) {
92
92
  verbHit = findPhrase(lemmaWords, VERB_TO_KIND);
93
93
  if (verbHit) canonWords = lemmaWords;
94
94
  }
95
+ let fuzzyVerb = false;
95
96
  if (!verbHit) {
96
97
  // tier 3: bounded-edit-distance rewrite toward verb/modifier keywords only
97
98
  // ("impotr" -> "import"); ≥4-char words only — below that the bound covers
98
99
  // half of English (and "and" is 1 edit from the "land in" constituent).
99
100
  const fuzzyWords = lcWords.map((w) => (w.length >= 4 && eligibleForCanon(w) ? fuzzyVocabWord(w) || w : w));
100
101
  verbHit = findPhrase(fuzzyWords, VERB_TO_KIND);
101
- if (verbHit) canonWords = fuzzyWords;
102
+ if (verbHit) { canonWords = fuzzyWords; fuzzyVerb = true; }
102
103
  }
103
104
  if (!verbHit && lcWords.includes("by")) {
104
105
  // A participle with no active verb entry still marks a passive when a passive
@@ -109,6 +110,9 @@ export function parseKeywordSpot(text, nlp = null) {
109
110
  }
110
111
  }
111
112
  if (!verbHit) return null;
113
+ // A tier-3 verb is a REPAIR, not a reading — downstream consumers (the teach
114
+ // lane's canonical receipt) need to know the difference, so it rides the AST.
115
+ const stamp = (ast) => (fuzzyVerb ? { ...ast, fuzzyVerb: true } : ast);
112
116
  // POS rescue (Node-side only): a relation word used as a NOUN in a "the
113
117
  // <imports> of <term>" frame would otherwise misparse; only fires inside this
114
118
  // exact det+NOUN+"of" shape, since the same word tags NOUN in genuine verb use too.
@@ -119,7 +123,7 @@ export function parseKeywordSpot(text, nlp = null) {
119
123
  const tags = nlp.posTags(words);
120
124
  if (tags[i] === "NOUN") {
121
125
  const objText = words.slice(i + 2).filter((w, j) => !STOPWORDS.has(lcWords[i + 2 + j])).join(" ").trim();
122
- if (objText) return { shape: "forward", entityType: null, modifier: "direct", kind: verbHit.kind, object: objText };
126
+ if (objText) return stamp({ shape: "forward", entityType: null, modifier: "direct", kind: verbHit.kind, object: objText });
123
127
  }
124
128
  }
125
129
  }
@@ -154,7 +158,7 @@ export function parseKeywordSpot(text, nlp = null) {
154
158
  // "when" turns a touches decomposition temporal; other verbs fall through.
155
159
  if (kind === "touches" && lcWords.includes("when")) {
156
160
  const objText = beforeText || afterText;
157
- if (objText) return { shape: "when", entityType: null, modifier: "direct", kind: "touches", object: objText };
161
+ if (objText) return stamp({ shape: "when", entityType: null, modifier: "direct", kind: "touches", object: objText });
158
162
  }
159
163
 
160
164
  // "who last touched X" would otherwise list every touching commit's author,
@@ -162,7 +166,7 @@ export function parseKeywordSpot(text, nlp = null) {
162
166
  // stopwords and wouldn't survive into beforeText/afterText.
163
167
  if (kind === "touches" && lcWords.includes("who") && lcWords.includes("last")) {
164
168
  const objText = beforeText || afterText;
165
- if (objText) return { shape: "whoLast", entityType: null, modifier: "direct", kind: "touches", object: objText };
169
+ if (objText) return stamp({ shape: "whoLast", entityType: null, modifier: "direct", kind: "touches", object: objText });
166
170
  }
167
171
 
168
172
  // Reversible passive ("PATIENT is VERBed BY AGENT"): a passive auxiliary plus a
@@ -190,7 +194,7 @@ export function parseKeywordSpot(text, nlp = null) {
190
194
  nextAfterBy = lcWords[i]; break;
191
195
  }
192
196
  const agentNamed = nextAfterBy != null && !WH_WORDS.has(nextAfterBy) && !ENTITY_TO_TYPE[nextAfterBy];
193
- return { shape: agentNamed ? "forward" : "reverse", entityType, modifier, kind, object };
197
+ return stamp({ shape: agentNamed ? "forward" : "reverse", entityType, modifier, kind, object });
194
198
  }
195
199
  }
196
200
 
@@ -201,19 +205,19 @@ export function parseKeywordSpot(text, nlp = null) {
201
205
  let subject = beforeText;
202
206
  let object = afterText;
203
207
  if (INHERITS_REVERSE_VERBS.includes(verbPhrase)) [subject, object] = [object, subject];
204
- return { shape: "ask", entityType: null, modifier: "direct", kind, subject, object };
208
+ return stamp({ shape: "ask", entityType: null, modifier: "direct", kind, subject, object });
205
209
  }
206
- if (afterText) return { shape: "reverse", entityType, modifier, kind, object: afterText };
210
+ if (afterText) return stamp({ shape: "reverse", entityType, modifier, kind, object: afterText });
207
211
  // "what is a kind of class": when the object is itself an entity-type noun, the
208
212
  // entity match swallows the whole post-verb span as a grain qualifier, leaving
209
213
  // afterText empty — re-read that span as the object instead.
210
214
  if (kind === "inherits" && !beforeText && entityHit && entityHit.start === verbHit.end) {
211
215
  const entityText = canonWords.slice(entityHit.start, entityHit.end).join(" ");
212
- if (entityText) return { shape: "reverse", entityType: null, modifier, kind, object: entityText };
216
+ if (entityText) return stamp({ shape: "reverse", entityType: null, modifier, kind, object: entityText });
213
217
  }
214
218
  // forward keeps the spotted entityType (traverse()'s commit-as-subject grain
215
219
  // selection); modifier stays hardcoded since no forward closure traversal exists.
216
- if (beforeText) return { shape: "forward", entityType, modifier: "direct", kind, object: beforeText };
220
+ if (beforeText) return stamp({ shape: "forward", entityType, modifier: "direct", kind, object: beforeText });
217
221
  return null;
218
222
  }
219
223