@bodhi-ventures/aiocs 0.6.0 → 0.6.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.
@@ -4367,7 +4367,7 @@ async function startDaemon(input) {
4367
4367
  // package.json
4368
4368
  var package_default = {
4369
4369
  name: "@bodhi-ventures/aiocs",
4370
- version: "0.6.0",
4370
+ version: "0.6.1",
4371
4371
  license: "MIT",
4372
4372
  type: "module",
4373
4373
  description: "Local-only documentation store, fetcher, and search CLI for AI agents.",
@@ -5439,6 +5439,63 @@ function overlapScore(queryTokens, candidate) {
5439
5439
  }
5440
5440
  return matches;
5441
5441
  }
5442
+ function uniqueTokens(tokens) {
5443
+ return [...new Set(tokens)];
5444
+ }
5445
+ function hasTokenCoverage(requiredTokens, candidate) {
5446
+ if (requiredTokens.length === 0) {
5447
+ return false;
5448
+ }
5449
+ const candidateTokens = new Set(tokenize(candidate));
5450
+ return requiredTokens.every((token) => candidateTokens.has(token));
5451
+ }
5452
+ var NAVIGATIONAL_TERMS = /* @__PURE__ */ new Set([
5453
+ "api",
5454
+ "apis",
5455
+ "auth",
5456
+ "authentication",
5457
+ "docs",
5458
+ "documentation",
5459
+ "endpoint",
5460
+ "endpoints",
5461
+ "overview",
5462
+ "reference",
5463
+ "references",
5464
+ "rest",
5465
+ "sdk",
5466
+ "transport",
5467
+ "websocket",
5468
+ "ws"
5469
+ ]);
5470
+ function classifyRetrievalQuery(query) {
5471
+ const queryTokens = uniqueTokens(tokenize(query));
5472
+ const matchedNavigationalTerms = uniqueTokens(queryTokens.filter((token) => NAVIGATIONAL_TERMS.has(token)));
5473
+ return {
5474
+ isNavigational: matchedNavigationalTerms.length > 0,
5475
+ queryTokens,
5476
+ routingTokens: queryTokens,
5477
+ matchedNavigationalTerms
5478
+ };
5479
+ }
5480
+ function scorePageCandidate(query, input) {
5481
+ const intent = classifyRetrievalQuery(query);
5482
+ const routingTokens = intent.routingTokens;
5483
+ const titleOverlap = overlapScore(routingTokens, input.pageTitle);
5484
+ const referenceOverlap = overlapScore(routingTokens, input.pageReference);
5485
+ const sectionOverlap = Math.max(0, ...input.sectionTitles.map((sectionTitle) => overlapScore(routingTokens, sectionTitle)));
5486
+ const exactTitleCoverage = hasTokenCoverage(routingTokens, input.pageTitle);
5487
+ const exactReferenceCoverage = hasTokenCoverage(routingTokens, input.pageReference);
5488
+ const lexicalWeight = intent.isNavigational ? 6 : 4;
5489
+ const vectorWeight = intent.isNavigational ? 1 : 3;
5490
+ const titleWeight = intent.isNavigational ? 8 : 4;
5491
+ const referenceWeight = intent.isNavigational ? 6 : 3;
5492
+ const sectionWeight = intent.isNavigational ? 4 : 2;
5493
+ const sourceHintWeight = intent.isNavigational ? 3 : 2;
5494
+ const commonLocationWeight = intent.isNavigational ? 7 : 4;
5495
+ const learningWeight = intent.isNavigational ? 5 : 4;
5496
+ const pureVectorPenalty = intent.isNavigational && input.bestLexicalScore === 0 && titleOverlap === 0 && referenceOverlap === 0 && input.commonLocationScore === 0 ? 8 : 0;
5497
+ return input.bestLexicalScore * lexicalWeight + input.bestVectorScore * vectorWeight + titleOverlap * titleWeight + referenceOverlap * referenceWeight + sectionOverlap * sectionWeight + input.sourceHintScore * sourceHintWeight + input.commonLocationScore * commonLocationWeight + input.learningScore * learningWeight + (exactTitleCoverage ? 12 : 0) + (exactReferenceCoverage ? 10 : 0) - pureVectorPenalty;
5498
+ }
5442
5499
  function scoreLearning(query, learning) {
5443
5500
  const queryTokens = tokenize(query);
5444
5501
  const candidates = [learning.intent, ...learning.searchTerms];
@@ -5537,6 +5594,9 @@ function dedupePagesByIdentity(rows) {
5537
5594
  }
5538
5595
  return deduped;
5539
5596
  }
5597
+ function pageIdentityKey(input) {
5598
+ return `${input.sourceId}::${input.snapshotId}::${input.filePath ?? input.pageUrl}`;
5599
+ }
5540
5600
  function createCatalog() {
5541
5601
  const dataDir = getAiocsDataDir();
5542
5602
  getAiocsConfigDir();
@@ -5796,6 +5856,8 @@ async function retrieveContext(query, options) {
5796
5856
  const avoidedPageKeys = new Set(
5797
5857
  avoidedLearnings.map((learning) => `${learning.sourceId}::${learning.filePath ?? learning.pageUrl ?? ""}`)
5798
5858
  );
5859
+ const sourceHintScores = /* @__PURE__ */ new Map();
5860
+ const commonLocationBoosts = /* @__PURE__ */ new Map();
5799
5861
  const sourceHints = sourceScope.map((sourceId) => {
5800
5862
  const contextRecord = catalog.getSourceContext(sourceId);
5801
5863
  const score = scoreSourceContext(query, contextRecord.context);
@@ -5819,6 +5881,28 @@ async function retrieveContext(query, options) {
5819
5881
  matchedCommonLocations
5820
5882
  };
5821
5883
  }).filter((entry) => Boolean(entry)).sort((left, right) => right.score - left.score || left.sourceId.localeCompare(right.sourceId));
5884
+ for (const sourceHint of sourceHints) {
5885
+ sourceHintScores.set(
5886
+ sourceHint.sourceId,
5887
+ Math.max(sourceHint.score, sourceHintScores.get(sourceHint.sourceId) ?? 0)
5888
+ );
5889
+ const latestSnapshotId = options.snapshot ?? catalog.getSourceById(sourceHint.sourceId)?.lastSuccessfulSnapshotId ?? null;
5890
+ if (!latestSnapshotId) {
5891
+ continue;
5892
+ }
5893
+ for (const location of sourceHint.matchedCommonLocations) {
5894
+ const key = pageIdentityKey({
5895
+ sourceId: sourceHint.sourceId,
5896
+ snapshotId: latestSnapshotId,
5897
+ pageUrl: location.url ?? "",
5898
+ filePath: location.filePath ?? null
5899
+ });
5900
+ commonLocationBoosts.set(
5901
+ key,
5902
+ Math.max(sourceHint.score, commonLocationBoosts.get(key) ?? 0)
5903
+ );
5904
+ }
5905
+ }
5822
5906
  const search = await searchHybridCatalog({
5823
5907
  catalog,
5824
5908
  config: hybridConfig,
@@ -5847,21 +5931,100 @@ async function retrieveContext(query, options) {
5847
5931
  pageUrl: result.pageUrl,
5848
5932
  filePath: result.filePath
5849
5933
  }));
5850
- const selectedPages = dedupePagesByIdentity([
5934
+ const commonLocationPages = sourceHints.flatMap((sourceHint) => {
5935
+ const snapshotId = options.snapshot ?? catalog.getSourceById(sourceHint.sourceId)?.lastSuccessfulSnapshotId ?? "";
5936
+ if (!snapshotId) {
5937
+ return [];
5938
+ }
5939
+ return sourceHint.matchedCommonLocations.map((location) => ({
5940
+ sourceId: sourceHint.sourceId,
5941
+ snapshotId,
5942
+ pageUrl: location.url ?? "",
5943
+ filePath: location.filePath ?? null
5944
+ }));
5945
+ });
5946
+ const pageCandidates = dedupePagesByIdentity([
5947
+ ...commonLocationPages,
5851
5948
  ...learnedPages,
5852
5949
  ...searchedPages
5853
- ]).slice(0, Math.max(1, pageLimit));
5854
- const pages = selectedPages.flatMap((entry) => {
5950
+ ]).filter((entry) => !avoidedPageKeys.has(`${entry.sourceId}::${entry.filePath ?? entry.pageUrl}`));
5951
+ const searchPageSignals = /* @__PURE__ */ new Map();
5952
+ for (const result of search.results) {
5953
+ if (avoidedPageKeys.has(`${result.sourceId}::${result.filePath ?? result.pageUrl}`)) {
5954
+ continue;
5955
+ }
5956
+ const key = pageIdentityKey({
5957
+ sourceId: result.sourceId,
5958
+ snapshotId: result.snapshotId,
5959
+ pageUrl: result.pageUrl,
5960
+ filePath: result.filePath
5961
+ });
5962
+ const record = searchPageSignals.get(key) ?? {
5963
+ bestLexicalScore: 0,
5964
+ bestVectorScore: 0,
5965
+ sectionTitles: /* @__PURE__ */ new Set()
5966
+ };
5967
+ const signalCount = Math.max(1, result.signals.length);
5968
+ const perSignalScore = result.score / signalCount;
5969
+ if (result.signals.includes("lexical")) {
5970
+ record.bestLexicalScore = Math.max(record.bestLexicalScore, perSignalScore);
5971
+ }
5972
+ if (result.signals.includes("vector")) {
5973
+ record.bestVectorScore = Math.max(record.bestVectorScore, perSignalScore);
5974
+ }
5975
+ record.sectionTitles.add(result.sectionTitle);
5976
+ searchPageSignals.set(key, record);
5977
+ }
5978
+ const learningBoosts = /* @__PURE__ */ new Map();
5979
+ for (const learning of matchedLearnings) {
5980
+ if (!learning.pageUrl && !learning.filePath) {
5981
+ continue;
5982
+ }
5983
+ const snapshotId = learning.snapshotId ?? catalog.getSourceById(learning.sourceId)?.lastSuccessfulSnapshotId ?? "";
5984
+ if (!snapshotId) {
5985
+ continue;
5986
+ }
5987
+ const key = pageIdentityKey({
5988
+ sourceId: learning.sourceId,
5989
+ snapshotId,
5990
+ pageUrl: learning.pageUrl ?? "",
5991
+ filePath: learning.filePath ?? null
5992
+ });
5993
+ learningBoosts.set(key, Math.max(learning.score, learningBoosts.get(key) ?? 0));
5994
+ }
5995
+ const rankedPages = pageCandidates.flatMap((entry) => {
5855
5996
  try {
5856
5997
  const page = catalog.getPage({
5857
5998
  sourceId: entry.sourceId,
5858
5999
  snapshotId: entry.snapshotId,
5859
6000
  ...entry.filePath ? { filePath: entry.filePath } : { url: entry.pageUrl }
5860
6001
  });
6002
+ const key = pageIdentityKey({
6003
+ sourceId: page.sourceId,
6004
+ snapshotId: page.snapshotId,
6005
+ pageUrl: page.page.url,
6006
+ filePath: page.page.filePath
6007
+ });
6008
+ const searchSignals = searchPageSignals.get(key) ?? {
6009
+ bestLexicalScore: 0,
6010
+ bestVectorScore: 0,
6011
+ sectionTitles: /* @__PURE__ */ new Set()
6012
+ };
6013
+ const score = scorePageCandidate(query, {
6014
+ pageTitle: page.page.title,
6015
+ pageReference: page.page.filePath ?? page.page.url,
6016
+ sectionTitles: [...searchSignals.sectionTitles],
6017
+ bestLexicalScore: searchSignals.bestLexicalScore,
6018
+ bestVectorScore: searchSignals.bestVectorScore,
6019
+ learningScore: learningBoosts.get(key) ?? 0,
6020
+ sourceHintScore: sourceHintScores.get(page.sourceId) ?? 0,
6021
+ commonLocationScore: commonLocationBoosts.get(key) ?? 0
6022
+ });
5861
6023
  return [{
5862
6024
  sourceId: page.sourceId,
5863
6025
  snapshotId: page.snapshotId,
5864
- ...page.page
6026
+ score,
6027
+ page: page.page
5865
6028
  }];
5866
6029
  } catch (error) {
5867
6030
  if (error instanceof AiocsError && (error.code === AIOCS_ERROR_CODES.pageNotFound || error.code === AIOCS_ERROR_CODES.snapshotNotFound)) {
@@ -5869,7 +6032,12 @@ async function retrieveContext(query, options) {
5869
6032
  }
5870
6033
  throw error;
5871
6034
  }
5872
- });
6035
+ }).sort((left, right) => right.score - left.score || left.page.title.localeCompare(right.page.title) || left.page.url.localeCompare(right.page.url)).slice(0, Math.max(1, pageLimit));
6036
+ const pages = rankedPages.map((entry) => ({
6037
+ sourceId: entry.sourceId,
6038
+ snapshotId: entry.snapshotId,
6039
+ ...entry.page
6040
+ }));
5873
6041
  return {
5874
6042
  query,
5875
6043
  modeRequested: search.modeRequested,
package/dist/cli.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  upsertSourceContextFromFile,
40
40
  upsertSourceFromSpecFile,
41
41
  verifyCoverage
42
- } from "./chunk-2UWV3O7E.js";
42
+ } from "./chunk-TJPALXTG.js";
43
43
 
44
44
  // src/cli.ts
45
45
  import { Command, CommanderError as CommanderError2 } from "commander";
@@ -34,7 +34,7 @@ import {
34
34
  upsertSourceContextFromFile,
35
35
  upsertSourceFromSpecFile,
36
36
  verifyCoverage
37
- } from "./chunk-2UWV3O7E.js";
37
+ } from "./chunk-TJPALXTG.js";
38
38
 
39
39
  // src/mcp-server.ts
40
40
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bodhi-ventures/aiocs",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local-only documentation store, fetcher, and search CLI for AI agents.",