@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 +91 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/db/schema.sql +14 -0
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
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
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
|
|
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:
|
|
2106
|
+
chunks: existingChunkCount,
|
|
2084
2107
|
patterns: existingPatternCount
|
|
2085
2108
|
});
|
|
2086
|
-
|
|
2087
|
-
|
|
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
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
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
|
|
2926
|
-
for (let index = 1; index <=
|
|
2927
|
-
addToStringMap(byDirectory,
|
|
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.
|
|
2948
|
-
|
|
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 (
|
|
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(
|
|
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 (
|
|
9956
|
+
else if (lowerText.includes(symbol.toLowerCase())) score += 1;
|
|
9909
9957
|
}
|
|
9910
|
-
for (const term of
|
|
9911
|
-
if (
|
|
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(
|
|
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()));
|