* Pick the most relevant knowledge notes for the given query, fitting under * the token budget. Returns an array of { name, content, relPath }.
(query, opts = {})
| 193 | * the token budget. Returns an array of { name, content, relPath }. |
| 194 | */ |
| 195 | selectForQuery(query, opts = {}) { |
| 196 | if (this.disabled) return []; |
| 197 | const index = this._buildIndex(); |
| 198 | if (index.length === 0) return []; |
| 199 | const queryTokens = this._tokenize(query); |
| 200 | if (queryTokens.length === 0) return []; |
| 201 | |
| 202 | const scored = index |
| 203 | .map(e => ({ e, score: this._scoreEntry(e, queryTokens) })) |
| 204 | .filter(s => s.score > 0) |
| 205 | .sort((a, b) => b.score - a.score); |
| 206 | |
| 207 | const maxChars = (opts.maxTokens || this.maxTokens) * 4; |
| 208 | const out = []; |
| 209 | let used = 0; |
| 210 | for (const { e, score } of scored) { |
| 211 | const body = e._bodyCache !== undefined ? e._bodyCache : (() => { |
| 212 | try { return fs.readFileSync(e.path, 'utf-8'); } catch { return ''; } |
| 213 | })(); |
| 214 | // Skip front-matter if present |
| 215 | const cleaned = body.replace(/^---\n[\s\S]*?\n---\n/, '').trim(); |
| 216 | if (!cleaned) continue; |
| 217 | // Per-entry hard cap to prevent one note swallowing the whole budget |
| 218 | const truncated = cleaned.length > 1500 |
| 219 | ? cleaned.slice(0, 1500) + '\n[...truncated]' |
| 220 | : cleaned; |
| 221 | if (used + truncated.length > maxChars) { |
| 222 | if (out.length === 0) { |
| 223 | // Always include at least the top hit, but truncated to fit |
| 224 | const fit = truncated.slice(0, Math.max(0, maxChars - 100)); |
| 225 | out.push({ name: e.name, content: fit, relPath: e.relPath, score }); |
| 226 | } |
| 227 | break; |
| 228 | } |
| 229 | out.push({ name: e.name, content: truncated, relPath: e.relPath, score }); |
| 230 | used += truncated.length; |
| 231 | } |
| 232 | return out; |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * Format selected entries as a system-prompt block. Returns '' if nothing |
no test coverage detected