kiri-mcp-server 0.13.0 → 0.16.0
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/README.md +13 -17
- package/config/scoring-profiles.yml +78 -0
- package/config/stop-words.yml +307 -0
- package/dist/config/scoring-profiles.yml +78 -0
- package/dist/config/stop-words.yml +307 -0
- package/dist/package.json +2 -2
- package/dist/src/indexer/cli.d.ts +1 -0
- package/dist/src/indexer/cli.d.ts.map +1 -1
- package/dist/src/indexer/cli.js +22 -2
- package/dist/src/indexer/cli.js.map +1 -1
- package/dist/src/indexer/cochange.d.ts +97 -0
- package/dist/src/indexer/cochange.d.ts.map +1 -0
- package/dist/src/indexer/cochange.js +315 -0
- package/dist/src/indexer/cochange.js.map +1 -0
- package/dist/src/indexer/graph-metrics.d.ts +68 -0
- package/dist/src/indexer/graph-metrics.d.ts.map +1 -0
- package/dist/src/indexer/graph-metrics.js +239 -0
- package/dist/src/indexer/graph-metrics.js.map +1 -0
- package/dist/src/indexer/schema.d.ts +15 -0
- package/dist/src/indexer/schema.d.ts.map +1 -1
- package/dist/src/indexer/schema.js +86 -0
- package/dist/src/indexer/schema.js.map +1 -1
- package/dist/src/server/config.d.ts.map +1 -1
- package/dist/src/server/config.js +22 -16
- package/dist/src/server/config.js.map +1 -1
- package/dist/src/server/handlers/snippets-get.d.ts +10 -0
- package/dist/src/server/handlers/snippets-get.d.ts.map +1 -1
- package/dist/src/server/handlers/snippets-get.js +40 -3
- package/dist/src/server/handlers/snippets-get.js.map +1 -1
- package/dist/src/server/handlers.d.ts +1 -1
- package/dist/src/server/handlers.d.ts.map +1 -1
- package/dist/src/server/handlers.js +195 -51
- package/dist/src/server/handlers.js.map +1 -1
- package/dist/src/server/idf-provider.d.ts +110 -0
- package/dist/src/server/idf-provider.d.ts.map +1 -0
- package/dist/src/server/idf-provider.js +233 -0
- package/dist/src/server/idf-provider.js.map +1 -0
- package/dist/src/server/rpc.d.ts.map +1 -1
- package/dist/src/server/rpc.js +43 -4
- package/dist/src/server/rpc.js.map +1 -1
- package/dist/src/server/scoring.d.ts +10 -0
- package/dist/src/server/scoring.d.ts.map +1 -1
- package/dist/src/server/scoring.js +73 -0
- package/dist/src/server/scoring.js.map +1 -1
- package/dist/src/server/services/index.d.ts +2 -0
- package/dist/src/server/services/index.d.ts.map +1 -1
- package/dist/src/server/services/index.js +3 -0
- package/dist/src/server/services/index.js.map +1 -1
- package/dist/src/server/stop-words.d.ts +106 -0
- package/dist/src/server/stop-words.d.ts.map +1 -0
- package/dist/src/server/stop-words.js +312 -0
- package/dist/src/server/stop-words.js.map +1 -0
- package/dist/src/shared/adaptive-k-categories.d.ts +5 -0
- package/dist/src/shared/adaptive-k-categories.d.ts.map +1 -0
- package/dist/src/shared/adaptive-k-categories.js +17 -0
- package/dist/src/shared/adaptive-k-categories.js.map +1 -0
- package/dist/src/shared/duckdb.d.ts +8 -2
- package/dist/src/shared/duckdb.d.ts.map +1 -1
- package/dist/src/shared/duckdb.js +37 -62
- package/dist/src/shared/duckdb.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Co-change Graph Extraction (Phase 4: Graph Layer)
|
|
3
|
+
*
|
|
4
|
+
* This module extracts co-change relationships from git commit history.
|
|
5
|
+
* Files that frequently change together in commits have higher co-change scores.
|
|
6
|
+
*
|
|
7
|
+
* Invariants (from Alloy/TLA+ specs):
|
|
8
|
+
* - CC1: Canonical ordering (file1 < file2 lexicographically)
|
|
9
|
+
* - CC2: Both files exist in the file table
|
|
10
|
+
* - CC3: Positive weight (cochange_count > 0)
|
|
11
|
+
* - CC4: Idempotent commit processing
|
|
12
|
+
*
|
|
13
|
+
* Metrics computed:
|
|
14
|
+
* - cochange_count: Number of commits where both files changed together
|
|
15
|
+
* - confidence: Jaccard similarity = |A ∩ B| / |A ∪ B|
|
|
16
|
+
*/
|
|
17
|
+
import type { DuckDBClient } from "../shared/duckdb.js";
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for co-change extraction
|
|
20
|
+
*/
|
|
21
|
+
export interface CochangeConfig {
|
|
22
|
+
/** Maximum number of commits to analyze (default: 1000) */
|
|
23
|
+
maxCommits: number;
|
|
24
|
+
/** Minimum co-change count to persist (default: 2) */
|
|
25
|
+
minCochangeCount: number;
|
|
26
|
+
/** Maximum files per commit to avoid mega-commits (default: 50) */
|
|
27
|
+
maxFilesPerCommit: number;
|
|
28
|
+
/** Only consider commits within this many days (default: 365) */
|
|
29
|
+
maxAgeDays: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parsed commit from git log
|
|
33
|
+
*/
|
|
34
|
+
interface GitCommit {
|
|
35
|
+
hash: string;
|
|
36
|
+
files: string[];
|
|
37
|
+
timestamp: Date;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create canonical pair ensuring file1 < file2 (CC1)
|
|
41
|
+
* Uses lexicographic comparison for deterministic ordering.
|
|
42
|
+
*/
|
|
43
|
+
export declare function canonicalPair(file1: string, file2: string): [string, string];
|
|
44
|
+
/**
|
|
45
|
+
* Generate all unique pairs from a set of files.
|
|
46
|
+
* Returns pairs in canonical order (file1 < file2).
|
|
47
|
+
*/
|
|
48
|
+
export declare function generatePairs(files: string[]): [string, string][];
|
|
49
|
+
/**
|
|
50
|
+
* Extract commits from git log.
|
|
51
|
+
* Returns commits with their changed files.
|
|
52
|
+
*/
|
|
53
|
+
export declare function extractCommitsFromGit(repoPath: string, config: CochangeConfig): GitCommit[];
|
|
54
|
+
/**
|
|
55
|
+
* Compute co-change graph from git history.
|
|
56
|
+
* Main entry point for co-change extraction.
|
|
57
|
+
*
|
|
58
|
+
* @param db - DuckDB client
|
|
59
|
+
* @param repoId - Repository ID
|
|
60
|
+
* @param repoPath - Path to git repository
|
|
61
|
+
* @param config - Optional configuration
|
|
62
|
+
*/
|
|
63
|
+
export declare function computeCochangeGraph(db: DuckDBClient, repoId: number, repoPath: string, config?: Partial<CochangeConfig>): Promise<{
|
|
64
|
+
processedCommits: number;
|
|
65
|
+
edges: number;
|
|
66
|
+
prunedEdges: number;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Incremental co-change update for new commits.
|
|
70
|
+
* Called during watch mode or incremental indexing.
|
|
71
|
+
*
|
|
72
|
+
* @param db - DuckDB client
|
|
73
|
+
* @param repoId - Repository ID
|
|
74
|
+
* @param repoPath - Path to git repository
|
|
75
|
+
* @param sinceCommit - Only process commits after this one
|
|
76
|
+
*/
|
|
77
|
+
export declare function incrementalCochangeUpdate(db: DuckDBClient, repoId: number, repoPath: string, sinceCommit?: string): Promise<{
|
|
78
|
+
processedCommits: number;
|
|
79
|
+
}>;
|
|
80
|
+
/**
|
|
81
|
+
* Remove co-change edges involving a deleted file.
|
|
82
|
+
* Called when files are removed from the index.
|
|
83
|
+
*
|
|
84
|
+
* Maintains CC2 invariant (both files exist).
|
|
85
|
+
*/
|
|
86
|
+
export declare function removeFileCochange(db: DuckDBClient, repoId: number, filePath: string): Promise<number>;
|
|
87
|
+
/**
|
|
88
|
+
* Get co-change neighbors for a file.
|
|
89
|
+
* Returns files that frequently change together with the target file.
|
|
90
|
+
*/
|
|
91
|
+
export declare function getCochangeNeighbors(db: DuckDBClient, repoId: number, filePath: string, limit?: number): Promise<{
|
|
92
|
+
path: string;
|
|
93
|
+
count: number;
|
|
94
|
+
confidence: number;
|
|
95
|
+
}[]>;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=cochange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cochange.d.ts","sourceRoot":"","sources":["../../../src/indexer/cochange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,gBAAgB,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;CACpB;AASD;;GAEG;AACH,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAE5E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAejE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,SAAS,EAAE,CA2D3F;AA8ID;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CA4D3E;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC,CA+CvC;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAK,GACT,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAehE"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Co-change Graph Extraction (Phase 4: Graph Layer)
|
|
3
|
+
*
|
|
4
|
+
* This module extracts co-change relationships from git commit history.
|
|
5
|
+
* Files that frequently change together in commits have higher co-change scores.
|
|
6
|
+
*
|
|
7
|
+
* Invariants (from Alloy/TLA+ specs):
|
|
8
|
+
* - CC1: Canonical ordering (file1 < file2 lexicographically)
|
|
9
|
+
* - CC2: Both files exist in the file table
|
|
10
|
+
* - CC3: Positive weight (cochange_count > 0)
|
|
11
|
+
* - CC4: Idempotent commit processing
|
|
12
|
+
*
|
|
13
|
+
* Metrics computed:
|
|
14
|
+
* - cochange_count: Number of commits where both files changed together
|
|
15
|
+
* - confidence: Jaccard similarity = |A ∩ B| / |A ∪ B|
|
|
16
|
+
*/
|
|
17
|
+
import { execSync } from "node:child_process";
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
maxCommits: 1000,
|
|
20
|
+
minCochangeCount: 2,
|
|
21
|
+
maxFilesPerCommit: 50,
|
|
22
|
+
maxAgeDays: 365,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Create canonical pair ensuring file1 < file2 (CC1)
|
|
26
|
+
* Uses lexicographic comparison for deterministic ordering.
|
|
27
|
+
*/
|
|
28
|
+
export function canonicalPair(file1, file2) {
|
|
29
|
+
return file1 < file2 ? [file1, file2] : [file2, file1];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generate all unique pairs from a set of files.
|
|
33
|
+
* Returns pairs in canonical order (file1 < file2).
|
|
34
|
+
*/
|
|
35
|
+
export function generatePairs(files) {
|
|
36
|
+
const pairs = [];
|
|
37
|
+
const sorted = [...files].sort();
|
|
38
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
39
|
+
for (let j = i + 1; j < sorted.length; j++) {
|
|
40
|
+
const f1 = sorted[i];
|
|
41
|
+
const f2 = sorted[j];
|
|
42
|
+
if (f1 !== undefined && f2 !== undefined) {
|
|
43
|
+
pairs.push([f1, f2]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return pairs;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract commits from git log.
|
|
51
|
+
* Returns commits with their changed files.
|
|
52
|
+
*/
|
|
53
|
+
export function extractCommitsFromGit(repoPath, config) {
|
|
54
|
+
const commits = [];
|
|
55
|
+
try {
|
|
56
|
+
// Get commit hashes with timestamps
|
|
57
|
+
// Format: hash|timestamp (ISO 8601)
|
|
58
|
+
const sinceDate = new Date();
|
|
59
|
+
sinceDate.setDate(sinceDate.getDate() - config.maxAgeDays);
|
|
60
|
+
const sinceArg = sinceDate.toISOString().split("T")[0];
|
|
61
|
+
const logOutput = execSync(`git log --format="%H|%aI" --since="${sinceArg}" -n ${config.maxCommits}`, {
|
|
62
|
+
cwd: repoPath,
|
|
63
|
+
encoding: "utf-8",
|
|
64
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large repos
|
|
65
|
+
});
|
|
66
|
+
const commitLines = logOutput.trim().split("\n").filter(Boolean);
|
|
67
|
+
for (const line of commitLines) {
|
|
68
|
+
const [hash, timestampStr] = line.split("|");
|
|
69
|
+
if (!hash || !timestampStr)
|
|
70
|
+
continue;
|
|
71
|
+
// Get files changed in this commit
|
|
72
|
+
const filesOutput = execSync(`git diff-tree --no-commit-id --name-only -r ${hash}`, {
|
|
73
|
+
cwd: repoPath,
|
|
74
|
+
encoding: "utf-8",
|
|
75
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
76
|
+
});
|
|
77
|
+
const files = filesOutput
|
|
78
|
+
.trim()
|
|
79
|
+
.split("\n")
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.filter((f) => !f.startsWith(".git/"));
|
|
82
|
+
// Skip mega-commits (likely auto-generated or initial commits)
|
|
83
|
+
if (files.length > config.maxFilesPerCommit) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// Skip single-file commits (no pairs possible)
|
|
87
|
+
if (files.length < 2) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
commits.push({
|
|
91
|
+
hash,
|
|
92
|
+
files,
|
|
93
|
+
timestamp: new Date(timestampStr),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.warn("[Cochange] Failed to extract git history:", error);
|
|
99
|
+
}
|
|
100
|
+
return commits;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if a commit has already been processed (CC4: Idempotent)
|
|
104
|
+
*/
|
|
105
|
+
async function isCommitProcessed(db, repoId, commitHash) {
|
|
106
|
+
const result = await db.all(`SELECT COUNT(*) as count FROM processed_commits
|
|
107
|
+
WHERE repo_id = ? AND commit_hash = ?`, [repoId, commitHash]);
|
|
108
|
+
return (result[0]?.count ?? 0) > 0;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Mark a commit as processed (CC4: Idempotent)
|
|
112
|
+
*/
|
|
113
|
+
async function markCommitProcessed(db, repoId, commitHash) {
|
|
114
|
+
await db.run(`INSERT INTO processed_commits (repo_id, commit_hash, processed_at)
|
|
115
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
116
|
+
ON CONFLICT DO NOTHING`, [repoId, commitHash]);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get set of indexed files for a repo (CC2: Both files exist)
|
|
120
|
+
*/
|
|
121
|
+
async function getIndexedFiles(db, repoId) {
|
|
122
|
+
const files = await db.all(`SELECT path FROM file WHERE repo_id = ?`, [repoId]);
|
|
123
|
+
return new Set(files.map((f) => f.path));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Update co-change counts for a single commit.
|
|
127
|
+
* Creates pairs for all changed files and increments counts.
|
|
128
|
+
*/
|
|
129
|
+
async function processCommitCochange(db, repoId, commit, indexedFiles, fileChangeCounts) {
|
|
130
|
+
// Filter to only indexed files (CC2)
|
|
131
|
+
const validFiles = commit.files.filter((f) => indexedFiles.has(f));
|
|
132
|
+
if (validFiles.length < 2) {
|
|
133
|
+
return; // Need at least 2 files for pairs
|
|
134
|
+
}
|
|
135
|
+
// Update file change counts
|
|
136
|
+
for (const file of validFiles) {
|
|
137
|
+
fileChangeCounts.set(file, (fileChangeCounts.get(file) ?? 0) + 1);
|
|
138
|
+
}
|
|
139
|
+
// Generate all pairs (CC1: canonical ordering)
|
|
140
|
+
const pairs = generatePairs(validFiles);
|
|
141
|
+
// Update co-change counts
|
|
142
|
+
for (const [file1, file2] of pairs) {
|
|
143
|
+
await db.run(`INSERT INTO cochange (repo_id, file1, file2, cochange_count, last_commit, last_cochange_at)
|
|
144
|
+
VALUES (?, ?, ?, 1, ?, CURRENT_TIMESTAMP)
|
|
145
|
+
ON CONFLICT (repo_id, file1, file2) DO UPDATE SET
|
|
146
|
+
cochange_count = cochange_count + 1,
|
|
147
|
+
last_commit = excluded.last_commit,
|
|
148
|
+
last_cochange_at = excluded.last_cochange_at`, [repoId, file1, file2, commit.hash]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Calculate Jaccard confidence for all co-change edges.
|
|
153
|
+
* confidence = cochange_count / (changes_file1 + changes_file2 - cochange_count)
|
|
154
|
+
*
|
|
155
|
+
* This is run after all commits are processed.
|
|
156
|
+
*/
|
|
157
|
+
async function updateJaccardConfidence(db, repoId, fileChangeCounts) {
|
|
158
|
+
// Get all co-change edges
|
|
159
|
+
const edges = await db.all(`SELECT file1, file2, cochange_count FROM cochange WHERE repo_id = ?`, [repoId]);
|
|
160
|
+
for (const edge of edges) {
|
|
161
|
+
const count1 = fileChangeCounts.get(edge.file1) ?? 0;
|
|
162
|
+
const count2 = fileChangeCounts.get(edge.file2) ?? 0;
|
|
163
|
+
// Jaccard = intersection / union
|
|
164
|
+
// intersection = cochange_count
|
|
165
|
+
// union = changes_file1 + changes_file2 - cochange_count
|
|
166
|
+
const union = count1 + count2 - edge.cochange_count;
|
|
167
|
+
const confidence = union > 0 ? edge.cochange_count / union : 0;
|
|
168
|
+
await db.run(`UPDATE cochange SET confidence = ?
|
|
169
|
+
WHERE repo_id = ? AND file1 = ? AND file2 = ?`, [confidence, repoId, edge.file1, edge.file2]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Prune co-change edges below minimum threshold (CC3: Positive weight)
|
|
174
|
+
*/
|
|
175
|
+
async function pruneWeakEdges(db, repoId, minCochangeCount) {
|
|
176
|
+
// Count rows to be deleted first
|
|
177
|
+
const countResult = await db.all(`SELECT COUNT(*) as count FROM cochange
|
|
178
|
+
WHERE repo_id = ? AND cochange_count < ?`, [repoId, minCochangeCount]);
|
|
179
|
+
const toDelete = countResult[0]?.count ?? 0;
|
|
180
|
+
// Delete the rows
|
|
181
|
+
await db.run(`DELETE FROM cochange
|
|
182
|
+
WHERE repo_id = ? AND cochange_count < ?`, [repoId, minCochangeCount]);
|
|
183
|
+
return toDelete;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Compute co-change graph from git history.
|
|
187
|
+
* Main entry point for co-change extraction.
|
|
188
|
+
*
|
|
189
|
+
* @param db - DuckDB client
|
|
190
|
+
* @param repoId - Repository ID
|
|
191
|
+
* @param repoPath - Path to git repository
|
|
192
|
+
* @param config - Optional configuration
|
|
193
|
+
*/
|
|
194
|
+
export async function computeCochangeGraph(db, repoId, repoPath, config = {}) {
|
|
195
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
196
|
+
console.info(`[Cochange] Computing co-change graph for repo ${repoId}...`);
|
|
197
|
+
// Get indexed files (CC2)
|
|
198
|
+
const indexedFiles = await getIndexedFiles(db, repoId);
|
|
199
|
+
if (indexedFiles.size === 0) {
|
|
200
|
+
console.info("[Cochange] No indexed files, skipping");
|
|
201
|
+
return { processedCommits: 0, edges: 0, prunedEdges: 0 };
|
|
202
|
+
}
|
|
203
|
+
// Extract commits from git
|
|
204
|
+
const commits = extractCommitsFromGit(repoPath, cfg);
|
|
205
|
+
console.info(`[Cochange] Found ${commits.length} commits to analyze`);
|
|
206
|
+
// Track file change counts for Jaccard calculation
|
|
207
|
+
const fileChangeCounts = new Map();
|
|
208
|
+
let processedCount = 0;
|
|
209
|
+
// Process each commit
|
|
210
|
+
for (const commit of commits) {
|
|
211
|
+
// Check idempotency (CC4)
|
|
212
|
+
const alreadyProcessed = await isCommitProcessed(db, repoId, commit.hash);
|
|
213
|
+
if (alreadyProcessed) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
// Process co-change pairs
|
|
217
|
+
await processCommitCochange(db, repoId, commit, indexedFiles, fileChangeCounts);
|
|
218
|
+
// Mark as processed (CC4)
|
|
219
|
+
await markCommitProcessed(db, repoId, commit.hash);
|
|
220
|
+
processedCount++;
|
|
221
|
+
}
|
|
222
|
+
console.info(`[Cochange] Processed ${processedCount} new commits`);
|
|
223
|
+
// Update Jaccard confidence scores only if we processed new commits
|
|
224
|
+
// Skip when processedCount is 0 to avoid zeroing existing confidence values
|
|
225
|
+
if (processedCount > 0) {
|
|
226
|
+
await updateJaccardConfidence(db, repoId, fileChangeCounts);
|
|
227
|
+
}
|
|
228
|
+
// Prune weak edges (CC3)
|
|
229
|
+
const prunedCount = await pruneWeakEdges(db, repoId, cfg.minCochangeCount);
|
|
230
|
+
// Get final edge count
|
|
231
|
+
const edgeCount = await db.all(`SELECT COUNT(*) as count FROM cochange WHERE repo_id = ?`, [repoId]);
|
|
232
|
+
console.info(`[Cochange] Completed: ${edgeCount[0]?.count ?? 0} edges, ${prunedCount} pruned`);
|
|
233
|
+
return {
|
|
234
|
+
processedCommits: processedCount,
|
|
235
|
+
edges: edgeCount[0]?.count ?? 0,
|
|
236
|
+
prunedEdges: prunedCount,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Incremental co-change update for new commits.
|
|
241
|
+
* Called during watch mode or incremental indexing.
|
|
242
|
+
*
|
|
243
|
+
* @param db - DuckDB client
|
|
244
|
+
* @param repoId - Repository ID
|
|
245
|
+
* @param repoPath - Path to git repository
|
|
246
|
+
* @param sinceCommit - Only process commits after this one
|
|
247
|
+
*/
|
|
248
|
+
export async function incrementalCochangeUpdate(db, repoId, repoPath, sinceCommit) {
|
|
249
|
+
console.info(`[Cochange] Incremental update for repo ${repoId}...`);
|
|
250
|
+
const indexedFiles = await getIndexedFiles(db, repoId);
|
|
251
|
+
const fileChangeCounts = new Map();
|
|
252
|
+
// Build file change counts from existing data
|
|
253
|
+
const existingEdges = await db.all(`SELECT DISTINCT file1, file2 FROM cochange WHERE repo_id = ?`, [repoId]);
|
|
254
|
+
// Note: This is a simplified approximation. Full recalc would be more accurate.
|
|
255
|
+
for (const edge of existingEdges) {
|
|
256
|
+
fileChangeCounts.set(edge.file1, (fileChangeCounts.get(edge.file1) ?? 0) + 1);
|
|
257
|
+
fileChangeCounts.set(edge.file2, (fileChangeCounts.get(edge.file2) ?? 0) + 1);
|
|
258
|
+
}
|
|
259
|
+
// Extract recent commits
|
|
260
|
+
const cfg = { ...DEFAULT_CONFIG, maxCommits: 100, maxAgeDays: 30 };
|
|
261
|
+
const commits = extractCommitsFromGit(repoPath, cfg);
|
|
262
|
+
let processedCount = 0;
|
|
263
|
+
for (const commit of commits) {
|
|
264
|
+
// Skip if already processed
|
|
265
|
+
if (await isCommitProcessed(db, repoId, commit.hash)) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
// Skip if before sinceCommit (optimization)
|
|
269
|
+
if (sinceCommit && commit.hash === sinceCommit) {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
await processCommitCochange(db, repoId, commit, indexedFiles, fileChangeCounts);
|
|
273
|
+
await markCommitProcessed(db, repoId, commit.hash);
|
|
274
|
+
processedCount++;
|
|
275
|
+
}
|
|
276
|
+
// Update confidence scores
|
|
277
|
+
if (processedCount > 0) {
|
|
278
|
+
await updateJaccardConfidence(db, repoId, fileChangeCounts);
|
|
279
|
+
}
|
|
280
|
+
console.info(`[Cochange] Incremental update: ${processedCount} commits`);
|
|
281
|
+
return { processedCommits: processedCount };
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Remove co-change edges involving a deleted file.
|
|
285
|
+
* Called when files are removed from the index.
|
|
286
|
+
*
|
|
287
|
+
* Maintains CC2 invariant (both files exist).
|
|
288
|
+
*/
|
|
289
|
+
export async function removeFileCochange(db, repoId, filePath) {
|
|
290
|
+
// Count rows to be deleted first
|
|
291
|
+
const countResult = await db.all(`SELECT COUNT(*) as count FROM cochange
|
|
292
|
+
WHERE repo_id = ? AND (file1 = ? OR file2 = ?)`, [repoId, filePath, filePath]);
|
|
293
|
+
const toDelete = countResult[0]?.count ?? 0;
|
|
294
|
+
// Delete the rows
|
|
295
|
+
await db.run(`DELETE FROM cochange
|
|
296
|
+
WHERE repo_id = ? AND (file1 = ? OR file2 = ?)`, [repoId, filePath, filePath]);
|
|
297
|
+
return toDelete;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get co-change neighbors for a file.
|
|
301
|
+
* Returns files that frequently change together with the target file.
|
|
302
|
+
*/
|
|
303
|
+
export async function getCochangeNeighbors(db, repoId, filePath, limit = 10) {
|
|
304
|
+
// Query both directions (file can be in file1 or file2)
|
|
305
|
+
const results = await db.all(`SELECT
|
|
306
|
+
CASE WHEN file1 = ? THEN file2 ELSE file1 END as path,
|
|
307
|
+
cochange_count as count,
|
|
308
|
+
confidence
|
|
309
|
+
FROM cochange
|
|
310
|
+
WHERE repo_id = ? AND (file1 = ? OR file2 = ?)
|
|
311
|
+
ORDER BY cochange_count DESC, confidence DESC
|
|
312
|
+
LIMIT ?`, [filePath, repoId, filePath, filePath, limit]);
|
|
313
|
+
return results;
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=cochange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cochange.js","sourceRoot":"","sources":["../../../src/indexer/cochange.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Metrics Computation (Phase 3.2: Graph Layer)
|
|
3
|
+
*
|
|
4
|
+
* This module computes graph centrality metrics from the dependency table
|
|
5
|
+
* and stores them in precomputed tables for fast query-time lookup.
|
|
6
|
+
*
|
|
7
|
+
* Invariants (from Alloy spec):
|
|
8
|
+
* - DG1: All path edges reference indexed files
|
|
9
|
+
* - DG2: No self-loops
|
|
10
|
+
* - DG4: Closure symmetry (outbound/inbound consistency)
|
|
11
|
+
*
|
|
12
|
+
* Metrics computed:
|
|
13
|
+
* - Degree centrality (inbound/outbound counts)
|
|
14
|
+
* - Importance score (PageRank-like)
|
|
15
|
+
* - Inbound closure (BFS up to depth 3)
|
|
16
|
+
*/
|
|
17
|
+
import type { DuckDBClient } from "../shared/duckdb.js";
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for graph metrics computation
|
|
20
|
+
*/
|
|
21
|
+
export interface GraphMetricsConfig {
|
|
22
|
+
/** Maximum BFS depth for inbound closure (default: 3) */
|
|
23
|
+
maxDepth: number;
|
|
24
|
+
/** PageRank damping factor (default: 0.85) */
|
|
25
|
+
dampingFactor: number;
|
|
26
|
+
/** Number of PageRank iterations (default: 10) */
|
|
27
|
+
iterations: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Compute all graph metrics for a repository.
|
|
31
|
+
* Called after dependency extraction during indexing.
|
|
32
|
+
*
|
|
33
|
+
* @param db - DuckDB client
|
|
34
|
+
* @param repoId - Repository ID
|
|
35
|
+
* @param config - Optional configuration
|
|
36
|
+
*/
|
|
37
|
+
export declare function computeGraphMetrics(db: DuckDBClient, repoId: number, config?: Partial<GraphMetricsConfig>): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Compute degree centrality (inbound and outbound counts).
|
|
40
|
+
*
|
|
41
|
+
* Uses SQL aggregation for efficiency.
|
|
42
|
+
*/
|
|
43
|
+
export declare function computeDegreeCentrality(db: DuckDBClient, repoId: number): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Precompute inbound closure using BFS up to maxDepth.
|
|
46
|
+
*
|
|
47
|
+
* Uses recursive CTE for efficient computation.
|
|
48
|
+
*/
|
|
49
|
+
export declare function precomputeInboundClosure(db: DuckDBClient, repoId: number, maxDepth: number): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Compute importance scores using PageRank-like algorithm.
|
|
52
|
+
*
|
|
53
|
+
* This is a simplified PageRank that:
|
|
54
|
+
* - Uses inbound edges for importance propagation
|
|
55
|
+
* - Runs for a fixed number of iterations
|
|
56
|
+
* - Normalizes scores to [0, 1]
|
|
57
|
+
*/
|
|
58
|
+
export declare function computeImportanceScores(db: DuckDBClient, repoId: number, dampingFactor: number, iterations: number): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Incrementally update graph metrics for changed files.
|
|
61
|
+
* Used during incremental indexing to avoid full recomputation.
|
|
62
|
+
*
|
|
63
|
+
* @param db - DuckDB client
|
|
64
|
+
* @param repoId - Repository ID
|
|
65
|
+
* @param changedPaths - Paths that were added, modified, or removed
|
|
66
|
+
*/
|
|
67
|
+
export declare function incrementalGraphUpdate(db: DuckDBClient, repoId: number, changedPaths: string[]): Promise<void>;
|
|
68
|
+
//# sourceMappingURL=graph-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-metrics.d.ts","sourceRoot":"","sources":["../../../src/indexer/graph-metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;CACpB;AAWD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,CAAC,kBAAkB,CAAM,GACvC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0D7F;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAgFf;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|