@fiale-plus/repo-arch 0.1.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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/cache.d.ts +19 -0
- package/dist/cache.js +84 -0
- package/dist/cache.js.map +1 -0
- package/dist/cards.d.ts +39 -0
- package/dist/cards.js +210 -0
- package/dist/cards.js.map +1 -0
- package/dist/check-diff.d.ts +22 -0
- package/dist/check-diff.js +156 -0
- package/dist/check-diff.js.map +1 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.js +410 -0
- package/dist/cli.js.map +1 -0
- package/dist/context-pack.d.ts +40 -0
- package/dist/context-pack.js +49 -0
- package/dist/context-pack.js.map +1 -0
- package/dist/embedder.d.ts +37 -0
- package/dist/embedder.js +84 -0
- package/dist/embedder.js.map +1 -0
- package/dist/eval.d.ts +36 -0
- package/dist/eval.js +175 -0
- package/dist/eval.js.map +1 -0
- package/dist/git-history.d.ts +41 -0
- package/dist/git-history.js +115 -0
- package/dist/git-history.js.map +1 -0
- package/dist/review.d.ts +12 -0
- package/dist/review.js +46 -0
- package/dist/review.js.map +1 -0
- package/dist/signals.d.ts +23 -0
- package/dist/signals.js +216 -0
- package/dist/signals.js.map +1 -0
- package/dist/similar.d.ts +20 -0
- package/dist/similar.js +51 -0
- package/dist/similar.js.map +1 -0
- package/dist/staleness.d.ts +28 -0
- package/dist/staleness.js +110 -0
- package/dist/staleness.js.map +1 -0
- package/dist/training.d.ts +53 -0
- package/dist/training.js +221 -0
- package/dist/training.js.map +1 -0
- package/dist/why.d.ts +38 -0
- package/dist/why.js +131 -0
- package/dist/why.js.map +1 -0
- package/package.json +42 -0
package/dist/embedder.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { resolveRepoRoot, getHeadSha } from './git-history.js';
|
|
5
|
+
export const DEFAULT_MODEL = 'Xenova/all-MiniLM-L6-v2';
|
|
6
|
+
export const EMBEDDING_DIM = 384;
|
|
7
|
+
let extractor = null;
|
|
8
|
+
function ensureCacheDir(repoRoot) {
|
|
9
|
+
const dir = path.join(repoRoot, '.repo-arch', 'index');
|
|
10
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
11
|
+
return dir;
|
|
12
|
+
}
|
|
13
|
+
function indexFilePath(repoRoot) {
|
|
14
|
+
return path.join(ensureCacheDir(repoRoot), 'vectors.json');
|
|
15
|
+
}
|
|
16
|
+
function cacheKey(repoRoot, headSha) {
|
|
17
|
+
return crypto.createHash('sha256').update(JSON.stringify({ repoRoot, headSha, model: DEFAULT_MODEL })).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
export async function loadExtractor(config = {}) {
|
|
20
|
+
if (extractor)
|
|
21
|
+
return extractor;
|
|
22
|
+
try {
|
|
23
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
24
|
+
extractor = await pipeline('feature-extraction', config.model ?? DEFAULT_MODEL);
|
|
25
|
+
return extractor;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new Error(`Failed to load embedding model. Install with: npm install @huggingface/transformers\n ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function embed(text, config = {}) {
|
|
32
|
+
const pipe = await loadExtractor(config);
|
|
33
|
+
const result = await pipe(text, { pooling: 'mean', normalize: true });
|
|
34
|
+
return Array.from(result.data);
|
|
35
|
+
}
|
|
36
|
+
export function cosineSimilarity(a, b) {
|
|
37
|
+
let dot = 0;
|
|
38
|
+
for (let i = 0; i < a.length; i++)
|
|
39
|
+
dot += a[i] * b[i];
|
|
40
|
+
return dot;
|
|
41
|
+
}
|
|
42
|
+
export function loadIndex(repoRoot) {
|
|
43
|
+
const filePath = indexFilePath(repoRoot);
|
|
44
|
+
if (!fs.existsSync(filePath))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function saveIndex(repoRoot, index) {
|
|
54
|
+
fs.writeFileSync(indexFilePath(repoRoot), JSON.stringify(index, null, 2), 'utf8');
|
|
55
|
+
}
|
|
56
|
+
export async function buildIndex(entries, options = {}) {
|
|
57
|
+
const repoRoot = resolveRepoRoot(options.repoPath);
|
|
58
|
+
const headSha = getHeadSha(repoRoot);
|
|
59
|
+
const pipe = await loadExtractor(options);
|
|
60
|
+
const vectorEntries = [];
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
const embedding = await embed(entry.text, options);
|
|
63
|
+
vectorEntries.push({ ...entry, embedding });
|
|
64
|
+
}
|
|
65
|
+
const index = {
|
|
66
|
+
model: options.model ?? DEFAULT_MODEL,
|
|
67
|
+
dim: EMBEDDING_DIM,
|
|
68
|
+
headSha,
|
|
69
|
+
entries: vectorEntries,
|
|
70
|
+
createdAt: new Date().toISOString(),
|
|
71
|
+
};
|
|
72
|
+
saveIndex(repoRoot, index);
|
|
73
|
+
return index;
|
|
74
|
+
}
|
|
75
|
+
export function searchIndex(index, queryEmbedding, topK = 5) {
|
|
76
|
+
const scored = index.entries.map(entry => ({
|
|
77
|
+
entry,
|
|
78
|
+
score: cosineSimilarity(queryEmbedding, entry.embedding),
|
|
79
|
+
}));
|
|
80
|
+
return scored
|
|
81
|
+
.sort((a, b) => b.score - a.score)
|
|
82
|
+
.slice(0, topK);
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=embedder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedder.js","sourceRoot":"","sources":["../src/embedder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAG,yBAAyB,CAAC;AACvD,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAsBjC,IAAI,SAAS,GAAQ,IAAI,CAAC;AAE1B,SAAS,cAAc,CAAC,QAAgB;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACvD,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB,EAAE,OAAe;IACjD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAyB,EAAE;IAC7D,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAChC,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;QAC/D,SAAS,GAAG,MAAM,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC;QAChF,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,0FAA0F,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACnJ,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAY,EAAE,SAAyB,EAAE;IACnE,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAa,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAgB,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAkB;IAC5D,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAoG,EACpG,UAAiD,EAAE;IAEnD,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,aAAa,GAAkB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACnD,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,KAAK,GAAgB;QACzB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,aAAa;QACrC,GAAG,EAAE,aAAa;QAClB,OAAO;QACP,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IAEF,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,KAAkB,EAClB,cAAwB,EACxB,OAAe,CAAC;IAEhB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzC,KAAK;QACL,KAAK,EAAE,gBAAgB,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC;KACzD,CAAC,CAAC,CAAC;IAEJ,OAAO,MAAM;SACV,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;SACjC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACpB,CAAC"}
|
package/dist/eval.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type InsightCard } from './cards.js';
|
|
2
|
+
export type EvalQuery = {
|
|
3
|
+
id: string;
|
|
4
|
+
query: string;
|
|
5
|
+
expectedCardId: string;
|
|
6
|
+
expectedTitle: string;
|
|
7
|
+
};
|
|
8
|
+
export type StrategyResult = {
|
|
9
|
+
strategy: string;
|
|
10
|
+
hits: number;
|
|
11
|
+
total: number;
|
|
12
|
+
hitRate: number;
|
|
13
|
+
results: {
|
|
14
|
+
queryId: string;
|
|
15
|
+
query: string;
|
|
16
|
+
expectedTitle: string;
|
|
17
|
+
found: boolean;
|
|
18
|
+
rank: number | null;
|
|
19
|
+
score: number | null;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
export type EvalReport = {
|
|
23
|
+
repoRoot: string;
|
|
24
|
+
headSha: string;
|
|
25
|
+
cardsTotal: number;
|
|
26
|
+
acceptedCards: number;
|
|
27
|
+
queries: number;
|
|
28
|
+
strategies: StrategyResult[];
|
|
29
|
+
bestStrategy: string;
|
|
30
|
+
timestamp: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function generateQueries(cards: InsightCard[]): EvalQuery[];
|
|
33
|
+
export declare function runEval(options?: {
|
|
34
|
+
repoPath?: string;
|
|
35
|
+
}): Promise<EvalReport>;
|
|
36
|
+
export declare function formatEval(report: EvalReport): string;
|
package/dist/eval.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { resolveRepoRoot, getHeadSha } from './git-history.js';
|
|
2
|
+
import { mineHistory } from './git-history.js';
|
|
3
|
+
import { classifyHistory } from './signals.js';
|
|
4
|
+
import { generateCards } from './cards.js';
|
|
5
|
+
import { getStatusOverrideMap } from './review.js';
|
|
6
|
+
import { cachedOrGenerate } from './cache.js';
|
|
7
|
+
import { loadIndex, buildIndex, searchIndex, embed } from './embedder.js';
|
|
8
|
+
export function generateQueries(cards) {
|
|
9
|
+
const queries = [];
|
|
10
|
+
for (const card of cards) {
|
|
11
|
+
if (card.status !== 'accepted')
|
|
12
|
+
continue;
|
|
13
|
+
// Query from title
|
|
14
|
+
queries.push({
|
|
15
|
+
id: `${card.id}-title`,
|
|
16
|
+
query: card.title,
|
|
17
|
+
expectedCardId: card.id,
|
|
18
|
+
expectedTitle: card.title,
|
|
19
|
+
});
|
|
20
|
+
// Query from suggestion (truncated to key phrase)
|
|
21
|
+
const short = card.suggestion.replace(/\.(?:\s|$).*/, '').trim();
|
|
22
|
+
if (short && short !== card.title) {
|
|
23
|
+
queries.push({
|
|
24
|
+
id: `${card.id}-suggestion`,
|
|
25
|
+
query: short,
|
|
26
|
+
expectedCardId: card.id,
|
|
27
|
+
expectedTitle: card.title,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return queries;
|
|
32
|
+
}
|
|
33
|
+
function keywordSearch(query, cards, topK = 5) {
|
|
34
|
+
const terms = query.toLowerCase().split(/\s+/).filter(t => t.length > 2);
|
|
35
|
+
const scored = cards.map(card => {
|
|
36
|
+
const haystack = `${card.title} ${card.suggestion} ${card.affectedFiles.join(' ')}`.toLowerCase();
|
|
37
|
+
let score = 0;
|
|
38
|
+
for (const term of terms) {
|
|
39
|
+
if (haystack.includes(term))
|
|
40
|
+
score += 1;
|
|
41
|
+
}
|
|
42
|
+
return { card, score: score / terms.length };
|
|
43
|
+
});
|
|
44
|
+
return scored.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
45
|
+
}
|
|
46
|
+
export async function runEval(options = {}) {
|
|
47
|
+
const repoRoot = resolveRepoRoot(options.repoPath);
|
|
48
|
+
const headSha = getHeadSha(repoRoot);
|
|
49
|
+
// Get cards with review state
|
|
50
|
+
const generateFn = () => {
|
|
51
|
+
const history = mineHistory({ repoPath: options.repoPath });
|
|
52
|
+
const classified = classifyHistory(history.records);
|
|
53
|
+
return generateCards(classified, {}, getStatusOverrideMap(repoRoot));
|
|
54
|
+
};
|
|
55
|
+
const { cards } = cachedOrGenerate(repoRoot, generateFn);
|
|
56
|
+
const acceptedCards = cards.filter(c => c.status === 'accepted');
|
|
57
|
+
const queries = generateQueries(cards);
|
|
58
|
+
if (acceptedCards.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
repoRoot,
|
|
61
|
+
headSha: headSha.slice(0, 12),
|
|
62
|
+
cardsTotal: cards.length,
|
|
63
|
+
acceptedCards: 0,
|
|
64
|
+
queries: 0,
|
|
65
|
+
strategies: [],
|
|
66
|
+
bestStrategy: 'none',
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const strategyResults = [];
|
|
71
|
+
// Strategy 1: Keyword search
|
|
72
|
+
{
|
|
73
|
+
const results = [];
|
|
74
|
+
for (const q of queries) {
|
|
75
|
+
const topResults = keywordSearch(q.query, cards, 5);
|
|
76
|
+
const foundIdx = topResults.findIndex(r => r.card.id === q.expectedCardId);
|
|
77
|
+
results.push({
|
|
78
|
+
queryId: q.id,
|
|
79
|
+
query: q.query,
|
|
80
|
+
expectedTitle: q.expectedTitle,
|
|
81
|
+
found: foundIdx >= 0,
|
|
82
|
+
rank: foundIdx >= 0 ? foundIdx + 1 : null,
|
|
83
|
+
score: foundIdx >= 0 ? topResults[foundIdx].score : null,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const hits = results.filter(r => r.found).length;
|
|
87
|
+
strategyResults.push({
|
|
88
|
+
strategy: 'keyword',
|
|
89
|
+
hits,
|
|
90
|
+
total: results.length,
|
|
91
|
+
hitRate: parseFloat((hits / results.length).toFixed(3)),
|
|
92
|
+
results,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Strategy 2: Embedding search
|
|
96
|
+
{
|
|
97
|
+
// Build or load embedding index
|
|
98
|
+
let index = loadIndex(repoRoot);
|
|
99
|
+
if (!index || index.headSha !== headSha) {
|
|
100
|
+
const entries = cards.map(card => ({
|
|
101
|
+
id: card.id,
|
|
102
|
+
text: `${card.title}. ${card.suggestion} ${card.supportingCommits.map(c => c.subject).join('. ')}`,
|
|
103
|
+
source: 'card',
|
|
104
|
+
metadata: { type: card.type, confidence: String(card.confidence), status: card.status },
|
|
105
|
+
}));
|
|
106
|
+
index = await buildIndex(entries, { repoPath: options.repoPath });
|
|
107
|
+
}
|
|
108
|
+
const results = [];
|
|
109
|
+
for (const q of queries) {
|
|
110
|
+
const queryEmbedding = await embed(q.query);
|
|
111
|
+
const topResults = searchIndex(index, queryEmbedding, 5);
|
|
112
|
+
const foundIdx = topResults.findIndex(r => r.entry.id === q.expectedCardId);
|
|
113
|
+
results.push({
|
|
114
|
+
queryId: q.id,
|
|
115
|
+
query: q.query,
|
|
116
|
+
expectedTitle: q.expectedTitle,
|
|
117
|
+
found: foundIdx >= 0,
|
|
118
|
+
rank: foundIdx >= 0 ? foundIdx + 1 : null,
|
|
119
|
+
score: foundIdx >= 0 ? topResults[foundIdx].score : null,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const hits = results.filter(r => r.found).length;
|
|
123
|
+
strategyResults.push({
|
|
124
|
+
strategy: 'embedding',
|
|
125
|
+
hits,
|
|
126
|
+
total: results.length,
|
|
127
|
+
hitRate: parseFloat((hits / results.length).toFixed(3)),
|
|
128
|
+
results,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Determine best strategy
|
|
132
|
+
const sorted = [...strategyResults].sort((a, b) => b.hitRate - a.hitRate);
|
|
133
|
+
const bestStrategy = sorted.length > 0 ? sorted[0].strategy : 'none';
|
|
134
|
+
return {
|
|
135
|
+
repoRoot,
|
|
136
|
+
headSha: headSha.slice(0, 12),
|
|
137
|
+
cardsTotal: cards.length,
|
|
138
|
+
acceptedCards: acceptedCards.length,
|
|
139
|
+
queries: queries.length,
|
|
140
|
+
strategies: strategyResults,
|
|
141
|
+
bestStrategy,
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function formatEval(report) {
|
|
146
|
+
const lines = [];
|
|
147
|
+
if (report.acceptedCards === 0) {
|
|
148
|
+
lines.push(`\n No accepted cards to evaluate.`);
|
|
149
|
+
lines.push(` Use "repo-arch accept <card-id>" on some cards first.\n`);
|
|
150
|
+
return lines.join('\n');
|
|
151
|
+
}
|
|
152
|
+
lines.push(`\n Eval benchmark for ${report.repoRoot}`);
|
|
153
|
+
lines.push(` ${report.headSha} | ${report.cardsTotal} cards (${report.acceptedCards} accepted, ${report.queries} queries)\n`);
|
|
154
|
+
for (const strategy of report.strategies) {
|
|
155
|
+
const rate = (strategy.hitRate * 100).toFixed(1);
|
|
156
|
+
const bar = '█'.repeat(Math.round(strategy.hitRate * 20));
|
|
157
|
+
const empty = '░'.repeat(20 - Math.round(strategy.hitRate * 20));
|
|
158
|
+
lines.push(` ${strategy.strategy.padEnd(12)} ${bar}${empty} ${rate}% (${strategy.hits}/${strategy.total})`);
|
|
159
|
+
}
|
|
160
|
+
lines.push(`\n Best strategy: ${report.bestStrategy}\n`);
|
|
161
|
+
// Show misses for worst strategy
|
|
162
|
+
const worst = [...report.strategies].sort((a, b) => a.hitRate - b.hitRate)[0];
|
|
163
|
+
if (worst) {
|
|
164
|
+
const misses = worst.results.filter(r => !r.found).slice(0, 5);
|
|
165
|
+
if (misses.length > 0) {
|
|
166
|
+
lines.push(` Missed queries (${worst.strategy}):`);
|
|
167
|
+
for (const m of misses) {
|
|
168
|
+
lines.push(` · "${m.query}" — expected "${m.expectedTitle.slice(0, 60)}"`);
|
|
169
|
+
}
|
|
170
|
+
lines.push('');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return lines.join('\n');
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=eval.js.map
|
package/dist/eval.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval.js","sourceRoot":"","sources":["../src/eval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type GitFileChange = {
|
|
2
|
+
status: string;
|
|
3
|
+
path: string;
|
|
4
|
+
oldPath?: string;
|
|
5
|
+
};
|
|
6
|
+
export type GitHistoryRecord = {
|
|
7
|
+
sha: string;
|
|
8
|
+
parents: string[];
|
|
9
|
+
author: {
|
|
10
|
+
name: string;
|
|
11
|
+
email: string;
|
|
12
|
+
};
|
|
13
|
+
authoredAt: string;
|
|
14
|
+
subject: string;
|
|
15
|
+
files: GitFileChange[];
|
|
16
|
+
paths: string[];
|
|
17
|
+
};
|
|
18
|
+
export type MineHistoryOptions = {
|
|
19
|
+
repoPath?: string;
|
|
20
|
+
outPath?: string;
|
|
21
|
+
};
|
|
22
|
+
export type MineHistoryResult = {
|
|
23
|
+
repoRoot: string;
|
|
24
|
+
headSha: string;
|
|
25
|
+
cacheHit: boolean;
|
|
26
|
+
cacheFile: string;
|
|
27
|
+
count: number;
|
|
28
|
+
records: GitHistoryRecord[];
|
|
29
|
+
jsonl: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function runGit(repoPath: string, args: string[]): string;
|
|
32
|
+
export declare function resolveRepoRoot(repoPath?: string): string;
|
|
33
|
+
export declare function getHeadSha(repoRoot: string): string;
|
|
34
|
+
export declare function cacheKeyFor({ repoRoot, headSha, version }: {
|
|
35
|
+
repoRoot: string;
|
|
36
|
+
headSha: string;
|
|
37
|
+
version?: number;
|
|
38
|
+
}): string;
|
|
39
|
+
export declare function cacheFileFor(repoRoot: string, cacheKey: string): string;
|
|
40
|
+
export declare function parseGitHistory(logOutput: string): GitHistoryRecord[];
|
|
41
|
+
export declare function mineHistory({ repoPath, outPath }?: MineHistoryOptions): MineHistoryResult;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
export function runGit(repoPath, args) {
|
|
6
|
+
return execFileSync('git', ['-C', repoPath, ...args], { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024 });
|
|
7
|
+
}
|
|
8
|
+
export function resolveRepoRoot(repoPath = process.cwd()) {
|
|
9
|
+
const resolved = path.resolve(repoPath);
|
|
10
|
+
const root = runGit(resolved, ['rev-parse', '--show-toplevel']).trim();
|
|
11
|
+
if (!root)
|
|
12
|
+
throw new Error(`Unable to resolve git repo root from ${resolved}`);
|
|
13
|
+
return root;
|
|
14
|
+
}
|
|
15
|
+
export function getHeadSha(repoRoot) {
|
|
16
|
+
return runGit(repoRoot, ['rev-parse', 'HEAD']).trim();
|
|
17
|
+
}
|
|
18
|
+
export function cacheKeyFor({ repoRoot, headSha, version = 1 }) {
|
|
19
|
+
return crypto
|
|
20
|
+
.createHash('sha256')
|
|
21
|
+
.update(JSON.stringify({ repoRoot, headSha, version }))
|
|
22
|
+
.digest('hex');
|
|
23
|
+
}
|
|
24
|
+
export function cacheFileFor(repoRoot, cacheKey) {
|
|
25
|
+
return path.join(repoRoot, '.repo-arch', 'cache', `history-${cacheKey}.jsonl`);
|
|
26
|
+
}
|
|
27
|
+
function ensureDir(filePath) {
|
|
28
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
export function parseGitHistory(logOutput) {
|
|
31
|
+
const records = [];
|
|
32
|
+
let current = null;
|
|
33
|
+
const pushCurrent = () => {
|
|
34
|
+
if (!current)
|
|
35
|
+
return;
|
|
36
|
+
current.paths = current.files.map(file => file.path);
|
|
37
|
+
records.push(current);
|
|
38
|
+
current = null;
|
|
39
|
+
};
|
|
40
|
+
for (const rawLine of logOutput.split(/\r?\n/)) {
|
|
41
|
+
if (!rawLine)
|
|
42
|
+
continue;
|
|
43
|
+
if (rawLine.startsWith('@@@')) {
|
|
44
|
+
pushCurrent();
|
|
45
|
+
const meta = rawLine.slice(3).split('\x1f');
|
|
46
|
+
const [sha, parentsStr = '', authorName = '', authorEmail = '', authoredAt = '', subject = ''] = meta;
|
|
47
|
+
current = {
|
|
48
|
+
sha,
|
|
49
|
+
parents: parentsStr ? parentsStr.split(' ').filter(Boolean) : [],
|
|
50
|
+
author: { name: authorName, email: authorEmail },
|
|
51
|
+
authoredAt,
|
|
52
|
+
subject,
|
|
53
|
+
files: [],
|
|
54
|
+
paths: []
|
|
55
|
+
};
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (!current)
|
|
59
|
+
continue;
|
|
60
|
+
const parts = rawLine.split('\t');
|
|
61
|
+
const status = parts[0] || '';
|
|
62
|
+
if (!status)
|
|
63
|
+
continue;
|
|
64
|
+
if ((status.startsWith('R') || status.startsWith('C')) && parts.length >= 3) {
|
|
65
|
+
current.files.push({ status, path: parts[2], oldPath: parts[1] });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
current.files.push({ status, path: parts[1] || '' });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
pushCurrent();
|
|
72
|
+
return records;
|
|
73
|
+
}
|
|
74
|
+
export function mineHistory({ repoPath, outPath } = {}) {
|
|
75
|
+
const repoRoot = resolveRepoRoot(repoPath);
|
|
76
|
+
const headSha = getHeadSha(repoRoot);
|
|
77
|
+
const cacheKey = cacheKeyFor({ repoRoot, headSha, version: 1 });
|
|
78
|
+
const cacheFile = cacheFileFor(repoRoot, cacheKey);
|
|
79
|
+
let cacheHit = false;
|
|
80
|
+
let records;
|
|
81
|
+
if (fs.existsSync(cacheFile)) {
|
|
82
|
+
cacheHit = true;
|
|
83
|
+
const cached = fs.readFileSync(cacheFile, 'utf8');
|
|
84
|
+
records = cached.trim() ? cached.trim().split(/\r?\n/).map(line => JSON.parse(line)) : [];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const logOutput = runGit(repoRoot, [
|
|
88
|
+
'log',
|
|
89
|
+
'--reverse',
|
|
90
|
+
'--date=iso-strict',
|
|
91
|
+
'--format=@@@%H%x1f%P%x1f%an%x1f%ae%x1f%ad%x1f%s',
|
|
92
|
+
'--name-status',
|
|
93
|
+
'--find-renames=50%'
|
|
94
|
+
]);
|
|
95
|
+
records = parseGitHistory(logOutput);
|
|
96
|
+
const jsonl = records.map(record => JSON.stringify(record)).join('\n') + (records.length ? '\n' : '');
|
|
97
|
+
ensureDir(cacheFile);
|
|
98
|
+
fs.writeFileSync(cacheFile, jsonl, 'utf8');
|
|
99
|
+
}
|
|
100
|
+
const jsonl = records.map(record => JSON.stringify(record)).join('\n') + (records.length ? '\n' : '');
|
|
101
|
+
if (outPath) {
|
|
102
|
+
fs.mkdirSync(path.dirname(path.resolve(outPath)), { recursive: true });
|
|
103
|
+
fs.writeFileSync(outPath, jsonl, 'utf8');
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
repoRoot,
|
|
107
|
+
headSha,
|
|
108
|
+
cacheHit,
|
|
109
|
+
cacheFile,
|
|
110
|
+
count: records.length,
|
|
111
|
+
records,
|
|
112
|
+
jsonl
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=git-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-history.js","sourceRoot":"","sources":["../src/git-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAiClD,MAAM,UAAU,MAAM,CAAC,QAAgB,EAAE,IAAc;IACrD,OAAO,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC;AAC3G,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvE,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,EAAE,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,CAAC,EAA2D;IACrH,OAAO,MAAM;SACV,UAAU,CAAC,QAAQ,CAAC;SACpB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;SACtD,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,QAAgB;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,QAAQ,QAAQ,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,OAAO,GAA4B,IAAI,CAAC;IAE5C,MAAM,WAAW,GAAG,GAAS,EAAE;QAC7B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,WAAW,EAAE,CAAC;YACd,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,WAAW,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;YACtG,OAAO,GAAG;gBACR,GAAG;gBACH,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;gBAChE,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE;gBAChD,UAAU;gBACV,OAAO;gBACP,KAAK,EAAE,EAAE;gBACT,KAAK,EAAE,EAAE;aACV,CAAC;YACF,SAAS;QACX,CAAC;QAED,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM;YAAE,SAAS;QAEtB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC5E,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,WAAW,EAAE,CAAC;IACd,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,KAAyB,EAAE;IACxE,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEnD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAA2B,CAAC;IAEhC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAClD,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChH,CAAC;SAAM,CAAC;QACN,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,EAAE;YACjC,KAAK;YACL,WAAW;YACX,mBAAmB;YACnB,iDAAiD;YACjD,eAAe;YACf,oBAAoB;SACrB,CAAC,CAAC;QACH,OAAO,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtG,IAAI,OAAO,EAAE,CAAC;QACZ,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO;QACL,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,SAAS;QACT,KAAK,EAAE,OAAO,CAAC,MAAM;QACrB,OAAO;QACP,KAAK;KACN,CAAC;AACJ,CAAC"}
|
package/dist/review.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CardStatus } from './cards.js';
|
|
2
|
+
export type ReviewEntry = {
|
|
3
|
+
status: CardStatus;
|
|
4
|
+
updatedAt: string;
|
|
5
|
+
};
|
|
6
|
+
export type ReviewMap = Record<string, ReviewEntry>;
|
|
7
|
+
export declare function loadReviewState(repoRoot: string): ReviewMap;
|
|
8
|
+
export declare function saveReviewState(repoRoot: string, state: ReviewMap): void;
|
|
9
|
+
export declare function setCardStatus(repoRoot: string, cardId: string, status: CardStatus): ReviewEntry | null;
|
|
10
|
+
export declare function getCardStatus(repoRoot: string, cardId: string): ReviewEntry | null;
|
|
11
|
+
export declare function listReviewState(repoRoot: string): ReviewMap;
|
|
12
|
+
export declare function getStatusOverrideMap(repoRoot: string): Record<string, CardStatus>;
|
package/dist/review.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
const REVIEW_FILE = '.repo-arch/review-state.json';
|
|
4
|
+
function reviewFilePath(repoRoot) {
|
|
5
|
+
return path.join(repoRoot, REVIEW_FILE);
|
|
6
|
+
}
|
|
7
|
+
export function loadReviewState(repoRoot) {
|
|
8
|
+
const filePath = reviewFilePath(repoRoot);
|
|
9
|
+
if (!fs.existsSync(filePath))
|
|
10
|
+
return {};
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function saveReviewState(repoRoot, state) {
|
|
19
|
+
const filePath = reviewFilePath(repoRoot);
|
|
20
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
21
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
22
|
+
}
|
|
23
|
+
export function setCardStatus(repoRoot, cardId, status) {
|
|
24
|
+
const state = loadReviewState(repoRoot);
|
|
25
|
+
state[cardId] = { status, updatedAt: new Date().toISOString() };
|
|
26
|
+
saveReviewState(repoRoot, state);
|
|
27
|
+
return state[cardId];
|
|
28
|
+
}
|
|
29
|
+
export function getCardStatus(repoRoot, cardId) {
|
|
30
|
+
const state = loadReviewState(repoRoot);
|
|
31
|
+
return state[cardId] ?? null;
|
|
32
|
+
}
|
|
33
|
+
export function listReviewState(repoRoot) {
|
|
34
|
+
return loadReviewState(repoRoot);
|
|
35
|
+
}
|
|
36
|
+
export function getStatusOverrideMap(repoRoot) {
|
|
37
|
+
const state = loadReviewState(repoRoot);
|
|
38
|
+
const overrides = {};
|
|
39
|
+
for (const [id, entry] of Object.entries(state)) {
|
|
40
|
+
if (entry.status === 'accepted' || entry.status === 'rejected') {
|
|
41
|
+
overrides[id] = entry.status;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return overrides;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAUlC,MAAM,WAAW,GAAG,8BAA8B,CAAC;AAEnD,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAc,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,KAAgB;IAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC1C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc,EAAE,MAAkB;IAChF,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACxC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAChE,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc;IAC5D,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,SAAS,GAA+B,EAAE,CAAC;IACjD,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/D,SAAS,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type GitHistoryRecord } from './git-history.js';
|
|
2
|
+
export type SignalDef = {
|
|
3
|
+
type: string;
|
|
4
|
+
label: string;
|
|
5
|
+
/** Regex patterns matched against commit subject */
|
|
6
|
+
subjectPatterns: RegExp[];
|
|
7
|
+
/** File-path patterns checked when subject match is weak */
|
|
8
|
+
filePatterns?: RegExp[];
|
|
9
|
+
/** Base confidence (0-1). Higher = stronger signal */
|
|
10
|
+
baseConfidence: number;
|
|
11
|
+
};
|
|
12
|
+
export type CommitSignal = {
|
|
13
|
+
type: string;
|
|
14
|
+
label: string;
|
|
15
|
+
confidence: number;
|
|
16
|
+
matchedOn: 'subject' | 'file';
|
|
17
|
+
};
|
|
18
|
+
export type ClassifiedCommit = GitHistoryRecord & {
|
|
19
|
+
signals: CommitSignal[];
|
|
20
|
+
};
|
|
21
|
+
export declare const SIGNAL_DEFS: SignalDef[];
|
|
22
|
+
export declare function classifyCommit(record: GitHistoryRecord, defs?: SignalDef[]): ClassifiedCommit;
|
|
23
|
+
export declare function classifyHistory(records: GitHistoryRecord[], defs?: SignalDef[]): ClassifiedCommit[];
|