@pratik7368patil/anchor-core 0.1.27 → 0.1.29

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.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
@@ -1707,14 +1722,22 @@ function openAnchorDatabase(cwd, databasePath = defaultDatabasePath(cwd)) {
1707
1722
  db.pragma("busy_timeout = 5000");
1708
1723
  db.pragma("journal_mode = WAL");
1709
1724
  db.pragma("foreign_keys = ON");
1725
+ applyPerformancePragmas(db);
1710
1726
  return db;
1711
1727
  }
1712
1728
  function openAnchorDatabaseReadOnly(databasePath) {
1713
1729
  const db = new Database(databasePath, { readonly: true, fileMustExist: true });
1714
1730
  db.pragma("busy_timeout = 5000");
1715
1731
  db.pragma("foreign_keys = ON");
1732
+ applyPerformancePragmas(db);
1716
1733
  return db;
1717
1734
  }
1735
+ function applyPerformancePragmas(db) {
1736
+ db.pragma("synchronous = NORMAL");
1737
+ db.pragma("cache_size = -65536");
1738
+ db.pragma("mmap_size = 268435456");
1739
+ db.pragma("temp_store = MEMORY");
1740
+ }
1718
1741
  function initializeSchema(db) {
1719
1742
  db.exec(SCHEMA_SQL);
1720
1743
  ensureColumn(db, "sync_state", "history_coverage", "TEXT");
@@ -1882,9 +1905,9 @@ function clearGraphQLFetchCheckpoint(db, repo, scope) {
1882
1905
  ).run((/* @__PURE__ */ new Date()).toISOString(), repo);
1883
1906
  }
1884
1907
  function deleteExistingPrData(db, prId) {
1885
- const unitRows = db.prepare("SELECT id FROM wisdom_units WHERE pr_id = ?").all(prId);
1886
- const deleteFts = db.prepare("DELETE FROM wisdom_units_fts WHERE unitId = ?");
1887
- for (const row of unitRows) deleteFts.run(row.id);
1908
+ db.prepare(
1909
+ "DELETE FROM wisdom_units_fts WHERE unitId IN (SELECT id FROM wisdom_units WHERE pr_id = ?)"
1910
+ ).run(prId);
1888
1911
  db.prepare("DELETE FROM regression_events WHERE pr_id = ?").run(prId);
1889
1912
  db.prepare("DELETE FROM wisdom_units WHERE pr_id = ?").run(prId);
1890
1913
  db.prepare("DELETE FROM pr_comments WHERE pr_id = ?").run(prId);
@@ -2075,16 +2098,17 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2075
2098
  });
2076
2099
  options.onProgress?.({ stage: "writing_code_index", repo, phase: "Writing code index" });
2077
2100
  const transaction = db.transaction(() => {
2078
- const existingChunks = db.prepare("SELECT id FROM code_chunks WHERE repo_id = ?").all(repoId);
2101
+ const existingChunkCount = db.prepare("SELECT COUNT(*) AS count FROM code_chunks WHERE repo_id = ?").get(repoId).count;
2079
2102
  const existingPatternCount = db.prepare("SELECT COUNT(*) AS count FROM architecture_patterns WHERE repo_id = ?").get(repoId).count;
2080
2103
  options.onProgress?.({
2081
2104
  stage: "deleting_existing_code_index",
2082
2105
  repo,
2083
- chunks: existingChunks.length,
2106
+ chunks: existingChunkCount,
2084
2107
  patterns: existingPatternCount
2085
2108
  });
2086
- const deleteFts = db.prepare("DELETE FROM code_chunks_fts WHERE chunkId = ?");
2087
- for (const row of existingChunks) deleteFts.run(row.id);
2109
+ db.prepare(
2110
+ "DELETE FROM code_chunks_fts WHERE chunkId IN (SELECT id FROM code_chunks WHERE repo_id = ?)"
2111
+ ).run(repoId);
2088
2112
  db.prepare("DELETE FROM code_chunks WHERE repo_id = ?").run(repoId);
2089
2113
  db.prepare("DELETE FROM code_files WHERE repo_id = ?").run(repoId);
2090
2114
  db.prepare("DELETE FROM test_links WHERE repo_id = ? AND reason != 'PR co-change'").run(repoId);
@@ -2222,9 +2246,9 @@ function replaceCodeIndex(db, repo, codeFiles, codeChunks, skippedFiles, cwd, ar
2222
2246
  };
2223
2247
  }
2224
2248
  function deleteExistingArchitectureData(db, repoId) {
2225
- const patternRows = db.prepare("SELECT id FROM architecture_patterns WHERE repo_id = ?").all(repoId);
2226
- const deleteFts = db.prepare("DELETE FROM architecture_patterns_fts WHERE patternId = ?");
2227
- for (const row of patternRows) deleteFts.run(row.id);
2249
+ db.prepare(
2250
+ "DELETE FROM architecture_patterns_fts WHERE patternId IN (SELECT id FROM architecture_patterns WHERE repo_id = ?)"
2251
+ ).run(repoId);
2228
2252
  db.prepare("DELETE FROM architecture_patterns WHERE repo_id = ?").run(repoId);
2229
2253
  db.prepare("DELETE FROM architecture_components WHERE repo_id = ?").run(repoId);
2230
2254
  db.prepare("DELETE FROM code_imports WHERE repo_id = ?").run(repoId);
@@ -2920,14 +2944,24 @@ function buildRelatedTestIndex(allPaths) {
2920
2944
  const testPaths = allPaths.filter((candidate) => isTestFilePath(candidate));
2921
2945
  const byBase = /* @__PURE__ */ new Map();
2922
2946
  const byDirectory = /* @__PURE__ */ new Map();
2947
+ const byDotPrefix = /* @__PURE__ */ new Map();
2923
2948
  for (const testPath of testPaths) {
2924
2949
  addToStringMap(byBase, testBaseFor(testPath), testPath);
2925
- const segments = path6.posix.dirname(testPath).split("/").filter(Boolean);
2926
- for (let index = 1; index <= segments.length; index += 1) {
2927
- addToStringMap(byDirectory, segments.slice(0, index).join("/"), testPath);
2950
+ const dirSegments = path6.posix.dirname(testPath).split("/").filter(Boolean);
2951
+ for (let index = 1; index <= dirSegments.length; index += 1) {
2952
+ addToStringMap(byDirectory, dirSegments.slice(0, index).join("/"), testPath);
2953
+ }
2954
+ const pathSegments2 = testPath.split("/");
2955
+ const dotPrefixes = /* @__PURE__ */ new Set();
2956
+ for (let i = 1; i < pathSegments2.length; i += 1) {
2957
+ const segment = pathSegments2[i] ?? "";
2958
+ for (let dot = segment.indexOf("."); dot >= 0; dot = segment.indexOf(".", dot + 1)) {
2959
+ dotPrefixes.add(segment.slice(0, dot));
2960
+ }
2928
2961
  }
2962
+ for (const prefix of dotPrefixes) addToStringMap(byDotPrefix, prefix, testPath);
2929
2963
  }
2930
- return { testPaths, byBase, byDirectory };
2964
+ return { testPaths, byBase, byDirectory, byDotPrefix };
2931
2965
  }
2932
2966
  function relatedTestsFor(filePath, index) {
2933
2967
  if (isTestFilePath(filePath)) return [];
@@ -2944,8 +2978,8 @@ function relatedTestsFor(filePath, index) {
2944
2978
  if (parsed.dir) {
2945
2979
  for (const testPath of index.byDirectory.get(parsed.dir) ?? []) add(testPath);
2946
2980
  }
2947
- for (const testPath of index.testPaths) {
2948
- if (testPath.includes(`/${basename}.`)) add(testPath);
2981
+ for (const testPath of index.byDotPrefix.get(basename) ?? []) {
2982
+ add(testPath);
2949
2983
  if (related.length >= 8) break;
2950
2984
  }
2951
2985
  return related.slice(0, 8);
@@ -4093,8 +4127,7 @@ function symbolMatch2(unit, querySymbols) {
4093
4127
  const lower = symbol.toLowerCase();
4094
4128
  if (unitSymbols.includes(lower)) best = Math.max(best, 1);
4095
4129
  else if (text.includes(`\`${lower}\``)) best = Math.max(best, 1);
4096
- else if (new RegExp(`\\b${escapeRegExp(lower)}\\b`, "i").test(text))
4097
- best = Math.max(best, 0.66);
4130
+ else if (symbolBoundaryRegex(lower).test(text)) best = Math.max(best, 0.66);
4098
4131
  else if (unitSymbols.some((candidate) => candidate.includes(lower) || lower.includes(candidate))) {
4099
4132
  best = Math.max(best, 0.35);
4100
4133
  }
@@ -4177,6 +4210,15 @@ function scoreUnit(unit, input, duplicateCount, repeatedEvidenceCount, freshness
4177
4210
  function escapeRegExp(value) {
4178
4211
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4179
4212
  }
4213
+ var symbolBoundaryRegexCache = /* @__PURE__ */ new Map();
4214
+ function symbolBoundaryRegex(lower) {
4215
+ let regex = symbolBoundaryRegexCache.get(lower);
4216
+ if (!regex) {
4217
+ regex = new RegExp(`\\b${escapeRegExp(lower)}\\b`, "i");
4218
+ symbolBoundaryRegexCache.set(lower, regex);
4219
+ }
4220
+ return regex;
4221
+ }
4180
4222
  function loadCandidates(db, input) {
4181
4223
  const ftsQuery = buildFtsQuery(input);
4182
4224
  const categories = "categories" in input ? input.categories ?? [] : [];
@@ -4212,9 +4254,11 @@ function loadClaimRepetitionCounts(db) {
4212
4254
  }
4213
4255
  return new Map([...grouped.entries()].map(([key, prs]) => [key, prs.size]));
4214
4256
  }
4215
- function loadFeedbackAdjustments(db) {
4216
- const rows = db.prepare("SELECT result_id, rating FROM feedback_events").all();
4257
+ function loadFeedbackAdjustments(db, resultIds) {
4217
4258
  const adjustments = /* @__PURE__ */ new Map();
4259
+ if (resultIds.length === 0) return adjustments;
4260
+ const placeholders = resultIds.map(() => "?").join(", ");
4261
+ const rows = db.prepare(`SELECT result_id, rating FROM feedback_events WHERE result_id IN (${placeholders})`).all(...resultIds);
4218
4262
  for (const row of rows) {
4219
4263
  const delta = row.rating === "useful" ? 0.03 : -0.03;
4220
4264
  adjustments.set(row.result_id, (adjustments.get(row.result_id) ?? 0) + delta);
@@ -4234,7 +4278,10 @@ function rankWisdomUnits(db, input) {
4234
4278
  const candidates = loadCandidates(db, input);
4235
4279
  const codeSnapshot = loadCurrentCodeSnapshot(db);
4236
4280
  const repetitionCounts = loadClaimRepetitionCounts(db);
4237
- const feedbackAdjustments = loadFeedbackAdjustments(db);
4281
+ const feedbackAdjustments = loadFeedbackAdjustments(
4282
+ db,
4283
+ candidates.map((unit) => unit.id)
4284
+ );
4238
4285
  const duplicates = /* @__PURE__ */ new Map();
4239
4286
  for (const unit of candidates) {
4240
4287
  const key = claimKeyFor(unit.category, unit.sanitizedText);
@@ -9896,8 +9943,9 @@ function queryTerms(input) {
9896
9943
  function matchesRepo(repo, repos) {
9897
9944
  return !repos || repos.length === 0 || repos.includes(repo);
9898
9945
  }
9899
- function rowScore(input, text, files, symbols) {
9946
+ function rowScore(input, text, files, symbols, lowerTerms) {
9900
9947
  let score = 0;
9948
+ const lowerText = text.toLowerCase();
9901
9949
  for (const file of input.files ?? []) {
9902
9950
  if (files.includes(file)) score += 5;
9903
9951
  else if (files.some((candidate) => candidate.endsWith(`/${file.split("/").pop() ?? file}`)))
@@ -9905,10 +9953,10 @@ function rowScore(input, text, files, symbols) {
9905
9953
  }
9906
9954
  for (const symbol of input.symbols ?? []) {
9907
9955
  if (symbols.includes(symbol)) score += 4;
9908
- else if (text.toLowerCase().includes(symbol.toLowerCase())) score += 1;
9956
+ else if (lowerText.includes(symbol.toLowerCase())) score += 1;
9909
9957
  }
9910
- for (const term of queryTerms(input)) {
9911
- if (text.toLowerCase().includes(term.toLowerCase())) score += 0.5;
9958
+ for (const term of lowerTerms) {
9959
+ if (lowerText.includes(term)) score += 0.5;
9912
9960
  }
9913
9961
  return score;
9914
9962
  }
@@ -9919,9 +9967,10 @@ function getWisdom(db, input, limit) {
9919
9967
  ORDER BY confidence DESC, created_at DESC
9920
9968
  LIMIT 500`
9921
9969
  ).all();
9970
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9922
9971
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9923
9972
  row,
9924
- score: rowScore(input, row.sanitized_text, parseStringArray(row.file_paths_json), [])
9973
+ score: rowScore(input, row.sanitized_text, parseStringArray(row.file_paths_json), [], lowerTerms)
9925
9974
  })).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);
9926
9975
  }
9927
9976
  function getCodeEvidence(db, input, limit) {
@@ -9931,13 +9980,15 @@ function getCodeEvidence(db, input, limit) {
9931
9980
  ORDER BY updated_at DESC
9932
9981
  LIMIT 800`
9933
9982
  ).all();
9983
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9934
9984
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9935
9985
  row,
9936
9986
  score: rowScore(
9937
9987
  input,
9938
9988
  row.sanitized_text,
9939
9989
  [row.file_path],
9940
- parseStringArray(row.symbols_json)
9990
+ parseStringArray(row.symbols_json),
9991
+ lowerTerms
9941
9992
  )
9942
9993
  })).filter((item) => item.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((item) => item.row);
9943
9994
  }
@@ -9948,23 +9999,30 @@ function getArchitecture(db, input, limit) {
9948
9999
  ORDER BY confidence DESC, created_at DESC
9949
10000
  LIMIT 300`
9950
10001
  ).all();
10002
+ const lowerTerms = queryTerms(input).map((term) => term.toLowerCase());
9951
10003
  return rows.filter((row) => matchesRepo(row.repo, input.repos)).map((row) => ({
9952
10004
  row,
9953
- score: rowScore(input, row.summary_sanitized, parseStringArray(row.source_files_json), [])
10005
+ score: rowScore(
10006
+ input,
10007
+ row.summary_sanitized,
10008
+ parseStringArray(row.source_files_json),
10009
+ [],
10010
+ lowerTerms
10011
+ )
9954
10012
  })).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);
9955
10013
  }
9956
10014
  function findOrgApiConsumers(db, config, input) {
9957
10015
  initializeSchema(db);
10016
+ const repoClause = input.repo ? " AND (provider_repo = ? OR consumer_repo = ?)" : "";
10017
+ const repoParams = input.repo ? [input.repo, input.repo] : [];
9958
10018
  const rows = db.prepare(
9959
10019
  `SELECT provider_repo, provider_path, consumer_repo, consumer_path, contract, evidence_json, confidence
9960
10020
  FROM org_api_consumers
9961
- WHERE org = ?
10021
+ WHERE org = ?${repoClause}
9962
10022
  ORDER BY confidence DESC`
9963
- ).all(config.org);
10023
+ ).all(config.org, ...repoParams);
9964
10024
  const limit = Math.max(1, Math.min(input.maxResults ?? 8, 25));
9965
- return rows.filter(
9966
- (row) => !input.repo || row.provider_repo === input.repo || row.consumer_repo === input.repo
9967
- ).filter((row) => {
10025
+ return rows.filter((row) => {
9968
10026
  const files = input.files ?? [];
9969
10027
  if (files.length === 0 && !input.query) return true;
9970
10028
  return files.some((file) => row.provider_path === file || row.consumer_path === file) || Boolean(input.query && row.contract.toLowerCase().includes(input.query.toLowerCase()));