notex-companion 0.4.0 → 0.4.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.
package/dist/cli.js CHANGED
@@ -6727,11 +6727,13 @@ function shatter(text) {
6727
6727
  function terms(question) {
6728
6728
  return [...new Set(shatter(question).filter((t) => t.length >= 3 && !STOPWORDS.has(t)))];
6729
6729
  }
6730
+ var FUZZY_MATCH_EXACT_MIN_COUNT = 2;
6730
6731
  function scoreNodes(index, queryTerms) {
6731
6732
  const scored = [];
6732
6733
  for (const entry of index) {
6733
6734
  let score = 0;
6734
6735
  let exact = false;
6736
+ let fuzzyMatchesByToken;
6735
6737
  for (const term of queryTerms) {
6736
6738
  let best = 0;
6737
6739
  if (entry.labelLower === term) {
@@ -6744,6 +6746,16 @@ function scoreNodes(index, queryTerms) {
6744
6746
  for (const tok of entry.labelTokens) {
6745
6747
  if (tok.length >= 4 && (tok.startsWith(term) || term.startsWith(tok))) {
6746
6748
  best = Math.max(best, 1.5);
6749
+ if (term.length >= 4) {
6750
+ if (!fuzzyMatchesByToken)
6751
+ fuzzyMatchesByToken = new Map;
6752
+ let matchedTerms = fuzzyMatchesByToken.get(tok);
6753
+ if (!matchedTerms) {
6754
+ matchedTerms = new Set;
6755
+ fuzzyMatchesByToken.set(tok, matchedTerms);
6756
+ }
6757
+ matchedTerms.add(term);
6758
+ }
6747
6759
  } else if (term.length >= 4 && tok.includes(term)) {
6748
6760
  best = Math.max(best, 1);
6749
6761
  }
@@ -6761,6 +6773,14 @@ function scoreNodes(index, queryTerms) {
6761
6773
  }
6762
6774
  score += best;
6763
6775
  }
6776
+ if (fuzzyMatchesByToken) {
6777
+ for (const matchedTerms of fuzzyMatchesByToken.values()) {
6778
+ if (matchedTerms.size >= FUZZY_MATCH_EXACT_MIN_COUNT) {
6779
+ exact = true;
6780
+ break;
6781
+ }
6782
+ }
6783
+ }
6764
6784
  if (score > 0)
6765
6785
  scored.push({ id: entry.id, score: Math.round(score * 100) / 100, exact });
6766
6786
  }
package/dist/client.js CHANGED
@@ -98,11 +98,13 @@ function shatter(text) {
98
98
  function terms(question) {
99
99
  return [...new Set(shatter(question).filter((t) => t.length >= 3 && !STOPWORDS.has(t)))];
100
100
  }
101
+ var FUZZY_MATCH_EXACT_MIN_COUNT = 2;
101
102
  function scoreNodes(index, queryTerms) {
102
103
  const scored = [];
103
104
  for (const entry of index) {
104
105
  let score = 0;
105
106
  let exact = false;
107
+ let fuzzyMatchesByToken;
106
108
  for (const term of queryTerms) {
107
109
  let best = 0;
108
110
  if (entry.labelLower === term) {
@@ -115,6 +117,16 @@ function scoreNodes(index, queryTerms) {
115
117
  for (const tok of entry.labelTokens) {
116
118
  if (tok.length >= 4 && (tok.startsWith(term) || term.startsWith(tok))) {
117
119
  best = Math.max(best, 1.5);
120
+ if (term.length >= 4) {
121
+ if (!fuzzyMatchesByToken)
122
+ fuzzyMatchesByToken = new Map;
123
+ let matchedTerms = fuzzyMatchesByToken.get(tok);
124
+ if (!matchedTerms) {
125
+ matchedTerms = new Set;
126
+ fuzzyMatchesByToken.set(tok, matchedTerms);
127
+ }
128
+ matchedTerms.add(term);
129
+ }
118
130
  } else if (term.length >= 4 && tok.includes(term)) {
119
131
  best = Math.max(best, 1);
120
132
  }
@@ -132,6 +144,14 @@ function scoreNodes(index, queryTerms) {
132
144
  }
133
145
  score += best;
134
146
  }
147
+ if (fuzzyMatchesByToken) {
148
+ for (const matchedTerms of fuzzyMatchesByToken.values()) {
149
+ if (matchedTerms.size >= FUZZY_MATCH_EXACT_MIN_COUNT) {
150
+ exact = true;
151
+ break;
152
+ }
153
+ }
154
+ }
135
155
  if (score > 0)
136
156
  scored.push({ id: entry.id, score: Math.round(score * 100) / 100, exact });
137
157
  }
package/dist/index.js CHANGED
@@ -98,11 +98,13 @@ function shatter(text) {
98
98
  function terms(question) {
99
99
  return [...new Set(shatter(question).filter((t) => t.length >= 3 && !STOPWORDS.has(t)))];
100
100
  }
101
+ var FUZZY_MATCH_EXACT_MIN_COUNT = 2;
101
102
  function scoreNodes(index, queryTerms) {
102
103
  const scored = [];
103
104
  for (const entry of index) {
104
105
  let score = 0;
105
106
  let exact = false;
107
+ let fuzzyMatchesByToken;
106
108
  for (const term of queryTerms) {
107
109
  let best = 0;
108
110
  if (entry.labelLower === term) {
@@ -115,6 +117,16 @@ function scoreNodes(index, queryTerms) {
115
117
  for (const tok of entry.labelTokens) {
116
118
  if (tok.length >= 4 && (tok.startsWith(term) || term.startsWith(tok))) {
117
119
  best = Math.max(best, 1.5);
120
+ if (term.length >= 4) {
121
+ if (!fuzzyMatchesByToken)
122
+ fuzzyMatchesByToken = new Map;
123
+ let matchedTerms = fuzzyMatchesByToken.get(tok);
124
+ if (!matchedTerms) {
125
+ matchedTerms = new Set;
126
+ fuzzyMatchesByToken.set(tok, matchedTerms);
127
+ }
128
+ matchedTerms.add(term);
129
+ }
118
130
  } else if (term.length >= 4 && tok.includes(term)) {
119
131
  best = Math.max(best, 1);
120
132
  }
@@ -132,6 +144,14 @@ function scoreNodes(index, queryTerms) {
132
144
  }
133
145
  score += best;
134
146
  }
147
+ if (fuzzyMatchesByToken) {
148
+ for (const matchedTerms of fuzzyMatchesByToken.values()) {
149
+ if (matchedTerms.size >= FUZZY_MATCH_EXACT_MIN_COUNT) {
150
+ exact = true;
151
+ break;
152
+ }
153
+ }
154
+ }
135
155
  if (score > 0)
136
156
  scored.push({ id: entry.id, score: Math.round(score * 100) / 100, exact });
137
157
  }
package/dist/ops.d.ts CHANGED
@@ -73,7 +73,7 @@ export type QueryResult = {
73
73
  };
74
74
  /** See companion-api.md §4.8 / notex-mcp-server.md §5 — same buildFooter as the write paths. */
75
75
  footer?: string;
76
- /** Set when no seed cleared the seed-score floor — an exact label or label-token match. */
76
+ /** Set when no seed cleared the seed-score floor — see ScoredNode's `exact` (scoring.ts). */
77
77
  lowConfidence?: {
78
78
  topScore: number;
79
79
  };
package/dist/scoring.d.ts CHANGED
@@ -10,7 +10,8 @@ export type ScoreIndexEntry = {
10
10
  export type ScoredNode = {
11
11
  id: string;
12
12
  score: number;
13
- /** Cleared the seed-score floor: at least one term matched a whole label or a whole label token. */
13
+ /** Cleared the seed-score floor: at least one term matched a whole label or a whole label
14
+ * token, or (TBR-130) at least two independent terms each fuzzy-prefix the same label token. */
14
15
  exact: boolean;
15
16
  };
16
17
  /** label tokens weigh full; path tokens weigh less — a path match is weaker evidence. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notex-companion",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Local retrieval companion for Notex — reads a checkout's graphify-out/graph.json and serves deterministic search/query/path/node lookups over loopback HTTP and MCP stdio. No LLM, no graph building, no network beyond 127.0.0.1.",
5
5
  "keywords": ["notex", "graphify", "mcp", "code-graph"],
6
6
  "license": "MIT",