brainbank 0.1.3-beta.1 → 0.1.4

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.
Files changed (47) hide show
  1. package/dist/{chunk-ZEUCCE6P.js → chunk-3UIWA32X.js} +32 -91
  2. package/dist/chunk-3UIWA32X.js.map +1 -0
  3. package/dist/{chunk-JMHBINOJ.js → chunk-DAGVUEXL.js} +4 -2
  4. package/dist/chunk-DAGVUEXL.js.map +1 -0
  5. package/dist/chunk-NNDY7P2R.js +211 -0
  6. package/dist/chunk-NNDY7P2R.js.map +1 -0
  7. package/dist/cli.js +111 -73
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{haiku-pruner-MT4JFKUC.js → haiku-pruner-5KVT5AI2.js} +2 -2
  10. package/dist/{http-server-RQHCN3QG.js → http-server-2ZQ6I43B.js} +2 -2
  11. package/dist/index.d.ts +25 -57
  12. package/dist/index.js +3 -7
  13. package/dist/index.js.map +1 -1
  14. package/dist/mcp.js +58 -6
  15. package/dist/mcp.js.map +1 -1
  16. package/dist/{stats-tui-SEHCVG5K.js → stats-tui-AD3AMYGV.js} +2 -2
  17. package/package.json +1 -1
  18. package/src/cli/commands/context.ts +8 -130
  19. package/src/cli/commands/help.ts +8 -4
  20. package/src/cli/commands/index.ts +4 -8
  21. package/src/cli/commands/query.ts +167 -0
  22. package/src/cli/factory/plugin-loader.ts +12 -13
  23. package/src/cli/index.ts +3 -0
  24. package/src/cli/server-client.ts +4 -0
  25. package/src/config.ts +0 -1
  26. package/src/engine/search-api.ts +1 -1
  27. package/src/index.ts +0 -4
  28. package/src/lib/languages.ts +0 -1
  29. package/src/lib/prune.ts +2 -1
  30. package/src/mcp/mcp-server.ts +77 -4
  31. package/src/providers/pruners/haiku-expander.ts +6 -1
  32. package/src/providers/pruners/haiku-pruner.ts +166 -25
  33. package/src/search/bm25-boost.ts +8 -1
  34. package/src/search/context-builder.ts +21 -90
  35. package/src/services/http-server.ts +4 -0
  36. package/src/types.ts +16 -2
  37. package/dist/chunk-3QVAKPTK.js +0 -102
  38. package/dist/chunk-3QVAKPTK.js.map +0 -1
  39. package/dist/chunk-5QUZ6CYK.js +0 -134
  40. package/dist/chunk-5QUZ6CYK.js.map +0 -1
  41. package/dist/chunk-JMHBINOJ.js.map +0 -1
  42. package/dist/chunk-ZEUCCE6P.js.map +0 -1
  43. package/dist/haiku-expander-CGEHFJIA.js +0 -8
  44. package/dist/http-server-RQHCN3QG.js.map +0 -1
  45. /package/dist/{haiku-expander-CGEHFJIA.js.map → haiku-pruner-5KVT5AI2.js.map} +0 -0
  46. /package/dist/{haiku-pruner-MT4JFKUC.js.map → http-server-2ZQ6I43B.js.map} +0 -0
  47. /package/dist/{stats-tui-SEHCVG5K.js.map → stats-tui-AD3AMYGV.js.map} +0 -0
@@ -7,7 +7,6 @@ import {
7
7
  isContextFieldPlugin,
8
8
  isContextFormatterPlugin,
9
9
  isDocsPlugin,
10
- isExpandablePlugin,
11
10
  isFileResolvable,
12
11
  isIndexable,
13
12
  isReembeddable,
@@ -51,7 +50,6 @@ function resolveConfig(partial = {}) {
51
50
  maxElements: partial.maxElements ?? DEFAULTS.maxElements,
52
51
  embeddingProvider: partial.embeddingProvider,
53
52
  pruner: partial.pruner,
54
- expander: partial.expander,
55
53
  webhookPort: partial.webhookPort,
56
54
  contextFields: partial.contextFields
57
55
  };
@@ -533,7 +531,7 @@ __name(fuseRankedLists, "fuseRankedLists");
533
531
 
534
532
  // src/lib/prune.ts
535
533
  var MAX_PREVIEW_CHARS = 8e3;
536
- async function pruneResults(query, results, pruner) {
534
+ async function pruneResults(query, results, pruner, context) {
537
535
  if (results.length <= 1) return results;
538
536
  const items = results.map((r, i) => ({
539
537
  id: i,
@@ -541,7 +539,7 @@ async function pruneResults(query, results, pruner) {
541
539
  preview: _buildPreview(r.content),
542
540
  metadata: r.metadata
543
541
  }));
544
- const keepIds = await pruner.prune(query, items);
542
+ const keepIds = await pruner.prune(query, items, context);
545
543
  const validIds = new Set(Array.from({ length: results.length }, (_, i) => i));
546
544
  return keepIds.filter((id) => validIds.has(id)).map((id) => results[id]);
547
545
  }
@@ -568,7 +566,11 @@ function filterByPath(results, prefix) {
568
566
  if (!prefix) return results;
569
567
  const prefixes = Array.isArray(prefix) ? prefix : [prefix];
570
568
  if (prefixes.length === 0) return results;
571
- return results.filter((r) => prefixes.some((p) => r.filePath?.startsWith(p)));
569
+ const normalized = prefixes.map((p) => {
570
+ if (p.endsWith("/") || p.includes(".")) return p;
571
+ return p + "/";
572
+ });
573
+ return results.filter((r) => normalized.some((p) => r.filePath?.startsWith(p)));
572
574
  }
573
575
  __name(filterByPath, "filterByPath");
574
576
  function filterByIgnore(results, ignorePaths) {
@@ -652,20 +654,18 @@ function dbg(msg) {
652
654
  }
653
655
  __name(dbg, "dbg");
654
656
  var ContextBuilder = class {
655
- constructor(_search, _registry, _pruner, _embedding, _configFields = {}, _expander) {
657
+ constructor(_search, _registry, _pruner, _embedding, _configFields = {}) {
656
658
  this._search = _search;
657
659
  this._registry = _registry;
658
660
  this._pruner = _pruner;
659
661
  this._embedding = _embedding;
660
662
  this._configFields = _configFields;
661
- this._expander = _expander;
662
663
  }
663
664
  _search;
664
665
  _registry;
665
666
  _pruner;
666
667
  _embedding;
667
668
  _configFields;
668
- _expander;
669
669
  static {
670
670
  __name(this, "ContextBuilder");
671
671
  }
@@ -673,10 +673,6 @@ var ContextBuilder = class {
673
673
  set configFields(fields) {
674
674
  this._configFields = fields;
675
675
  }
676
- /** Set the expander instance. */
677
- set expander(expander) {
678
- this._expander = expander;
679
- }
680
676
  /** Build a full context block for a task. Returns markdown for system prompt. */
681
677
  async build(task, options = {}) {
682
678
  const t0 = Date.now();
@@ -692,10 +688,20 @@ var ContextBuilder = class {
692
688
  results = filterByIgnore(results, options.ignorePaths);
693
689
  const pruner = options.pruner ?? this._pruner;
694
690
  const beforePrune = results;
691
+ if (pruner && results.length > 40) {
692
+ const topScore = Math.max(...results.map((r) => r.score));
693
+ const threshold = topScore * 0.35;
694
+ const preFiltered = results.filter((r) => r.score >= threshold);
695
+ if (preFiltered.length < results.length && preFiltered.length >= 3) {
696
+ dbg(`[pre-filter] Dropped ${results.length - preFiltered.length} low-score results (threshold: ${threshold.toFixed(3)}, top: ${topScore.toFixed(3)})`);
697
+ results = preFiltered;
698
+ }
699
+ }
695
700
  if (pruner && results.length > 1) {
696
701
  dbg(`[pruner] Running ${_prunerName(pruner)} on ${results.length} results...`);
697
702
  const pruneT0 = Date.now();
698
- results = await pruneResults(task, results, pruner);
703
+ const prunerDesc = [options.context, options.prunerContext].filter(Boolean).join("\n\n") || void 0;
704
+ results = await pruneResults(task, results, pruner, prunerDesc);
699
705
  const pruneMs = Date.now() - pruneT0;
700
706
  const dropped = beforePrune.filter((r) => !results.includes(r));
701
707
  dbg(`[pruner] ${beforePrune.length} \u2192 ${results.length} in ${pruneMs}ms (${dropped.length} dropped)`);
@@ -714,40 +720,18 @@ var ContextBuilder = class {
714
720
  results = results.filter((r) => !r.filePath || !options.excludeFiles.has(r.filePath));
715
721
  }
716
722
  const resolvedFields = this._resolveFields(options);
717
- let expanderNote;
718
- if (resolvedFields.expander === true && this._expander && results.length > 0) {
719
- dbg(`[expander] Running expansion on ${results.length} results...`);
720
- const expansion = await this._expand(task, results);
721
- dbg(`[expander] Expansion returned ${expansion.results.length} new chunks${expansion.note ? `, note: "${expansion.note}"` : ""}`);
722
- if (expansion.results.length > 0) {
723
- results = [...results, ...expansion.results];
724
- }
725
- expanderNote = expansion.note;
726
- } else if (resolvedFields.expander === true && !this._expander) {
727
- dbg(`[expander] Field enabled but no expander instance! Check config.json "expander" key.`);
728
- }
729
723
  const parts = [`# Context for: "${task}"
730
724
  `];
731
725
  this._appendFormatterResults(results, parts, options, resolvedFields);
732
726
  await this._appendSearchableResults(task, src, minScore, parts);
733
- if (expanderNote) {
734
- parts.push(`
735
- ## Expansion Notes
736
-
737
- ${expanderNote}
738
- `);
739
- }
740
727
  const prunedResults = pruner ? beforePrune.filter((r) => !results.includes(r)) : [];
741
- const expanderEnabled = resolvedFields.expander === true;
742
- const expandedResults = expanderNote !== void 0 ? results.filter((r) => !beforePrune.includes(r) && !prunedResults.includes(r)) : [];
743
728
  logQuery({
744
729
  source: options.source ?? "api",
745
730
  method: "getContext",
746
731
  query: task,
747
732
  embedding: this._embedding ? providerKey(this._embedding) : "unknown",
748
733
  pruner: pruner ? _prunerName(pruner) : null,
749
- expander: expanderEnabled ? this._expander ? _expanderName(this._expander) : "configured-no-instance" : null,
750
- expandedCount: expandedResults.length > 0 ? expandedResults.length : void 0,
734
+ expander: null,
751
735
  options: {
752
736
  sources: src,
753
737
  pathPrefix: options.pathPrefix,
@@ -787,44 +771,6 @@ ${expanderNote}
787
771
  }
788
772
  return { ...defaults, ...this._configFields, ...options.fields ?? {} };
789
773
  }
790
- /**
791
- * Run LLM expansion: build manifest of candidate chunks from files
792
- * NOT already in search results, call expander, resolve selected IDs.
793
- */
794
- async _expand(task, results) {
795
- if (!this._expander) return { results: [] };
796
- const excludeFilePaths = [...new Set(
797
- results.filter((r) => r.filePath).map((r) => r.filePath)
798
- )];
799
- const excludeIds = [];
800
- for (const r of results) {
801
- const meta = r.metadata;
802
- const id = meta?.id;
803
- if (id !== void 0) excludeIds.push(id);
804
- }
805
- const manifest = [];
806
- let resolver;
807
- for (const mod of this._registry.all) {
808
- if (!isExpandablePlugin(mod)) continue;
809
- manifest.push(...mod.buildManifest(excludeFilePaths, excludeIds, excludeFilePaths));
810
- if (!resolver) {
811
- resolver = /* @__PURE__ */ __name((ids) => mod.resolveChunks(ids), "resolver");
812
- }
813
- }
814
- if (manifest.length === 0 || !resolver) {
815
- dbg(`[expander] No manifest items (no ExpandablePlugin or all chunks excluded)`);
816
- return { results: [] };
817
- }
818
- const priorityCount = manifest.filter((m) => m.priority).length;
819
- dbg(`[expander] Manifest: ${manifest.length} chunks (${priorityCount} priority, ${manifest.length - priorityCount} other), excluding ${excludeFilePaths.length} files`);
820
- try {
821
- const expandResult = await this._expander.expand(task, excludeIds, manifest);
822
- if (expandResult.ids.length === 0) return { results: [], note: expandResult.note };
823
- return { results: resolver(expandResult.ids), note: expandResult.note };
824
- } catch {
825
- return { results: [] };
826
- }
827
- }
828
774
  /** Collect results from SearchablePlugins that don't have their own formatter. */
829
775
  async _appendSearchableResults(task, sources, minScore, parts) {
830
776
  for (const mod of this._registry.all) {
@@ -856,10 +802,6 @@ function _prunerName(pruner) {
856
802
  return pruner.constructor?.name ?? "custom";
857
803
  }
858
804
  __name(_prunerName, "_prunerName");
859
- function _expanderName(expander) {
860
- return expander.constructor?.name ?? "custom";
861
- }
862
- __name(_expanderName, "_expanderName");
863
805
 
864
806
  // src/search/keyword/composite-bm25-search.ts
865
807
  var DEFAULT_K = 8;
@@ -1598,7 +1540,6 @@ var IGNORE_DIRS = /* @__PURE__ */ new Set([
1598
1540
  ".svelte-kit",
1599
1541
  // Auto-generated code
1600
1542
  "generated",
1601
- "sdk",
1602
1543
  "openapi",
1603
1544
  // Version control
1604
1545
  ".git",
@@ -2206,7 +2147,7 @@ function createSearchAPI(_db, embedding, config, registry, kvService, sharedHnsw
2206
2147
  embedding
2207
2148
  }) : void 0;
2208
2149
  const bm25 = new CompositeBM25Search(registry);
2209
- const contextBuilder = new ContextBuilder(search, registry, config.pruner, embedding, config.contextFields ?? {}, config.expander);
2150
+ const contextBuilder = new ContextBuilder(search, registry, config.pruner, embedding, config.contextFields ?? {});
2210
2151
  return new SearchAPI({
2211
2152
  search,
2212
2153
  bm25,
@@ -3191,23 +3132,23 @@ async function setupProviders(brainOpts, config, flags, env) {
3191
3132
  if (perplexityKey) process.env.PERPLEXITY_API_KEY = perplexityKey;
3192
3133
  if (openaiKey) process.env.OPENAI_API_KEY = openaiKey;
3193
3134
  const prunerFlag = flags?.pruner ?? config?.pruner;
3194
- if (prunerFlag === "haiku") {
3195
- const { HaikuPruner } = await import("./haiku-pruner-MT4JFKUC.js");
3135
+ if (prunerFlag === "haiku" || prunerFlag === "sonnet") {
3136
+ const { HaikuPruner } = await import("./haiku-pruner-5KVT5AI2.js");
3137
+ const model = prunerFlag === "sonnet" ? "claude-sonnet-4-6" : void 0;
3138
+ brainOpts.pruner = new HaikuPruner({ apiKey: anthropicKey, model });
3139
+ } else if (!prunerFlag && anthropicKey) {
3140
+ const { HaikuPruner } = await import("./haiku-pruner-5KVT5AI2.js");
3196
3141
  brainOpts.pruner = new HaikuPruner({ apiKey: anthropicKey });
3197
3142
  }
3198
- const expanderFlag = flags?.expander ?? config?.expander;
3199
- if (expanderFlag === "haiku") {
3200
- try {
3201
- const { HaikuExpander } = await import("./haiku-expander-CGEHFJIA.js");
3202
- brainOpts.expander = new HaikuExpander({ apiKey: anthropicKey });
3203
- } catch {
3204
- }
3205
- }
3206
3143
  const embFlag = flags?.embedding ?? config?.embedding ?? env?.BRAINBANK_EMBEDDING ?? process.env.BRAINBANK_EMBEDDING;
3207
3144
  if (embFlag) {
3208
3145
  const provider = await resolveEmbeddingKey(embFlag);
3209
3146
  brainOpts.embeddingProvider = provider;
3210
3147
  brainOpts.embeddingDims = provider.dims;
3148
+ } else if (perplexityKey) {
3149
+ const provider = await resolveEmbeddingKey("perplexity");
3150
+ brainOpts.embeddingProvider = provider;
3151
+ brainOpts.embeddingDims = provider.dims;
3211
3152
  }
3212
3153
  if (config?.context) {
3213
3154
  brainOpts.contextFields = config.context;
@@ -3397,4 +3338,4 @@ export {
3397
3338
  resetFactoryCache,
3398
3339
  createBrain
3399
3340
  };
3400
- //# sourceMappingURL=chunk-ZEUCCE6P.js.map
3341
+ //# sourceMappingURL=chunk-3UIWA32X.js.map