@pratik7368patil/anchor-core 0.1.28 → 0.1.30

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/index.d.ts CHANGED
@@ -781,6 +781,18 @@ type CodeIndexProgress = {
781
781
  repo: string;
782
782
  chunks: number;
783
783
  patterns: number;
784
+ } | {
785
+ stage: "deleting_code_fts";
786
+ repo: string;
787
+ current: number;
788
+ total: number;
789
+ chunks: number;
790
+ } | {
791
+ stage: "deleting_architecture_fts";
792
+ repo: string;
793
+ current: number;
794
+ total: number;
795
+ patterns: number;
784
796
  } | {
785
797
  stage: "writing_code_files";
786
798
  repo: string;
package/dist/index.js CHANGED
@@ -707,6 +707,21 @@ CREATE INDEX IF NOT EXISTS idx_org_consumers_provider ON org_api_consumers(org,
707
707
  CREATE INDEX IF NOT EXISTS idx_org_consumers_consumer ON org_api_consumers(org, consumer_repo);
708
708
  CREATE INDEX IF NOT EXISTS idx_org_anomalies_org ON org_anomaly_events(org, severity);
709
709
  CREATE INDEX IF NOT EXISTS idx_org_graph_state_status ON org_graph_state(org, last_status);
710
+
711
+ -- Foreign-key indexes backing per-repo / per-PR bulk deletes and re-index scans.
712
+ -- Without these, replaceCodeIndex/deleteExistingPrData full-scan each table per repo/PR.
713
+ CREATE INDEX IF NOT EXISTS idx_code_chunks_repo ON code_chunks(repo_id);
714
+ CREATE INDEX IF NOT EXISTS idx_code_files_repo ON code_files(repo_id);
715
+ CREATE INDEX IF NOT EXISTS idx_code_imports_repo ON code_imports(repo_id);
716
+ CREATE INDEX IF NOT EXISTS idx_test_files_repo ON test_files(repo_id);
717
+ CREATE INDEX IF NOT EXISTS idx_test_links_repo ON test_links(repo_id);
718
+ CREATE INDEX IF NOT EXISTS idx_architecture_components_repo ON architecture_components(repo_id);
719
+ CREATE INDEX IF NOT EXISTS idx_architecture_patterns_repo ON architecture_patterns(repo_id);
720
+ CREATE INDEX IF NOT EXISTS idx_architecture_map_edges_repo ON architecture_map_edges(repo_id);
721
+ CREATE INDEX IF NOT EXISTS idx_wisdom_units_repo ON wisdom_units(repo_id);
722
+ CREATE INDEX IF NOT EXISTS idx_regression_events_repo ON regression_events(repo_id);
723
+ CREATE INDEX IF NOT EXISTS idx_pr_files_pr ON pr_files(pr_id);
724
+ CREATE INDEX IF NOT EXISTS idx_pr_comments_pr ON pr_comments(pr_id);
710
725
  `;
711
726
 
712
727
  // src/rules/team-rules.ts
@@ -1695,9 +1710,28 @@ function calculateCoverage(input) {
1695
1710
 
1696
1711
  // src/db/database.ts
1697
1712
  var CODE_WRITE_PROGRESS_INTERVAL = 150;
1713
+ var FTS_DELETE_BATCH_SIZE = 500;
1698
1714
  function shouldEmitCodeWriteProgress(current, total) {
1699
1715
  return current === 0 || current === 1 || current === total || current % CODE_WRITE_PROGRESS_INTERVAL === 0;
1700
1716
  }
1717
+ function shouldEmitFtsDeleteProgress(current, total) {
1718
+ return current === 0 || current === 1 || current === total || current % FTS_DELETE_BATCH_SIZE === 0;
1719
+ }
1720
+ function deleteFtsRowsByRowId(db, ftsTable, rowIds, onProgress) {
1721
+ if (rowIds.length === 0) {
1722
+ onProgress?.(0, 0);
1723
+ return;
1724
+ }
1725
+ const deleteRow = db.prepare(`DELETE FROM ${ftsTable} WHERE rowid = ?`);
1726
+ onProgress?.(0, rowIds.length);
1727
+ for (const [index, rowId] of rowIds.entries()) {
1728
+ deleteRow.run(rowId);
1729
+ const current = index + 1;
1730
+ if (shouldEmitFtsDeleteProgress(current, rowIds.length)) {
1731
+ onProgress?.(current, rowIds.length);
1732
+ }
1733
+ }
1734
+ }
1701
1735
  function defaultDatabasePath(cwd) {
1702
1736
  return path4.join(cwd, ".anchor", "index.sqlite");
1703
1737
  }
@@ -1707,14 +1741,22 @@ function openAnchorDatabase(cwd, databasePath = defaultDatabasePath(cwd)) {
1707
1741
  db.pragma("busy_timeout = 5000");
1708
1742
  db.pragma("journal_mode = WAL");
1709
1743
  db.pragma("foreign_keys = ON");
1744
+ applyPerformancePragmas(db);
1710
1745
  return db;
1711
1746
  }
1712
1747
  function openAnchorDatabaseReadOnly(databasePath) {
1713
1748
  const db = new Database(databasePath, { readonly: true, fileMustExist: true });
1714
1749
  db.pragma("busy_timeout = 5000");
1715
1750
  db.pragma("foreign_keys = ON");
1751
+ applyPerformancePragmas(db);
1716
1752
  return db;
1717
1753
  }
1754
+ function applyPerformancePragmas(db) {
1755
+ db.pragma("synchronous = NORMAL");
1756
+ db.pragma("cache_size = -65536");
1757
+ db.pragma("mmap_size = 268435456");
1758
+ db.pragma("temp_store = MEMORY");
1759
+ }
1718
1760
  function initializeSchema(db) {
1719
1761
  db.exec(SCHEMA_SQL);
1720
1762
  ensureColumn(db, "sync_state", "history_coverage", "TEXT");
@@ -1882,9 +1924,12 @@ function clearGraphQLFetchCheckpoint(db, repo, scope) {
1882
1924
  ).run((/* @__PURE__ */ new Date()).toISOString(), repo);
1883
1925
  }
1884
1926
  function deleteExistingPrData(db, prId) {
1885
- db.prepare(
1886
- "DELETE FROM wisdom_units_fts WHERE unitId IN (SELECT id FROM wisdom_units WHERE pr_id = ?)"
1887
- ).run(prId);
1927
+ const wisdomRowIds = db.prepare("SELECT rowid FROM wisdom_units WHERE pr_id = ?").all(prId);
1928
+ deleteFtsRowsByRowId(
1929
+ db,
1930
+ "wisdom_units_fts",
1931
+ wisdomRowIds.map((row) => row.rowid)
1932
+ );
1888
1933
  db.prepare("DELETE FROM regression_events WHERE pr_id = ?").run(prId);
1889
1934
  db.prepare("DELETE FROM wisdom_units WHERE pr_id = ?").run(prId);
1890
1935
  db.prepare("DELETE FROM pr_comments WHERE pr_id = ?").run(prId);
@@ -1996,11 +2041,11 @@ function upsertPullRequest(db, pr, wisdomUnits, regressionEvents = []) {
1996
2041
  );
1997
2042
  const insertFts = db.prepare(
1998
2043
  `INSERT INTO wisdom_units_fts
1999
- (unitId, sanitizedText, filePaths, symbols, prTitle, prBody, category)
2000
- VALUES (?, ?, ?, ?, ?, ?, ?)`
2044
+ (rowid, unitId, sanitizedText, filePaths, symbols, prTitle, prBody, category)
2045
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
2001
2046
  );
2002
2047
  for (const unit of wisdomUnits) {
2003
- insertWisdom.run(
2048
+ const wisdomInsert = insertWisdom.run(
2004
2049
  unit.id,
2005
2050
  repoId,
2006
2051
  prRow.id,
@@ -2019,6 +2064,7 @@ function upsertPullRequest(db, pr, wisdomUnits, regressionEvents = []) {
2019
2064
  unit.confidence
2020
2065
  );
2021
2066
  insertFts.run(
2067
+ Number(wisdomInsert.lastInsertRowid),
2022
2068
  unit.id,
2023
2069
  unit.sanitizedText,
2024
2070
  unit.filePaths.join(" "),
@@ -2075,22 +2121,31 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2075
2121
  });
2076
2122
  options.onProgress?.({ stage: "writing_code_index", repo, phase: "Writing code index" });
2077
2123
  const transaction = db.transaction(() => {
2078
- const existingChunkCount = db.prepare("SELECT COUNT(*) AS count FROM code_chunks WHERE repo_id = ?").get(repoId).count;
2079
- const existingPatternCount = db.prepare("SELECT COUNT(*) AS count FROM architecture_patterns WHERE repo_id = ?").get(repoId).count;
2124
+ const existingChunkRowIds = db.prepare("SELECT rowid FROM code_chunks WHERE repo_id = ?").all(repoId);
2125
+ const existingPatternRowIds = db.prepare("SELECT rowid FROM architecture_patterns WHERE repo_id = ?").all(repoId);
2080
2126
  options.onProgress?.({
2081
2127
  stage: "deleting_existing_code_index",
2082
2128
  repo,
2083
- chunks: existingChunkCount,
2084
- patterns: existingPatternCount
2129
+ chunks: existingChunkRowIds.length,
2130
+ patterns: existingPatternRowIds.length
2085
2131
  });
2086
- db.prepare(
2087
- "DELETE FROM code_chunks_fts WHERE chunkId IN (SELECT id FROM code_chunks WHERE repo_id = ?)"
2088
- ).run(repoId);
2132
+ deleteFtsRowsByRowId(
2133
+ db,
2134
+ "code_chunks_fts",
2135
+ existingChunkRowIds.map((row) => row.rowid),
2136
+ (current, total) => options.onProgress?.({
2137
+ stage: "deleting_code_fts",
2138
+ repo,
2139
+ current,
2140
+ total,
2141
+ chunks: existingChunkRowIds.length
2142
+ })
2143
+ );
2089
2144
  db.prepare("DELETE FROM code_chunks WHERE repo_id = ?").run(repoId);
2090
2145
  db.prepare("DELETE FROM code_files WHERE repo_id = ?").run(repoId);
2091
2146
  db.prepare("DELETE FROM test_links WHERE repo_id = ? AND reason != 'PR co-change'").run(repoId);
2092
2147
  db.prepare("DELETE FROM test_files WHERE repo_id = ?").run(repoId);
2093
- deleteExistingArchitectureData(db, repoId);
2148
+ deleteExistingArchitectureData(db, repoId, repo, existingPatternRowIds, options);
2094
2149
  const insertFile = db.prepare(
2095
2150
  `INSERT INTO code_files
2096
2151
  (repo_id, path, language, size_bytes, content_hash, updated_at)
@@ -2132,8 +2187,8 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2132
2187
  );
2133
2188
  const insertFts = db.prepare(
2134
2189
  `INSERT INTO code_chunks_fts
2135
- (chunkId, sanitizedText, filePath, symbols, language)
2136
- VALUES (?, ?, ?, ?, ?)`
2190
+ (rowid, chunkId, sanitizedText, filePath, symbols, language)
2191
+ VALUES (?, ?, ?, ?, ?, ?)`
2137
2192
  );
2138
2193
  options.onProgress?.({
2139
2194
  stage: "writing_code_chunks",
@@ -2146,7 +2201,7 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2146
2201
  for (const [index, chunk] of codeChunks.entries()) {
2147
2202
  const fileId = fileIds.get(chunk.filePath);
2148
2203
  if (!fileId) continue;
2149
- insertChunk.run(
2204
+ const chunkInsert = insertChunk.run(
2150
2205
  chunk.id,
2151
2206
  repoId,
2152
2207
  fileId,
@@ -2161,6 +2216,7 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2161
2216
  chunk.updatedAt
2162
2217
  );
2163
2218
  insertFts.run(
2219
+ Number(chunkInsert.lastInsertRowid),
2164
2220
  chunk.id,
2165
2221
  chunk.sanitizedText,
2166
2222
  chunk.filePath,
@@ -2222,10 +2278,19 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2222
2278
  databasePath: defaultDatabasePath(cwd)
2223
2279
  };
2224
2280
  }
2225
- function deleteExistingArchitectureData(db, repoId) {
2226
- db.prepare(
2227
- "DELETE FROM architecture_patterns_fts WHERE patternId IN (SELECT id FROM architecture_patterns WHERE repo_id = ?)"
2228
- ).run(repoId);
2281
+ function deleteExistingArchitectureData(db, repoId, repo, patternRowIds, options = {}) {
2282
+ deleteFtsRowsByRowId(
2283
+ db,
2284
+ "architecture_patterns_fts",
2285
+ patternRowIds.map((row) => row.rowid),
2286
+ (current, total) => options.onProgress?.({
2287
+ stage: "deleting_architecture_fts",
2288
+ repo,
2289
+ current,
2290
+ total,
2291
+ patterns: patternRowIds.length
2292
+ })
2293
+ );
2229
2294
  db.prepare("DELETE FROM architecture_patterns WHERE repo_id = ?").run(repoId);
2230
2295
  db.prepare("DELETE FROM architecture_components WHERE repo_id = ?").run(repoId);
2231
2296
  db.prepare("DELETE FROM code_imports WHERE repo_id = ?").run(repoId);
@@ -2308,8 +2373,8 @@ function insertArchitectureData(db, repoId, repo, architecture, options = {}) {
2308
2373
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2309
2374
  );
2310
2375
  const insertFts = db.prepare(
2311
- `INSERT INTO architecture_patterns_fts (patternId, summary, area, sourceFiles, symbols)
2312
- VALUES (?, ?, ?, ?, ?)`
2376
+ `INSERT INTO architecture_patterns_fts (rowid, patternId, summary, area, sourceFiles, symbols)
2377
+ VALUES (?, ?, ?, ?, ?, ?)`
2313
2378
  );
2314
2379
  options.onProgress?.({
2315
2380
  stage: "writing_architecture_data",
@@ -2319,7 +2384,7 @@ function insertArchitectureData(db, repoId, repo, architecture, options = {}) {
2319
2384
  kind: "patterns"
2320
2385
  });
2321
2386
  for (const [index, pattern] of architecture.patterns.entries()) {
2322
- insertPattern.run(
2387
+ const patternInsert = insertPattern.run(
2323
2388
  pattern.id,
2324
2389
  repoId,
2325
2390
  pattern.repo,
@@ -2333,6 +2398,7 @@ function insertArchitectureData(db, repoId, repo, architecture, options = {}) {
2333
2398
  pattern.createdAt
2334
2399
  );
2335
2400
  insertFts.run(
2401
+ Number(patternInsert.lastInsertRowid),
2336
2402
  pattern.id,
2337
2403
  pattern.sanitizedSummary,
2338
2404
  pattern.area,
@@ -2921,14 +2987,24 @@ function buildRelatedTestIndex(allPaths) {
2921
2987
  const testPaths = allPaths.filter((candidate) => isTestFilePath(candidate));
2922
2988
  const byBase = /* @__PURE__ */ new Map();
2923
2989
  const byDirectory = /* @__PURE__ */ new Map();
2990
+ const byDotPrefix = /* @__PURE__ */ new Map();
2924
2991
  for (const testPath of testPaths) {
2925
2992
  addToStringMap(byBase, testBaseFor(testPath), testPath);
2926
- const segments = path6.posix.dirname(testPath).split("/").filter(Boolean);
2927
- for (let index = 1; index <= segments.length; index += 1) {
2928
- addToStringMap(byDirectory, segments.slice(0, index).join("/"), testPath);
2993
+ const dirSegments = path6.posix.dirname(testPath).split("/").filter(Boolean);
2994
+ for (let index = 1; index <= dirSegments.length; index += 1) {
2995
+ addToStringMap(byDirectory, dirSegments.slice(0, index).join("/"), testPath);
2996
+ }
2997
+ const pathSegments2 = testPath.split("/");
2998
+ const dotPrefixes = /* @__PURE__ */ new Set();
2999
+ for (let i = 1; i < pathSegments2.length; i += 1) {
3000
+ const segment = pathSegments2[i] ?? "";
3001
+ for (let dot = segment.indexOf("."); dot >= 0; dot = segment.indexOf(".", dot + 1)) {
3002
+ dotPrefixes.add(segment.slice(0, dot));
3003
+ }
2929
3004
  }
3005
+ for (const prefix of dotPrefixes) addToStringMap(byDotPrefix, prefix, testPath);
2930
3006
  }
2931
- return { testPaths, byBase, byDirectory };
3007
+ return { testPaths, byBase, byDirectory, byDotPrefix };
2932
3008
  }
2933
3009
  function relatedTestsFor(filePath, index) {
2934
3010
  if (isTestFilePath(filePath)) return [];
@@ -2945,8 +3021,8 @@ function relatedTestsFor(filePath, index) {
2945
3021
  if (parsed.dir) {
2946
3022
  for (const testPath of index.byDirectory.get(parsed.dir) ?? []) add(testPath);
2947
3023
  }
2948
- for (const testPath of index.testPaths) {
2949
- if (testPath.includes(`/${basename}.`)) add(testPath);
3024
+ for (const testPath of index.byDotPrefix.get(basename) ?? []) {
3025
+ add(testPath);
2950
3026
  if (related.length >= 8) break;
2951
3027
  }
2952
3028
  return related.slice(0, 8);
@@ -4094,8 +4170,7 @@ function symbolMatch2(unit, querySymbols) {
4094
4170
  const lower = symbol.toLowerCase();
4095
4171
  if (unitSymbols.includes(lower)) best = Math.max(best, 1);
4096
4172
  else if (text.includes(`\`${lower}\``)) best = Math.max(best, 1);
4097
- else if (new RegExp(`\\b${escapeRegExp(lower)}\\b`, "i").test(text))
4098
- best = Math.max(best, 0.66);
4173
+ else if (symbolBoundaryRegex(lower).test(text)) best = Math.max(best, 0.66);
4099
4174
  else if (unitSymbols.some((candidate) => candidate.includes(lower) || lower.includes(candidate))) {
4100
4175
  best = Math.max(best, 0.35);
4101
4176
  }
@@ -4178,6 +4253,15 @@ function scoreUnit(unit, input, duplicateCount, repeatedEvidenceCount, freshness
4178
4253
  function escapeRegExp(value) {
4179
4254
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4180
4255
  }
4256
+ var symbolBoundaryRegexCache = /* @__PURE__ */ new Map();
4257
+ function symbolBoundaryRegex(lower) {
4258
+ let regex = symbolBoundaryRegexCache.get(lower);
4259
+ if (!regex) {
4260
+ regex = new RegExp(`\\b${escapeRegExp(lower)}\\b`, "i");
4261
+ symbolBoundaryRegexCache.set(lower, regex);
4262
+ }
4263
+ return regex;
4264
+ }
4181
4265
  function loadCandidates(db, input) {
4182
4266
  const ftsQuery = buildFtsQuery(input);
4183
4267
  const categories = "categories" in input ? input.categories ?? [] : [];
@@ -4213,9 +4297,11 @@ function loadClaimRepetitionCounts(db) {
4213
4297
  }
4214
4298
  return new Map([...grouped.entries()].map(([key, prs]) => [key, prs.size]));
4215
4299
  }
4216
- function loadFeedbackAdjustments(db) {
4217
- const rows = db.prepare("SELECT result_id, rating FROM feedback_events").all();
4300
+ function loadFeedbackAdjustments(db, resultIds) {
4218
4301
  const adjustments = /* @__PURE__ */ new Map();
4302
+ if (resultIds.length === 0) return adjustments;
4303
+ const placeholders = resultIds.map(() => "?").join(", ");
4304
+ const rows = db.prepare(`SELECT result_id, rating FROM feedback_events WHERE result_id IN (${placeholders})`).all(...resultIds);
4219
4305
  for (const row of rows) {
4220
4306
  const delta = row.rating === "useful" ? 0.03 : -0.03;
4221
4307
  adjustments.set(row.result_id, (adjustments.get(row.result_id) ?? 0) + delta);
@@ -4235,7 +4321,10 @@ function rankWisdomUnits(db, input) {
4235
4321
  const candidates = loadCandidates(db, input);
4236
4322
  const codeSnapshot = loadCurrentCodeSnapshot(db);
4237
4323
  const repetitionCounts = loadClaimRepetitionCounts(db);
4238
- const feedbackAdjustments = loadFeedbackAdjustments(db);
4324
+ const feedbackAdjustments = loadFeedbackAdjustments(
4325
+ db,
4326
+ candidates.map((unit) => unit.id)
4327
+ );
4239
4328
  const duplicates = /* @__PURE__ */ new Map();
4240
4329
  for (const unit of candidates) {
4241
4330
  const key = claimKeyFor(unit.category, unit.sanitizedText);
@@ -9897,8 +9986,9 @@ function queryTerms(input) {
9897
9986
  function matchesRepo(repo, repos) {
9898
9987
  return !repos || repos.length === 0 || repos.includes(repo);
9899
9988
  }
9900
- function rowScore(input, text, files, symbols) {
9989
+ function rowScore(input, text, files, symbols, lowerTerms) {
9901
9990
  let score = 0;
9991
+ const lowerText = text.toLowerCase();
9902
9992
  for (const file of input.files ?? []) {
9903
9993
  if (files.includes(file)) score += 5;
9904
9994
  else if (files.some((candidate) => candidate.endsWith(`/${file.split("/").pop() ?? file}`)))
@@ -9906,10 +9996,10 @@ function rowScore(input, text, files, symbols) {
9906
9996
  }
9907
9997
  for (const symbol of input.symbols ?? []) {
9908
9998
  if (symbols.includes(symbol)) score += 4;
9909
- else if (text.toLowerCase().includes(symbol.toLowerCase())) score += 1;
9999
+ else if (lowerText.includes(symbol.toLowerCase())) score += 1;
9910
10000
  }
9911
- for (const term of queryTerms(input)) {
9912
- if (text.toLowerCase().includes(term.toLowerCase())) score += 0.5;
10001
+ for (const term of lowerTerms) {
10002
+ if (lowerText.includes(term)) score += 0.5;
9913
10003
  }
9914
10004
  return score;
9915
10005
  }
@@ -9920,9 +10010,10 @@ function getWisdom(db, input, limit) {
9920
10010
  ORDER BY confidence DESC, created_at DESC
9921
10011
  LIMIT 500`
9922
10012
  ).all();
10013
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9923
10014
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9924
10015
  row,
9925
- score: rowScore(input, row.sanitized_text, parseStringArray(row.file_paths_json), [])
10016
+ score: rowScore(input, row.sanitized_text, parseStringArray(row.file_paths_json), [], lowerTerms)
9926
10017
  })).filter((item) => item.score > 0 || (input.files ?? []).length === 0).sort((a, b) => b.score - a.score || b.row.confidence - a.row.confidence).slice(0, limit).map((item) => item.row);
9927
10018
  }
9928
10019
  function getCodeEvidence(db, input, limit) {
@@ -9932,13 +10023,15 @@ function getCodeEvidence(db, input, limit) {
9932
10023
  ORDER BY updated_at DESC
9933
10024
  LIMIT 800`
9934
10025
  ).all();
10026
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9935
10027
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9936
10028
  row,
9937
10029
  score: rowScore(
9938
10030
  input,
9939
10031
  row.sanitized_text,
9940
10032
  [row.file_path],
9941
- parseStringArray(row.symbols_json)
10033
+ parseStringArray(row.symbols_json),
10034
+ lowerTerms
9942
10035
  )
9943
10036
  })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((item) => item.row);
9944
10037
  }
@@ -9949,23 +10042,30 @@ function getArchitecture(db, input, limit) {
9949
10042
  ORDER BY confidence DESC, created_at DESC
9950
10043
  LIMIT 300`
9951
10044
  ).all();
10045
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9952
10046
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9953
10047
  row,
9954
- score: rowScore(input, row.summary_sanitized, parseStringArray(row.source_files_json), [])
10048
+ score: rowScore(
10049
+ input,
10050
+ row.summary_sanitized,
10051
+ parseStringArray(row.source_files_json),
10052
+ [],
10053
+ lowerTerms
10054
+ )
9955
10055
  })).filter((item) => item.score > 0 || (input.files ?? []).length === 0).sort((a, b) => b.score - a.score || b.row.confidence - a.row.confidence).slice(0, limit).map((item) => item.row);
9956
10056
  }
9957
10057
  function findOrgApiConsumers(db, config, input) {
9958
10058
  initializeSchema(db);
10059
+ const repoClause = input.repo ? " AND (provider_repo = ? OR consumer_repo = ?)" : "";
10060
+ const repoParams = input.repo ? [input.repo, input.repo] : [];
9959
10061
  const rows = db.prepare(
9960
10062
  `SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json, confidence
9961
10063
  FROM org_api_consumers
9962
- WHERE org = ?
10064
+ WHERE org = ?${repoClause}
9963
10065
  ORDER BY confidence DESC`
9964
- ).all(config.org);
10066
+ ).all(config.org, ...repoParams);
9965
10067
  const limit = Math.max(1, Math.min(input.maxResults ?? 8, 25));
9966
- return rows.filter(
9967
- (row) => !input.repo || row.provider_repo === input.repo || row.consumer_repo === input.repo
9968
- ).filter((row) => {
10068
+ return rows.filter((row) => {
9969
10069
  const files = input.files ?? [];
9970
10070
  if (files.length === 0 && !input.query) return true;
9971
10071
  return files.some((file) => row.provider_path === file || row.consumer_path === file) || Boolean(input.query && row.contract.toLowerCase().includes(input.query.toLowerCase()));