@polycode-projects/seonix 0.10.11 → 0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/seonix",
3
- "version": "0.10.11",
3
+ "version": "0.10.14",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "SEONIX — a deterministic, offline, $0 code-graph tool that makes a cheap coding agent edit like an expensive one. Indexes a repo with Python ast + git (zero model calls) and renders a bounded edit-digest.",
@@ -50,6 +50,7 @@
50
50
  "setup": "node scripts/setup.mjs",
51
51
  "bench": "node bench/run.mjs",
52
52
  "bench:quick": "node bench/run.mjs --instances 1 --runs 1",
53
+ "bench:telemetry": "node bench/run.mjs --suite django-lh --arms seonix-ask --instances 1 --runs 1 --telemetry",
53
54
  "join:telemetry": "node scripts/join-telemetry.mjs",
54
55
  "bench:lite": "node bench/run.mjs --suite lite",
55
56
  "bench:seonix": "node bench/run.mjs --suite django-lh --arms seonix,seonix-min --runs 5",
@@ -88,7 +89,7 @@
88
89
  },
89
90
  "dependencies": {
90
91
  "@modelcontextprotocol/sdk": "^1.29.0",
91
- "@polycode-projects/the-mechanical-code-talker": "^0.9.11",
92
+ "@polycode-projects/the-mechanical-code-talker": "^1.0.4",
92
93
  "cytoscape": "^3.30.0",
93
94
  "smol-toml": "^1.7.0",
94
95
  "tree-sitter": "^0.21.1",
@@ -590,6 +590,10 @@
590
590
  "touchs": "touches",
591
591
  "tuoches": "touches",
592
592
  "touhced": "touched",
593
+ // WHERE_MARKERS typo (0.9.13 Tier-1 playtest): "defined" itself had no typo
594
+ // entry, so "where is it defned" fell through to the bare-object search path
595
+ // instead of the where-shape ("no module matching 'it defned' found").
596
+ "defned": "defined",
593
597
  "chagned": "changed",
594
598
  "chnaged": "changed",
595
599
  "chagnes": "changes",
@@ -602,6 +606,17 @@
602
606
  "whcih": "which",
603
607
  "wich": "which",
604
608
  "whihc": "which",
609
+ // "wehre"/"whre" (0.9.13 Tier-1 playtest, "where is it defined" drill-down):
610
+ // the WHERE-DEFINED shape's own anchor word had NO typo tolerance at all
611
+ // (unlike which/what/does/the above), so a plain dropped/transposed letter
612
+ // fell straight through resolveObject and hit either the grammar wall or a
613
+ // bogus "no module matching 'it defined'" search. "were" (the missing-h
614
+ // homophone slip) is NOT curated here — it's a real word already load-bearing
615
+ // as the TEMPORAL_AUX auxiliary ("when were the modules last touched"), so
616
+ // that one typo is handled by its own anchored phrasing frame instead
617
+ // (normalize.mjs PHRASING_FRAMES) to avoid clobbering the legitimate reading.
618
+ "wehre": "where",
619
+ "whre": "where",
605
620
  // "wat" (chatbench cycle 2, tf-wat-calls): the internet-casual spelling of
606
621
  // "what" — neither curated noise nor a restorable trigger typo, so "wat calls
607
622
  // fnAlpha" used to die as "couldn't resolve one of the terms". Restored here
@@ -685,7 +700,8 @@
685
700
  "kinda",
686
701
  "sorta",
687
702
  "btw",
688
- "by the way"
703
+ "by the way",
704
+ "you"
689
705
  ]);
690
706
  var CONTEXT_PRONOUNS = Object.freeze(["this", "it", "that", "here", "this one", "that one"]);
691
707
  var NEGATION_FRAMES = Object.freeze([
@@ -992,9 +1008,10 @@
992
1008
  const words = rest.replace(/\?+\s*$/, "").trim().split(/\s+/);
993
1009
  return LISTING_TAIL_KINDS.has((words[words.length - 1] || "").toLowerCase());
994
1010
  };
995
- var GREETING_PREAMBLE_RE = /^(?:hi|hiya|hello|hey|yo|howdy)(?:\s+there)?\s*[,—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
1011
+ var GREETING_PREAMBLE_RE = /^(?:hi|hiya|hello|hey|yo|howdy|g'?day|good\s+(?:morning|afternoon|evening|day)|greetings|salutations)(?:\s+there)?\s*[,.—–-]\s*(?:(?:just\s+a\s+)?quick\s+question\s*[,:—–-]?\s*)?(.+)$/i;
996
1012
  var 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;
997
1013
  var MODAL_WRAPPER_RE = /^(?:can|could|would|will)\s+you\s+(?:please\s+)?(.+?)(?:[,\s]+please)?\??$/i;
1014
+ var EXPLAIN_WRAPPER_RE = /^explain\s+(?:to\s+me\s+|please\s+)*(.+?)\??$/i;
998
1015
  var SHOW_GIVE_ME_RE = /^(?:show|give)\s+me\s+(?:the\s+)?(.+?)\??$/i;
999
1016
  function applyPreambleFrames(text) {
1000
1017
  let q = String(text || "");
@@ -1006,6 +1023,8 @@
1006
1023
  if (m) q = m[1].trim();
1007
1024
  m = q.match(MODAL_WRAPPER_RE);
1008
1025
  if (m) q = m[1].trim();
1026
+ m = q.match(EXPLAIN_WRAPPER_RE);
1027
+ if (m && INTERROGATIVE_LEAD_RE.test(m[1].trim())) q = m[1].trim();
1009
1028
  m = q.match(SHOW_GIVE_ME_RE);
1010
1029
  if (m) {
1011
1030
  const rest = m[1].trim();
@@ -1027,6 +1046,18 @@
1027
1046
  }
1028
1047
  return q;
1029
1048
  }
1049
+ var SELF_CORRECTION_RE = /^.+?(?:\s*(?:--|—|-)\s*)?\b(?:sorry|i\s+mean)\b\s*(?:--|—|-|,|:)\s*(.+)$/i;
1050
+ function applySelfCorrectionFrames(text) {
1051
+ let q = String(text || "");
1052
+ for (let pass = 0; pass < 3; pass++) {
1053
+ const m = q.match(SELF_CORRECTION_RE);
1054
+ if (!m) break;
1055
+ const next = m[1].trim();
1056
+ if (!next || next === q) break;
1057
+ q = next;
1058
+ }
1059
+ return q;
1060
+ }
1030
1061
  var CONDITIONAL_VERB_GERUND = Object.freeze({
1031
1062
  imports: "importing",
1032
1063
  calls: "calling",
@@ -1073,6 +1104,7 @@
1073
1104
  q = q.replace(WRONG_WORD_RE, (m) => WRONG_WORDS[m.toLowerCase()]);
1074
1105
  q = q.replace(G_DROP, "$1ing");
1075
1106
  q = applyPreambleFrames(q);
1107
+ q = applySelfCorrectionFrames(q);
1076
1108
  q = applySubordinationFrames(q);
1077
1109
  q = applyConditionalFrames(q);
1078
1110
  if (FILLER_WORDS.length) {
@@ -1111,6 +1143,15 @@
1111
1143
  { re: /^what\s+(?:defined|declared)\s+(?:the\s+)?(?:function\s+|method\s+|class\s+|module\s+|variable\s+|constant\s+)?(.+?)\??$/i, to: (m) => `where is ${m[1]} defined` },
1112
1144
  // "where's X defined" (the "where's" contraction is not in the contraction table)
1113
1145
  { re: /^where'?s\s+(?:the\s+)?(.+?)\s+(defined|declared|located|implemented)\??$/i, to: (m) => `where is ${m[1]} ${m[2]}` },
1146
+ // "were is X defined" (0.9.13 Tier-1 playtest: the missing-h typo of "where").
1147
+ // NOT curated as a blanket MISSPELLINGS entry — "were" is a real word already
1148
+ // load-bearing as the TEMPORAL_AUX auxiliary ("when were the modules last
1149
+ // touched"), so a global word-boundary rewrite would clobber that reading.
1150
+ // This frame is anchored to the WHERE-DEFINED shape specifically ("were is
1151
+ // … defined/declared/located/implemented"), a construction no legitimate
1152
+ // temporal query produces ("were" as an auxiliary never leads directly into
1153
+ // a bare "is").
1154
+ { re: /^were\s+is\s+(?:the\s+)?(.+?)\s+(defined|declared|located|implemented)\??$/i, to: (m) => `where is ${m[1]} ${m[2]}` },
1114
1155
  // PREDICATIVE QUALIFIER → the ATTRIBUTIVE form the grammar already answers. The
1115
1156
  // adjective-qualifier post-filters (ask-vocab.mjs QUALIFIERS: tested/untested,
1116
1157
  // public/private, exported, static/abstract/constant, …) parse in the ATTRIBUTIVE
@@ -1304,11 +1345,13 @@
1304
1345
  return /^[a-z]+$/.test(w) && !STOPWORDS2.has(w) && !VOCAB_WORDS.has(w);
1305
1346
  }
1306
1347
  function fuzzyVocabWord(w) {
1307
- const bound = fuzzyBound(w);
1348
+ return fuzzyMatchInSet(w, FUZZY_TARGET_WORDS, fuzzyBound(w));
1349
+ }
1350
+ function fuzzyMatchInSet(w, candidates, bound = fuzzyBound(w)) {
1308
1351
  let best = bound + 1;
1309
1352
  let hit2 = null;
1310
1353
  let tied = false;
1311
- for (const target of FUZZY_TARGET_WORDS) {
1354
+ for (const target of candidates) {
1312
1355
  const d = editDistance(w, target, Math.min(best, bound));
1313
1356
  if (d < best) {
1314
1357
  best = d;
@@ -2281,7 +2324,7 @@
2281
2324
  }
2282
2325
  var MAX_COMPOSE_DEPTH = 4;
2283
2326
  var NEST_SENTINEL = "zzinnerset";
2284
- var PRED_LEAD_SKIP = /* @__PURE__ */ new Set(["that", "which", "who", "are", "is", "was", "were", "do", "does", "also", "still", "both", "and"]);
2327
+ var PRED_LEAD_SKIP = /* @__PURE__ */ new Set(["that", "which", "who", "are", "is", "was", "were", "do", "does", "also", "still", "both", "and", "then", "though"]);
2285
2328
  var FRAME_WORDS = /* @__PURE__ */ new Set(["which", "what", "who", "list", "show", "find", "give", "me", "us", "all"]);
2286
2329
  var entityNoun = (w) => ENTITY_TO_TYPE[w] ? { entityType: ENTITY_TO_TYPE[w], placeholder: false } : PLACEHOLDER_NOUNS.includes(w) ? { entityType: null, placeholder: true } : null;
2287
2330
  var isGerundVerb = (w) => !!VERB_TO_KIND[w] && w.endsWith("ing");
@@ -2291,7 +2334,7 @@
2291
2334
  function parseComposite(text, nlp) {
2292
2335
  const w = splitWords(text);
2293
2336
  const lc = w.map((x) => x.toLowerCase());
2294
- return parseNegation(text, nlp, 0) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || 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) || parseRelationalOrQualified(w, lc, nlp, 0);
2337
+ return parseExistence(w, lc) || parseNegation(text, nlp, 0) || parseForwardNegation(w, lc, nlp) || parseTemporal(w, lc, nlp, 0) || 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) || parseRelationalOrQualified(w, lc, nlp, 0);
2295
2338
  }
2296
2339
  function complementAst(entityType, diffAtom) {
2297
2340
  return {
@@ -2441,6 +2484,9 @@
2441
2484
  return { node: "temporal", inner, entityType: noun && noun.entityType || null };
2442
2485
  }
2443
2486
  function parseAnaphora(w, lc, nlp) {
2487
+ if (lc.length === 2 && lc[0] === "which" && (lc[1] === "ones" || lc[1] === "one")) {
2488
+ return { node: "anaphora", mode: "list", filter: { type: "all" } };
2489
+ }
2444
2490
  let p = -1;
2445
2491
  let viaOf = false;
2446
2492
  for (let i = 1; i < lc.length; i += 1) {
@@ -2477,6 +2523,46 @@
2477
2523
  }
2478
2524
  return void 0;
2479
2525
  }
2526
+ function parseExistence(w, lc) {
2527
+ let i;
2528
+ if (lc[0] === "is" && lc[1] === "there") i = 2;
2529
+ else if (lc[0] === "are" && lc[1] === "there") i = 2;
2530
+ else return null;
2531
+ const article = lc[i];
2532
+ if (article === "a" || article === "an" || article === "any") i += 1;
2533
+ else return null;
2534
+ const noun = i < lc.length ? entityNoun(lc[i]) : null;
2535
+ if (!noun || noun.placeholder || !noun.entityType) return null;
2536
+ const entityType = noun.entityType;
2537
+ i += 1;
2538
+ let rest = lc.slice(i);
2539
+ let restW = w.slice(i);
2540
+ if (rest.length && rest[rest.length - 1] === "anywhere") {
2541
+ rest = rest.slice(0, -1);
2542
+ restW = restW.slice(0, -1);
2543
+ } else if (rest.length >= 2 && rest[rest.length - 2] === "at" && rest[rest.length - 1] === "all") {
2544
+ rest = rest.slice(0, -2);
2545
+ restW = restW.slice(0, -2);
2546
+ }
2547
+ if (!rest.length) return { node: "exists", entityType, term: null, scopeModule: null };
2548
+ if (rest[0] === "called" || rest[0] === "named") {
2549
+ if (rest.length < 2) return { node: "miss", reason: `"${rest[0]}" needs a name afterward` };
2550
+ const inIdx = rest.indexOf("in", 1);
2551
+ if (inIdx > 0) {
2552
+ const term2 = restW.slice(1, inIdx).join(" ").trim();
2553
+ const scopeModule = restW.slice(inIdx + 1).join(" ").trim();
2554
+ if (!term2 || !scopeModule) return { node: "miss", reason: `a named existence check needs both a name and a module after "in"` };
2555
+ return { node: "exists", entityType, term: term2, scopeModule };
2556
+ }
2557
+ const term = restW.slice(1).join(" ").trim();
2558
+ return term ? { node: "exists", entityType, term, scopeModule: null } : { node: "miss", reason: `"${rest[0]}" needs a name afterward` };
2559
+ }
2560
+ if (rest[0] === "in") {
2561
+ const scopeModule = restW.slice(1).join(" ").trim();
2562
+ return scopeModule ? { node: "exists", entityType, term: null, scopeModule } : { node: "miss", reason: `"in" needs a module afterward` };
2563
+ }
2564
+ return null;
2565
+ }
2480
2566
  var AGG_TAIL_FILLER = /* @__PURE__ */ new Set([
2481
2567
  "total",
2482
2568
  "altogether",
@@ -2492,7 +2578,9 @@
2492
2578
  "index",
2493
2579
  "codebase",
2494
2580
  "repo",
2495
- "repository"
2581
+ "repository",
2582
+ "this",
2583
+ "that"
2496
2584
  ]);
2497
2585
  function parseAggregate(w, lc, nlp) {
2498
2586
  const trig = AGGREGATE_TRIGGERS.find((t) => lc.slice(0, t.split(" ").length).join(" ") === t);
@@ -2524,6 +2612,7 @@
2524
2612
  var LIST_SKIP = /* @__PURE__ */ new Set(["the", "a", "an", "all", "me", "us"]);
2525
2613
  var LIST_TRIGGERS_SORTED = [...LIST_TRIGGERS].sort((a, b) => b.split(" ").length - a.split(" ").length);
2526
2614
  var LISTABLE_KINDS = "functions, classes, methods, modules, attributes, variables, or commits";
2615
+ var SCOPE_PREPOSITIONS = /* @__PURE__ */ new Set(["in", "inside", "under"]);
2527
2616
  function parseList(w, lc, nlp, depth) {
2528
2617
  let i = 0;
2529
2618
  let interrogative = false;
@@ -2560,11 +2649,19 @@
2560
2649
  i += 1;
2561
2650
  const tail = w.slice(i);
2562
2651
  const tailMeaningful = lc.slice(i).some((t) => !STOPWORDS2.has(t) && !AGG_TAIL_FILLER.has(t));
2563
- if (interrogative && (tailMeaningful || tail.length === 0)) return null;
2652
+ let scopeTailLc = lc.slice(i);
2653
+ let scopeTailWords = tail;
2654
+ if (scopeTailLc[0] === "is" || scopeTailLc[0] === "are") {
2655
+ scopeTailLc = scopeTailLc.slice(1);
2656
+ scopeTailWords = scopeTailWords.slice(1);
2657
+ }
2658
+ const scopedException = interrogative && SCOPE_PREPOSITIONS.has(scopeTailLc[0]);
2659
+ if (interrogative && (tailMeaningful && !scopedException || tail.length === 0)) return null;
2564
2660
  let base;
2565
2661
  let scoped = false;
2566
2662
  if (tailMeaningful) {
2567
- const setAst = parseSetPhrase(`which ${[...quals, entWord, ...tail].join(" ")}`, nlp, (depth || 0) + 1);
2663
+ const useTail = scopedException ? scopeTailWords : tail;
2664
+ const setAst = parseSetPhrase(`which ${[...quals, entWord, ...useTail].join(" ")}`, nlp, (depth || 0) + 1);
2568
2665
  if (!setAst || setAst.node === "miss") return { node: "miss", reason: setAst && setAst.reason || "the list filter didn't parse" };
2569
2666
  base = setAst;
2570
2667
  scoped = true;
@@ -2709,7 +2806,7 @@
2709
2806
  const noun = i < lc.length ? entityNoun(lc[i]) : null;
2710
2807
  if (!noun) {
2711
2808
  const nextNoun = i + 1 < lc.length ? entityNoun(lc[i + 1]) : null;
2712
- if ((framed || quals.length) && nextNoun && /^[a-z]+$/.test(lc[i]) && !VERB_TO_KIND[lc[i]] && !STOPWORDS2.has(lc[i])) {
2809
+ if ((framed || quals.length) && nextNoun && /^[a-z]+$/.test(lc[i]) && !VERB_TO_KIND[lc[i]] && !STOPWORDS2.has(lc[i]) && !CASCADE_NOISE_SET.has(lc[i])) {
2713
2810
  return { node: "find", entityType: nextNoun.entityType, term: w[i] };
2714
2811
  }
2715
2812
  return null;
@@ -3139,8 +3236,33 @@
3139
3236
  const winners = scored.filter((s) => s.score === best).map((s) => s.ind);
3140
3237
  return { compositeKind: "superlative", entityType: ast.entityType, metricNoun: ast.metricNoun, extreme: ast.extreme, score: best, matches: winners };
3141
3238
  }
3239
+ function evalExists(graph, ast) {
3240
+ const { entityType, term, scopeModule } = ast;
3241
+ let scopeMatch = null;
3242
+ if (scopeModule) {
3243
+ const r = resolveObject(graph, scopeModule, { expectedClass: "Module" });
3244
+ if (!r.match) return { compositeKind: "exists", entityType, term, scopeModule, scopeMiss: true, matches: [] };
3245
+ scopeMatch = r.match;
3246
+ }
3247
+ if (term) {
3248
+ const r = resolveObject(graph, term, { expectedClass: entityType });
3249
+ const inScope = !scopeMatch || r.match && moduleIdOf2(graph, r.match) === scopeMatch.id;
3250
+ const hit2 = r.match && inScope;
3251
+ return {
3252
+ compositeKind: "exists",
3253
+ entityType,
3254
+ term,
3255
+ scopeModule,
3256
+ scopeMatch,
3257
+ matches: hit2 ? [r.match] : []
3258
+ };
3259
+ }
3260
+ const pool = scopeMatch ? refineToEntities(graph, /* @__PURE__ */ new Set([scopeMatch.id]), entityType) : graph.individuals.filter((i) => i.class === entityType);
3261
+ return { compositeKind: "exists", entityType, term: null, scopeModule, scopeMatch, matches: pool };
3262
+ }
3142
3263
  function evalComposite(graph, ast, opts = {}) {
3143
3264
  if (ast.node === "miss") return { compositeMiss: true, reason: ast.reason || null, matches: [] };
3265
+ if (ast.node === "exists") return evalExists(graph, ast);
3144
3266
  if (ast.node === "count") return { compositeKind: "count", count: evalSet(graph, ast.base, opts).length, entityType: ast.entityType, matches: [] };
3145
3267
  if (ast.node === "list") return { compositeKind: "list", matches: evalSet(graph, ast.base, opts), entityType: ast.entityType, scoped: ast.scoped };
3146
3268
  if (ast.node === "superlative") return evalSuperlative(graph, ast);
@@ -3175,6 +3297,27 @@
3175
3297
  }
3176
3298
  return { content: `couldn't compile this compositional question${result.reason ? ` (${result.reason})` : ""}. ${compositionalHint()}.`, miss: true, ambiguous: false };
3177
3299
  }
3300
+ if (result.compositeKind === "exists") {
3301
+ if (result.scopeMiss) {
3302
+ return { content: `no module matching "${result.scopeModule}" found in the index.`, miss: true, ambiguous: false };
3303
+ }
3304
+ const kindSingular = nounFor(result.entityType, 1);
3305
+ const kindPlural = nounFor(result.entityType, 2);
3306
+ const scopeSuffix = result.scopeMatch ? ` in ${result.scopeMatch.label}` : "";
3307
+ if (result.term) {
3308
+ if (!result.matches.length) {
3309
+ return { content: `No \u2014 no ${kindSingular} named "${result.term}" found${scopeSuffix}.`, miss: true, ambiguous: false };
3310
+ }
3311
+ const hit2 = result.matches[0];
3312
+ const modLabel = moduleLabelOf(hit2);
3313
+ const definedIn = hit2.class === "Module" ? "" : modLabel && modLabel !== "(unknown module)" ? `, defined in ${modLabel}` : "";
3314
+ return { content: `Yes \u2014 ${hit2.label} is a ${kindSingular}${definedIn}.`, miss: false, ambiguous: false, matches: result.matches };
3315
+ }
3316
+ if (!result.matches.length) {
3317
+ return { content: `No \u2014 no ${kindPlural} found${scopeSuffix}.`, miss: true, ambiguous: false };
3318
+ }
3319
+ return { content: `Yes \u2014 ${compositeList(result.matches)}${scopeSuffix}.`, miss: false, ambiguous: false, matches: result.matches };
3320
+ }
3178
3321
  if (result.compositeKind === "count") {
3179
3322
  const noun = result.entityType ? nounFor(result.entityType, result.count) : result.count === 1 ? "result" : "results";
3180
3323
  return { content: `${result.count} ${noun}.`, miss: false, ambiguous: false, matches: [] };
@@ -3771,7 +3914,8 @@
3771
3914
  }
3772
3915
  if (!result.objMatch && (!result.candidates || result.candidates.length === 0) && parsed.shape !== "ask") {
3773
3916
  const objText = String(parsed.object || "").trim();
3774
- const what = /^(?:commit[:\s])?[0-9a-f]{7,40}$/i.test(objText) ? "commit" : !objText.includes("/") && /^[\w$]+(\.[\w$]+)+$/.test(objText) ? "symbol" : "module";
3917
+ const fallback = parsed.entityType && PLURAL_FORMS[parsed.entityType] ? nounFor(parsed.entityType, 1) : "module";
3918
+ const what = /^(?:commit[:\s])?[0-9a-f]{7,40}$/i.test(objText) ? "commit" : !objText.includes("/") && /^[\w$]+(\.[\w$]+)+$/.test(objText) ? "symbol" : fallback;
3775
3919
  return {
3776
3920
  content: `no ${what} matching "${parsed.object}" found in the index.`,
3777
3921
  miss: true,
@@ -1 +1 @@
1
- 0.9.11
1
+ 1.0.4