@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fiale-plus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Repo-Arch
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@fiale-plus/repo-arch)
|
|
4
|
+
[](https://www.npmjs.com/package/@fiale-plus/repo-arch)
|
|
5
|
+
[](https://github.com/fiale-plus/repo-arch/actions/workflows/ci.yml)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Local project-memory engine for git history.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @fiale-plus/repo-arch
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Repo-Arch mines repository history, classifies commit signals, builds cards, explains files, warns on diffs, checks staleness, runs similarity search, prepares evals, and generates training data.
|
|
15
|
+
|
|
16
|
+
## CLI-first
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
repo-arch mine-history --repo .
|
|
20
|
+
repo-arch classify --repo .
|
|
21
|
+
repo-arch cards --repo .
|
|
22
|
+
repo-arch why src/core.ts --json
|
|
23
|
+
repo-arch check-diff --base main --json
|
|
24
|
+
repo-arch check-stale --json
|
|
25
|
+
repo-arch index
|
|
26
|
+
repo-arch similar "why auth middleware token-only?" --json
|
|
27
|
+
repo-arch eval
|
|
28
|
+
repo-arch dataset
|
|
29
|
+
repo-arch train
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Docs
|
|
33
|
+
|
|
34
|
+
- [docs/vision.md](./docs/vision.md)
|
|
35
|
+
- [docs/usage.md](./docs/usage.md)
|
|
36
|
+
- [docs/release-flow.md](./docs/release-flow.md)
|
|
37
|
+
- [docs/roadmap.md](./docs/roadmap.md)
|
|
38
|
+
- [docs/embeddings.md](./docs/embeddings.md)
|
|
39
|
+
|
|
40
|
+
CLI is the primary interface. Optional adapters come later.
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { InsightCard } from './cards.js';
|
|
2
|
+
export declare const CACHE_VERSION = 1;
|
|
3
|
+
export type CardCacheEntry = {
|
|
4
|
+
version: number;
|
|
5
|
+
headSha: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
cards: InsightCard[];
|
|
8
|
+
};
|
|
9
|
+
export declare function getCachedCards(repoRoot: string, headSha: string): CardCacheEntry | null;
|
|
10
|
+
export declare function writeCachedCards(repoRoot: string, headSha: string, cards: InsightCard[]): void;
|
|
11
|
+
export declare function invalidateCache(repoRoot: string): number;
|
|
12
|
+
export declare function cacheHeadFor(repoRoot: string, options?: {
|
|
13
|
+
repoPath?: string;
|
|
14
|
+
}): string;
|
|
15
|
+
export declare function cachedOrGenerate(repoRoot: string, generate: () => InsightCard[]): {
|
|
16
|
+
cards: InsightCard[];
|
|
17
|
+
cacheHit: boolean;
|
|
18
|
+
headSha: string;
|
|
19
|
+
};
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { getHeadSha } from './git-history.js';
|
|
4
|
+
export const CACHE_VERSION = 1;
|
|
5
|
+
function cacheDir(repoRoot) {
|
|
6
|
+
return path.join(repoRoot, '.repo-arch', 'cache', 'cards');
|
|
7
|
+
}
|
|
8
|
+
function cacheFilePath(repoRoot, headSha) {
|
|
9
|
+
return path.join(cacheDir(repoRoot), `${headSha}.json`);
|
|
10
|
+
}
|
|
11
|
+
export function getCachedCards(repoRoot, headSha) {
|
|
12
|
+
const filePath = cacheFilePath(repoRoot, headSha);
|
|
13
|
+
if (!fs.existsSync(filePath))
|
|
14
|
+
return null;
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
17
|
+
const entry = JSON.parse(raw);
|
|
18
|
+
if (entry.version !== CACHE_VERSION)
|
|
19
|
+
return null;
|
|
20
|
+
if (entry.headSha !== headSha)
|
|
21
|
+
return null;
|
|
22
|
+
return entry;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function writeCachedCards(repoRoot, headSha, cards) {
|
|
29
|
+
const dir = cacheDir(repoRoot);
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
// Remove stale cache files for other HEADs to prevent bloat
|
|
32
|
+
cleanupStaleCaches(dir, headSha);
|
|
33
|
+
const entry = {
|
|
34
|
+
version: CACHE_VERSION,
|
|
35
|
+
headSha,
|
|
36
|
+
createdAt: new Date().toISOString(),
|
|
37
|
+
cards,
|
|
38
|
+
};
|
|
39
|
+
fs.writeFileSync(cacheFilePath(repoRoot, headSha), JSON.stringify(entry, null, 2), 'utf8');
|
|
40
|
+
}
|
|
41
|
+
export function invalidateCache(repoRoot) {
|
|
42
|
+
const dir = cacheDir(repoRoot);
|
|
43
|
+
if (!fs.existsSync(dir))
|
|
44
|
+
return 0;
|
|
45
|
+
let removed = 0;
|
|
46
|
+
for (const file of fs.readdirSync(dir)) {
|
|
47
|
+
if (file.endsWith('.json')) {
|
|
48
|
+
fs.rmSync(path.join(dir, file));
|
|
49
|
+
removed++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return removed;
|
|
53
|
+
}
|
|
54
|
+
function cleanupStaleCaches(dir, keepHead, maxFiles = 10) {
|
|
55
|
+
if (!fs.existsSync(dir))
|
|
56
|
+
return;
|
|
57
|
+
const files = fs.readdirSync(dir)
|
|
58
|
+
.filter(f => f.endsWith('.json'))
|
|
59
|
+
.map(f => ({
|
|
60
|
+
name: f,
|
|
61
|
+
head: f.replace(/\.json$/, ''),
|
|
62
|
+
mtime: fs.statSync(path.join(dir, f)).mtimeMs,
|
|
63
|
+
}))
|
|
64
|
+
.sort((a, b) => b.mtime - a.mtime); // newest first
|
|
65
|
+
// Keep the current HEAD's cache and up to maxFiles-1 others
|
|
66
|
+
const toRemove = files.filter(f => f.head !== keepHead).slice(maxFiles - 1);
|
|
67
|
+
for (const f of toRemove) {
|
|
68
|
+
fs.rmSync(path.join(dir, f.name));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function cacheHeadFor(repoRoot, options = {}) {
|
|
72
|
+
return getHeadSha(repoRoot);
|
|
73
|
+
}
|
|
74
|
+
export function cachedOrGenerate(repoRoot, generate) {
|
|
75
|
+
const headSha = cacheHeadFor(repoRoot);
|
|
76
|
+
const cached = getCachedCards(repoRoot, headSha);
|
|
77
|
+
if (cached) {
|
|
78
|
+
return { cards: cached.cards, cacheHit: true, headSha };
|
|
79
|
+
}
|
|
80
|
+
const cards = generate();
|
|
81
|
+
writeCachedCards(repoRoot, headSha, cards);
|
|
82
|
+
return { cards, cacheHit: false, headSha };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAS/B,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,OAAe;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAe;IAC9D,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QAChD,IAAI,KAAK,CAAC,OAAO,KAAK,aAAa;YAAE,OAAO,IAAI,CAAC;QACjD,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAE,KAAoB;IACtF,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/B,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvC,4DAA4D;IAC5D,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAEjC,MAAM,KAAK,GAAmB;QAC5B,OAAO,EAAE,aAAa;QACtB,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,KAAK;KACN,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAElC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YAChC,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAE,QAAgB,EAAE,WAAmB,EAAE;IAC9E,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO;IAEhC,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC;SAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACT,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;QAC9B,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO;KAC9C,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe;IAErD,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC5E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,UAAiC,EAAE;IAChF,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAAgB,EAChB,QAA6B;IAE7B,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEjD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3C,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC"}
|
package/dist/cards.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ClassifiedCommit } from './signals.js';
|
|
2
|
+
export type CardType = 'churn-hotspot' | 'repeated-fix' | 'rationale-cluster' | 'test-gap' | 'revert-pattern' | 'co-change';
|
|
3
|
+
export type CardStatus = 'pending' | 'accepted' | 'rejected' | 'stale' | 'superseded';
|
|
4
|
+
export type InsightCard = {
|
|
5
|
+
id: string;
|
|
6
|
+
type: CardType;
|
|
7
|
+
title: string;
|
|
8
|
+
confidence: number;
|
|
9
|
+
status: CardStatus;
|
|
10
|
+
supportingCommits: {
|
|
11
|
+
sha: string;
|
|
12
|
+
subject: string;
|
|
13
|
+
}[];
|
|
14
|
+
affectedFiles: string[];
|
|
15
|
+
suggestion: string;
|
|
16
|
+
};
|
|
17
|
+
export type CardsOptions = {
|
|
18
|
+
repoPath?: string;
|
|
19
|
+
outPath?: string;
|
|
20
|
+
minConfidence?: number;
|
|
21
|
+
maxCards?: number;
|
|
22
|
+
};
|
|
23
|
+
export type CardsResult = {
|
|
24
|
+
repoRoot: string;
|
|
25
|
+
headSha: string;
|
|
26
|
+
count: number;
|
|
27
|
+
cards: InsightCard[];
|
|
28
|
+
jsonl: string;
|
|
29
|
+
};
|
|
30
|
+
export declare function cardIdFrom(type: CardType, title: string, affectedFiles: string[]): string;
|
|
31
|
+
export declare const CARD_GENERATORS: Array<{
|
|
32
|
+
type: CardType;
|
|
33
|
+
label: string;
|
|
34
|
+
run: (records: ClassifiedCommit[]) => Omit<InsightCard, 'id' | 'status'>[];
|
|
35
|
+
}>;
|
|
36
|
+
export declare function generateCards(classifiedRecords: ClassifiedCommit[], options?: {
|
|
37
|
+
minConfidence?: number;
|
|
38
|
+
maxCards?: number;
|
|
39
|
+
}, statusOverrides?: Record<string, CardStatus>): InsightCard[];
|
package/dist/cards.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
export function cardIdFrom(type, title, affectedFiles) {
|
|
3
|
+
const raw = JSON.stringify({ type, title, files: [...affectedFiles].sort() });
|
|
4
|
+
return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
|
|
5
|
+
}
|
|
6
|
+
function toCard(data, statusOverride) {
|
|
7
|
+
return {
|
|
8
|
+
...data,
|
|
9
|
+
id: cardIdFrom(data.type, data.title, data.affectedFiles),
|
|
10
|
+
status: statusOverride ?? 'pending',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
// ─── Generators ────────────────────────────────────────────
|
|
14
|
+
function churnHotspotCards(records) {
|
|
15
|
+
const fileCount = new Map();
|
|
16
|
+
for (const record of records) {
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
for (const file of record.files) {
|
|
19
|
+
if (!seen.has(file.path)) {
|
|
20
|
+
seen.add(file.path);
|
|
21
|
+
fileCount.set(file.path, (fileCount.get(file.path) ?? 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const sorted = [...fileCount.entries()].sort((a, b) => b[1] - a[1]);
|
|
26
|
+
const threshold = Math.max(2, Math.round(records.length * 0.08));
|
|
27
|
+
const hotspots = sorted.filter(([, count]) => count >= threshold).slice(0, 5);
|
|
28
|
+
return hotspots.map(([file, count]) => ({
|
|
29
|
+
type: 'churn-hotspot',
|
|
30
|
+
title: `High-churn file: ${file}`,
|
|
31
|
+
confidence: Math.min(0.9, parseFloat((0.4 + (count / records.length) * 0.5).toFixed(2))),
|
|
32
|
+
status: 'pending',
|
|
33
|
+
supportingCommits: records
|
|
34
|
+
.filter(r => r.files.some(f => f.path === file))
|
|
35
|
+
.slice(0, 5)
|
|
36
|
+
.map(r => ({ sha: r.sha, subject: r.subject })),
|
|
37
|
+
affectedFiles: [file],
|
|
38
|
+
suggestion: `Changed in ${count} commits. Consider whether this file needs refactoring, splitting, or a stabilization pass.`,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function repeatedFixCards(records) {
|
|
42
|
+
const fixRecords = records.filter(r => r.signals.some(s => s.type === 'fix'));
|
|
43
|
+
const fileCount = new Map();
|
|
44
|
+
for (const record of fixRecords) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
for (const file of record.files) {
|
|
47
|
+
if (!seen.has(file.path)) {
|
|
48
|
+
seen.add(file.path);
|
|
49
|
+
const entry = fileCount.get(file.path) ?? { count: 0, commits: [] };
|
|
50
|
+
entry.count += 1;
|
|
51
|
+
entry.commits.push(record);
|
|
52
|
+
fileCount.set(file.path, entry);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return [...fileCount.entries()]
|
|
57
|
+
.filter(([, entry]) => entry.count >= 2)
|
|
58
|
+
.slice(0, 5)
|
|
59
|
+
.map(([file, entry]) => ({
|
|
60
|
+
type: 'repeated-fix',
|
|
61
|
+
title: `Repeated fixes in: ${file}`,
|
|
62
|
+
confidence: Math.min(0.95, parseFloat((0.5 + (entry.count - 1) * 0.1).toFixed(2))),
|
|
63
|
+
supportingCommits: entry.commits.slice(0, 5).map(r => ({ sha: r.sha, subject: r.subject })),
|
|
64
|
+
affectedFiles: [file],
|
|
65
|
+
suggestion: `This file was fixed ${entry.count} times. Consider adding regression tests or a deeper refactor to address root cause.`,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
function rationaleClusterCards(records) {
|
|
69
|
+
const rationaleRecords = records.filter(r => r.signals.some(s => s.type === 'rationale'));
|
|
70
|
+
if (rationaleRecords.length === 0)
|
|
71
|
+
return [];
|
|
72
|
+
// Group by top-level directory
|
|
73
|
+
const dirMap = new Map();
|
|
74
|
+
for (const record of rationaleRecords) {
|
|
75
|
+
const dirs = new Set();
|
|
76
|
+
for (const file of record.files) {
|
|
77
|
+
const parts = file.path.split('/');
|
|
78
|
+
const top = parts.length > 1 ? parts[0] : '<root>';
|
|
79
|
+
dirs.add(top);
|
|
80
|
+
}
|
|
81
|
+
for (const dir of dirs) {
|
|
82
|
+
const list = dirMap.get(dir) ?? [];
|
|
83
|
+
list.push(record);
|
|
84
|
+
dirMap.set(dir, list);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return [...dirMap.entries()]
|
|
88
|
+
.filter(([, records]) => records.length >= 2)
|
|
89
|
+
.slice(0, 5)
|
|
90
|
+
.map(([dir, commits]) => ({
|
|
91
|
+
type: 'rationale-cluster',
|
|
92
|
+
title: `Design rationale cluster: ${dir}/`,
|
|
93
|
+
confidence: parseFloat((0.5 + commits.length * 0.05).toFixed(2)),
|
|
94
|
+
supportingCommits: commits.slice(0, 5).map(r => ({ sha: r.sha, subject: r.subject })),
|
|
95
|
+
affectedFiles: [...new Set(commits.flatMap(r => r.files.map(f => f.path)))].slice(0, 8),
|
|
96
|
+
suggestion: `${commits.length} commits with explanatory messages in ${dir}/. These are good candidates for extracting explicit decision records.`,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
function testGapCards(records) {
|
|
100
|
+
const isTestPath = (p) => /\.(test|spec)\./.test(p) || /__tests__\//.test(p) || /\/test\//.test(p);
|
|
101
|
+
const sourceFiles = new Map();
|
|
102
|
+
for (const record of records) {
|
|
103
|
+
const hasTest = record.files.some(f => isTestPath(f.path));
|
|
104
|
+
if (!hasTest) {
|
|
105
|
+
for (const file of record.files) {
|
|
106
|
+
if (!isTestPath(file.path) && !file.path.startsWith('.')) {
|
|
107
|
+
sourceFiles.set(file.path, (sourceFiles.get(file.path) ?? 0) + 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const filtered = [...sourceFiles.entries()]
|
|
113
|
+
.filter(([, count]) => count >= Math.ceil(records.length * 0.05))
|
|
114
|
+
.sort((a, b) => b[1] - a[1])
|
|
115
|
+
.slice(0, 4);
|
|
116
|
+
return filtered.map(([file, count]) => ({
|
|
117
|
+
type: 'test-gap',
|
|
118
|
+
title: `Possible test gap: ${file}`,
|
|
119
|
+
confidence: Math.min(0.7, parseFloat((0.4 + count * 0.03).toFixed(2))),
|
|
120
|
+
supportingCommits: records
|
|
121
|
+
.filter(r => r.files.some(f => f.path === file) && !r.files.some(f => isTestPath(f.path)))
|
|
122
|
+
.slice(0, 5)
|
|
123
|
+
.map(r => ({ sha: r.sha, subject: r.subject })),
|
|
124
|
+
affectedFiles: [file],
|
|
125
|
+
suggestion: `This file changed ${count} times without a corresponding test change. Review whether tests exist elsewhere or should be added.`,
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
function revertPatternCards(records) {
|
|
129
|
+
const revertRecords = records.filter(r => r.signals.some(s => s.type === 'revert'));
|
|
130
|
+
if (revertRecords.length === 0)
|
|
131
|
+
return [];
|
|
132
|
+
const revert = records.filter(r => r.signals.some(s => s.type === 'revert'));
|
|
133
|
+
const revertedFiles = new Map();
|
|
134
|
+
for (const record of revert) {
|
|
135
|
+
for (const file of record.files) {
|
|
136
|
+
revertedFiles.set(file.path, (revertedFiles.get(file.path) ?? 0) + 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const topFiles = [...revertedFiles.entries()]
|
|
140
|
+
.sort((a, b) => b[1] - a[1])
|
|
141
|
+
.slice(0, 4)
|
|
142
|
+
.map(([file, count]) => ({ file, count }));
|
|
143
|
+
const suggestions = topFiles.length === 0
|
|
144
|
+
? [{ file: '<various>', count: revert.length }]
|
|
145
|
+
: topFiles;
|
|
146
|
+
return suggestions.map(({ file, count }) => ({
|
|
147
|
+
type: 'revert-pattern',
|
|
148
|
+
title: `Reversion pattern${file !== '<various>' ? `: ${file}` : ''}`,
|
|
149
|
+
confidence: Math.min(0.9, parseFloat((0.6 + (revert.length / records.length) * 0.3).toFixed(2))),
|
|
150
|
+
supportingCommits: revert.slice(0, 5).map(r => ({ sha: r.sha, subject: r.subject })),
|
|
151
|
+
affectedFiles: file !== '<various>' ? [file] : [...new Set(revert.flatMap(r => r.files.map(f => f.path)))].slice(0, 8),
|
|
152
|
+
suggestion: `${revert.length} revert commits found${file !== '<various>' ? ` affecting ${file}` : ''}. Reverted changes indicate instability — review before modifying these areas.`,
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
function coChangeCards(records) {
|
|
156
|
+
const pairCount = new Map();
|
|
157
|
+
const pairCommits = new Map();
|
|
158
|
+
for (const record of records) {
|
|
159
|
+
const files = record.files.map(f => f.path).filter(Boolean).sort();
|
|
160
|
+
for (let i = 0; i < files.length; i++) {
|
|
161
|
+
for (let j = i + 1; j < files.length; j++) {
|
|
162
|
+
const key = `${files[i]} <-> ${files[j]}`;
|
|
163
|
+
pairCount.set(key, (pairCount.get(key) ?? 0) + 1);
|
|
164
|
+
const commits = pairCommits.get(key) ?? [];
|
|
165
|
+
commits.push(record);
|
|
166
|
+
pairCommits.set(key, commits);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return [...pairCount.entries()]
|
|
171
|
+
.filter(([, count]) => count >= 2)
|
|
172
|
+
.sort((a, b) => b[1] - a[1])
|
|
173
|
+
.slice(0, 5)
|
|
174
|
+
.map(([pairKey, count]) => {
|
|
175
|
+
const [a, b] = pairKey.split(' <-> ');
|
|
176
|
+
return {
|
|
177
|
+
type: 'co-change',
|
|
178
|
+
title: `Co-change cluster: ${a}, ${b}`,
|
|
179
|
+
confidence: parseFloat((0.3 + count * 0.05).toFixed(2)),
|
|
180
|
+
supportingCommits: (pairCommits.get(pairKey) ?? []).slice(0, 5).map(r => ({ sha: r.sha, subject: r.subject })),
|
|
181
|
+
affectedFiles: [a, b],
|
|
182
|
+
suggestion: `These files changed together in ${count} commits. Consider whether they should be colocated, refactored, or have shared tests.`,
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// ─── Registry ──────────────────────────────────────────────
|
|
187
|
+
export const CARD_GENERATORS = [
|
|
188
|
+
{ type: 'churn-hotspot', label: 'Churn hotspots', run: churnHotspotCards },
|
|
189
|
+
{ type: 'repeated-fix', label: 'Repeated fix areas', run: repeatedFixCards },
|
|
190
|
+
{ type: 'rationale-cluster', label: 'Design rationale clusters', run: rationaleClusterCards },
|
|
191
|
+
{ type: 'test-gap', label: 'Test gaps', run: testGapCards },
|
|
192
|
+
{ type: 'revert-pattern', label: 'Reversion patterns', run: revertPatternCards },
|
|
193
|
+
{ type: 'co-change', label: 'Co-change clusters', run: coChangeCards },
|
|
194
|
+
];
|
|
195
|
+
// ─── Orchestrator ──────────────────────────────────────────
|
|
196
|
+
export function generateCards(classifiedRecords, options = {}, statusOverrides) {
|
|
197
|
+
const minConfidence = options.minConfidence ?? 0.3;
|
|
198
|
+
const maxCards = options.maxCards ?? 20;
|
|
199
|
+
const allCards = [];
|
|
200
|
+
for (const generator of CARD_GENERATORS) {
|
|
201
|
+
const cards = generator.run(classifiedRecords);
|
|
202
|
+
allCards.push(...cards);
|
|
203
|
+
}
|
|
204
|
+
return allCards
|
|
205
|
+
.filter(card => card.confidence >= minConfidence)
|
|
206
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
207
|
+
.slice(0, maxCards)
|
|
208
|
+
.map(data => toCard(data, statusOverrides?.[cardIdFrom(data.type, data.title, data.affectedFiles)]));
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=cards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.js","sourceRoot":"","sources":["../src/cards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type DiffWarning = {
|
|
2
|
+
filePath: string;
|
|
3
|
+
type: 'repeated-fix' | 'test-gap' | 'revert-pattern' | 'co-change-reminder';
|
|
4
|
+
severity: 'low' | 'medium' | 'high';
|
|
5
|
+
message: string;
|
|
6
|
+
evidence: string;
|
|
7
|
+
confidence: number;
|
|
8
|
+
};
|
|
9
|
+
export type CheckDiffOptions = {
|
|
10
|
+
repoPath?: string;
|
|
11
|
+
base?: string;
|
|
12
|
+
head?: string;
|
|
13
|
+
};
|
|
14
|
+
export type CheckDiffResult = {
|
|
15
|
+
repoRoot: string;
|
|
16
|
+
baseSha: string;
|
|
17
|
+
headSha: string;
|
|
18
|
+
changedFiles: string[];
|
|
19
|
+
warnings: DiffWarning[];
|
|
20
|
+
};
|
|
21
|
+
export declare function checkDiff(options?: CheckDiffOptions): CheckDiffResult;
|
|
22
|
+
export declare function formatCheckDiff(result: CheckDiffResult): string;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { mineHistory, runGit, resolveRepoRoot } from './git-history.js';
|
|
2
|
+
import { classifyHistory } from './signals.js';
|
|
3
|
+
import { generateCards } from './cards.js';
|
|
4
|
+
import { getStatusOverrideMap } from './review.js';
|
|
5
|
+
function getChangedFiles(repoRoot, base, head) {
|
|
6
|
+
const output = runGit(repoRoot, [
|
|
7
|
+
'diff', '--name-status', '--no-renames', `${base}..${head}`,
|
|
8
|
+
]).trim();
|
|
9
|
+
if (!output)
|
|
10
|
+
return [];
|
|
11
|
+
return output.split(/\r?\n/).filter(Boolean).map(line => {
|
|
12
|
+
const [status, path] = line.split(/\t/);
|
|
13
|
+
return { status: status ?? 'M', path: path ?? '' };
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function getBaseAndHead(repoRoot, options) {
|
|
17
|
+
const base = options.base ?? 'HEAD~1';
|
|
18
|
+
const head = options.head ?? 'HEAD';
|
|
19
|
+
const baseSha = runGit(repoRoot, ['rev-parse', base]).trim();
|
|
20
|
+
const headSha = runGit(repoRoot, ['rev-parse', head]).trim();
|
|
21
|
+
return { base: baseSha, head: headSha };
|
|
22
|
+
}
|
|
23
|
+
function severityFor(confidence, type) {
|
|
24
|
+
if (type === 'revert-pattern')
|
|
25
|
+
return 'high';
|
|
26
|
+
if (type === 'repeated-fix')
|
|
27
|
+
return confidence >= 0.7 ? 'high' : confidence >= 0.5 ? 'medium' : 'low';
|
|
28
|
+
if (type === 'test-gap')
|
|
29
|
+
return 'medium';
|
|
30
|
+
if (type === 'co-change-reminder')
|
|
31
|
+
return 'low';
|
|
32
|
+
return 'low';
|
|
33
|
+
}
|
|
34
|
+
function warningsForFile(filePath, status, classified, cards) {
|
|
35
|
+
const warnings = [];
|
|
36
|
+
const touchingCommits = classified.filter(r => r.files.some(f => f.path === filePath));
|
|
37
|
+
if (touchingCommits.length === 0)
|
|
38
|
+
return warnings;
|
|
39
|
+
const fileCards = cards.filter(c => c.affectedFiles.includes(filePath));
|
|
40
|
+
// Check repeat-fix card for this file
|
|
41
|
+
for (const card of fileCards) {
|
|
42
|
+
if (card.type === 'repeated-fix') {
|
|
43
|
+
warnings.push({
|
|
44
|
+
filePath,
|
|
45
|
+
type: 'repeated-fix',
|
|
46
|
+
severity: severityFor(card.confidence, 'repeated-fix'),
|
|
47
|
+
message: `This file was fixed ${card.supportingCommits.length} times previously. Changes here risk reintroducing past bugs.`,
|
|
48
|
+
evidence: card.supportingCommits.map(c => ` · ${c.subject} (${c.sha})`).join('\n'),
|
|
49
|
+
confidence: card.confidence,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (card.type === 'revert-pattern') {
|
|
53
|
+
warnings.push({
|
|
54
|
+
filePath,
|
|
55
|
+
type: 'revert-pattern',
|
|
56
|
+
severity: 'high',
|
|
57
|
+
message: `This file has been reverted before — changes here need extra caution.`,
|
|
58
|
+
evidence: card.supportingCommits.map(c => ` · ${c.subject} (${c.sha})`).join('\n'),
|
|
59
|
+
confidence: card.confidence,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check test gap: if adding/modifying non-test source file without test companion
|
|
64
|
+
if (status !== 'D' && !/\.(test|spec)\./.test(filePath) && !/__tests__\//.test(filePath) && !/\/test\//.test(filePath)) {
|
|
65
|
+
const isTestPath = (p) => /\.(test|spec)\./.test(p) || /__tests__\//.test(p) || /\/test\//.test(p);
|
|
66
|
+
const hasTestCompanion = touchingCommits.some(c => c.files.some(f => isTestPath(f.path) && f.path.replace(/\.(test|spec)\./, '.').split('/').pop() === filePath.split('/').pop()));
|
|
67
|
+
if (!hasTestCompanion) {
|
|
68
|
+
warnings.push({
|
|
69
|
+
filePath,
|
|
70
|
+
type: 'test-gap',
|
|
71
|
+
severity: 'medium',
|
|
72
|
+
message: `History shows changes to this file without co-occurring test updates. Consider adding or updating tests.`,
|
|
73
|
+
evidence: `${touchingCommits.length} commits touching this file.`,
|
|
74
|
+
confidence: 0.5,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Co-change reminder: check for partners that aren't in this diff
|
|
79
|
+
const partnerMap = new Map();
|
|
80
|
+
for (const commit of touchingCommits) {
|
|
81
|
+
const seen = new Set();
|
|
82
|
+
for (const file of commit.files) {
|
|
83
|
+
if (file.path !== filePath && !seen.has(file.path)) {
|
|
84
|
+
seen.add(file.path);
|
|
85
|
+
partnerMap.set(file.path, (partnerMap.get(file.path) ?? 0) + 1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const topPartners = [...partnerMap.entries()]
|
|
90
|
+
.filter(([, count]) => count >= Math.min(2, touchingCommits.length * 0.3))
|
|
91
|
+
.slice(0, 3);
|
|
92
|
+
if (topPartners.length > 0) {
|
|
93
|
+
warnings.push({
|
|
94
|
+
filePath,
|
|
95
|
+
type: 'co-change-reminder',
|
|
96
|
+
severity: 'low',
|
|
97
|
+
message: `This file historically changes alongside: ${topPartners.map(([p]) => p).join(', ')}. Verify they don't need updates too.`,
|
|
98
|
+
evidence: topPartners.map(([p, c]) => ` · ${p} (${c} time${c !== 1 ? 's' : ''})`).join('\n'),
|
|
99
|
+
confidence: Math.min(0.7, 0.3 + touchingCommits.length * 0.02),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return warnings;
|
|
103
|
+
}
|
|
104
|
+
export function checkDiff(options = {}) {
|
|
105
|
+
const repoRoot = resolveRepoRoot(options.repoPath);
|
|
106
|
+
const { base, head } = getBaseAndHead(repoRoot, options);
|
|
107
|
+
const changedFiles = getChangedFiles(repoRoot, base, head);
|
|
108
|
+
const changedPaths = changedFiles.map(f => f.path);
|
|
109
|
+
// Run the full pipeline once
|
|
110
|
+
const history = mineHistory({ repoPath: repoRoot });
|
|
111
|
+
const classified = classifyHistory(history.records);
|
|
112
|
+
const cards = generateCards(classified, { minConfidence: 0.3 }, getStatusOverrideMap(repoRoot));
|
|
113
|
+
const warnings = [];
|
|
114
|
+
for (const { path: filePath, status } of changedFiles) {
|
|
115
|
+
const fileWarnings = warningsForFile(filePath, status, classified, cards);
|
|
116
|
+
warnings.push(...fileWarnings);
|
|
117
|
+
}
|
|
118
|
+
// Sort by severity then confidence
|
|
119
|
+
const severityOrder = { high: 0, medium: 1, low: 2 };
|
|
120
|
+
warnings.sort((a, b) => {
|
|
121
|
+
const sevDiff = (severityOrder[a.severity] ?? 99) - (severityOrder[b.severity] ?? 99);
|
|
122
|
+
if (sevDiff !== 0)
|
|
123
|
+
return sevDiff;
|
|
124
|
+
return b.confidence - a.confidence;
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
repoRoot,
|
|
128
|
+
baseSha: base,
|
|
129
|
+
headSha: head,
|
|
130
|
+
changedFiles: changedPaths,
|
|
131
|
+
warnings,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function formatCheckDiff(result) {
|
|
135
|
+
const lines = [];
|
|
136
|
+
lines.push('');
|
|
137
|
+
if (result.warnings.length === 0) {
|
|
138
|
+
lines.push(` ✅ No historical warnings for this diff.`);
|
|
139
|
+
lines.push(` (${result.changedFiles.length} file${result.changedFiles.length !== 1 ? 's' : ''} changed)`);
|
|
140
|
+
lines.push('');
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
}
|
|
143
|
+
for (const { filePath, type, severity, message, evidence, confidence } of result.warnings) {
|
|
144
|
+
const icon = severity === 'high' ? '🔴' : severity === 'medium' ? '🟡' : '🟢';
|
|
145
|
+
const tag = severity.toUpperCase();
|
|
146
|
+
lines.push(` ${icon} [${tag}] ${filePath}`);
|
|
147
|
+
lines.push(` ${message}`);
|
|
148
|
+
lines.push(` ${evidence.replace(/\n/g, '\n ')}`);
|
|
149
|
+
lines.push(` confidence: ${confidence}`);
|
|
150
|
+
lines.push('');
|
|
151
|
+
}
|
|
152
|
+
lines.push(` ${result.warnings.length} warning${result.warnings.length !== 1 ? 's' : ''} across ${result.changedFiles.length} changed file${result.changedFiles.length !== 1 ? 's' : ''}.`);
|
|
153
|
+
lines.push('');
|
|
154
|
+
return lines.join('\n');
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=check-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-diff.js","sourceRoot":"","sources":["../src/check-diff.ts"],"names":[],"mappings":"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"}
|