@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.
@@ -0,0 +1,216 @@
1
+ export const SIGNAL_DEFS = [
2
+ {
3
+ type: 'fix',
4
+ label: 'Bug fix',
5
+ subjectPatterns: [
6
+ /^(fix|bugfix|hotfix)[\s(:]/i,
7
+ /\bfix(e[sd])?\b/i,
8
+ /\bbug\b/i,
9
+ /\bhotfix\b/i,
10
+ /\bpatch\b/i,
11
+ /\bresolve[ds]?\b/i,
12
+ /\bregression\b/i,
13
+ ],
14
+ baseConfidence: 1.0,
15
+ },
16
+ {
17
+ type: 'revert',
18
+ label: 'Revert',
19
+ subjectPatterns: [
20
+ /^revert[\s(:]/i,
21
+ /this reverts/i,
22
+ /back out/i,
23
+ /undo\b/i,
24
+ ],
25
+ baseConfidence: 1.0,
26
+ },
27
+ {
28
+ type: 'rationale',
29
+ label: 'Contains rationale',
30
+ subjectPatterns: [
31
+ /\bbecause\b/i,
32
+ /\bsince\b/i,
33
+ /\bto avoid\b/i,
34
+ /\bin order to\b/i,
35
+ /\breason\b/i,
36
+ /\bavoid\b/i,
37
+ /\bprevent\b/i,
38
+ /\btrade[ -]off\b/i,
39
+ /\bwe choose?\b/i,
40
+ /\bthe reason\b/i,
41
+ /\bnecessitates?\b/i,
42
+ ],
43
+ baseConfidence: 0.7,
44
+ },
45
+ {
46
+ type: 'migration',
47
+ label: 'Migration / structural change',
48
+ subjectPatterns: [
49
+ /^migrate[\s(:]/i,
50
+ /\bmigrat(e|ion)\b/i,
51
+ /\brename\b/i,
52
+ /\bwrap\b/i,
53
+ /\brestructure\b/i,
54
+ /\breplace\b/i,
55
+ /\bconsolidate\b/i,
56
+ /\bsplit\b/i,
57
+ ],
58
+ filePatterns: [/^R\d*\t/],
59
+ baseConfidence: 0.8,
60
+ },
61
+ {
62
+ type: 'deprecate',
63
+ label: 'Deprecation',
64
+ subjectPatterns: [
65
+ /^deprecat/i,
66
+ /\bdeprecat\w+\b/i,
67
+ /\bwill be removed\b/i,
68
+ /\bno longer supported\b/i,
69
+ ],
70
+ baseConfidence: 0.9,
71
+ },
72
+ {
73
+ type: 'refactor',
74
+ label: 'Refactor',
75
+ subjectPatterns: [
76
+ /^refactor/i,
77
+ /\brefactor(or|ing)?\b/i,
78
+ /\brewrite\b/i,
79
+ /\bcleanup\b/i,
80
+ /\bsimplif(y|ies|ied|ying)\b/i,
81
+ ],
82
+ baseConfidence: 0.8,
83
+ },
84
+ {
85
+ type: 'test',
86
+ label: 'Test change',
87
+ subjectPatterns: [
88
+ /^test[\s(:]/i,
89
+ /\badd.*test/i,
90
+ /\bupdate.*test/i,
91
+ /\bfix.*test/i,
92
+ /\btest.*case/i,
93
+ /\bspec\b/i,
94
+ ],
95
+ filePatterns: [/\.test\./, /\.spec\./, /__tests__\//, /\/test\//],
96
+ baseConfidence: 0.7,
97
+ },
98
+ {
99
+ type: 'docs',
100
+ label: 'Documentation',
101
+ subjectPatterns: [
102
+ /^docs?[\s(:]/i,
103
+ /\bdocument/i,
104
+ /\bREADME\b/i,
105
+ ],
106
+ filePatterns: [/\.md$/, /\.mdx$/, /\/docs\//, /\/wiki\//],
107
+ baseConfidence: 0.8,
108
+ },
109
+ {
110
+ type: 'perf',
111
+ label: 'Performance',
112
+ subjectPatterns: [
113
+ /^perf[\s(:]/i,
114
+ /\bperformanc/i,
115
+ /\bspeed\b/i,
116
+ /\boptimize?\b/i,
117
+ /\blatency\b/i,
118
+ /\bthroughput\b/i,
119
+ ],
120
+ baseConfidence: 0.8,
121
+ },
122
+ {
123
+ type: 'security',
124
+ label: 'Security',
125
+ subjectPatterns: [
126
+ /^security/i,
127
+ /\bsecurity\b/i,
128
+ /\bCVE-/i,
129
+ /\bexploit\b/i,
130
+ /\bXSS\b/i,
131
+ /\bSQL injection\b/i,
132
+ ],
133
+ baseConfidence: 0.9,
134
+ },
135
+ {
136
+ type: 'experimental',
137
+ label: 'Experimental',
138
+ subjectPatterns: [
139
+ /^experimental/i,
140
+ /\bexperiment\b/i,
141
+ /\bprototype?\b/i,
142
+ /\bPoC\b/i,
143
+ /\bproof of concept\b/i,
144
+ ],
145
+ baseConfidence: 0.7,
146
+ },
147
+ {
148
+ type: 'config',
149
+ label: 'Config / dependency change',
150
+ subjectPatterns: [
151
+ /^chore[\s(:]/i,
152
+ /\bdependenc/i,
153
+ /^bump\b/i,
154
+ /\bconfig\b/i,
155
+ /\bversion\b/i,
156
+ ],
157
+ filePatterns: [/package\.json$/, /\.config\./, /Dockerfile/, /docker-compose/, /\.ya?ml$/, /^\./, /Makefile/],
158
+ baseConfidence: 0.6,
159
+ },
160
+ ];
161
+ export function classifyCommit(record, defs = SIGNAL_DEFS) {
162
+ const signals = [];
163
+ const subject = record.subject;
164
+ for (const def of defs) {
165
+ // Check subject first
166
+ let matched = false;
167
+ for (const pattern of def.subjectPatterns) {
168
+ if (pattern.test(subject)) {
169
+ signals.push({
170
+ type: def.type,
171
+ label: def.label,
172
+ confidence: def.baseConfidence,
173
+ matchedOn: 'subject',
174
+ });
175
+ matched = true;
176
+ break;
177
+ }
178
+ }
179
+ // If subject didn't match, try file patterns as fallback
180
+ if (!matched && def.filePatterns) {
181
+ for (const file of record.files) {
182
+ for (const pattern of def.filePatterns) {
183
+ if (pattern.test(file.path) || (file.oldPath && pattern.test(file.oldPath))) {
184
+ signals.push({
185
+ type: def.type,
186
+ label: def.label,
187
+ confidence: Math.round(def.baseConfidence * 0.8 * 100) / 100, // lower confidence for file-only match
188
+ matchedOn: 'file',
189
+ });
190
+ matched = true;
191
+ break;
192
+ }
193
+ }
194
+ if (matched)
195
+ break;
196
+ }
197
+ }
198
+ }
199
+ // Deduplicate by type (keep the first/highest confidence match)
200
+ const seen = new Set();
201
+ const uniqueSignals = [];
202
+ for (const s of signals) {
203
+ if (!seen.has(s.type)) {
204
+ seen.add(s.type);
205
+ uniqueSignals.push(s);
206
+ }
207
+ }
208
+ return {
209
+ ...record,
210
+ signals: uniqueSignals,
211
+ };
212
+ }
213
+ export function classifyHistory(records, defs = SIGNAL_DEFS) {
214
+ return records.map(record => classifyCommit(record, defs));
215
+ }
216
+ //# sourceMappingURL=signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.js","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export type SimilarResult = {
2
+ query: string;
3
+ results: {
4
+ id: string;
5
+ text: string;
6
+ source: string;
7
+ score: number;
8
+ metadata: Record<string, string>;
9
+ }[];
10
+ indexStats: {
11
+ model: string;
12
+ dim: number;
13
+ entries: number;
14
+ headSha: string;
15
+ };
16
+ };
17
+ export declare function similar(query: string, options?: {
18
+ repoPath?: string;
19
+ topK?: number;
20
+ }): Promise<SimilarResult>;
@@ -0,0 +1,51 @@
1
+ import { mineHistory } from './git-history.js';
2
+ import { classifyHistory } from './signals.js';
3
+ import { generateCards } from './cards.js';
4
+ import { getStatusOverrideMap } from './review.js';
5
+ import { loadIndex, buildIndex, searchIndex, embed } from './embedder.js';
6
+ import { resolveRepoRoot, getHeadSha } from './git-history.js';
7
+ import { cachedOrGenerate } from './cache.js';
8
+ export async function similar(query, options = {}) {
9
+ const repoRoot = resolveRepoRoot(options.repoPath);
10
+ const headSha = getHeadSha(repoRoot);
11
+ const topK = options.topK ?? 5;
12
+ // Load or build index
13
+ let index = loadIndex(repoRoot);
14
+ if (!index || index.headSha !== headSha) {
15
+ // Build index from cards
16
+ const generateFn = () => {
17
+ const history = mineHistory({ repoPath: options.repoPath });
18
+ const classified = classifyHistory(history.records);
19
+ return generateCards(classified, {}, getStatusOverrideMap(repoRoot));
20
+ };
21
+ const { cards } = cachedOrGenerate(repoRoot, generateFn);
22
+ const entries = cards.map(card => ({
23
+ id: card.id,
24
+ text: `${card.title}. ${card.suggestion} ${card.supportingCommits.map(c => c.subject).join('. ')}`,
25
+ source: 'card',
26
+ metadata: { type: card.type, confidence: String(card.confidence), status: card.status },
27
+ }));
28
+ index = await buildIndex(entries, { repoPath: options.repoPath });
29
+ }
30
+ // Embed query
31
+ const queryEmbedding = await embed(query);
32
+ // Search
33
+ const results = searchIndex(index, queryEmbedding, topK);
34
+ return {
35
+ query,
36
+ results: results.map(r => ({
37
+ id: r.entry.id,
38
+ text: r.entry.text,
39
+ source: r.entry.source,
40
+ score: parseFloat(r.score.toFixed(4)),
41
+ metadata: r.entry.metadata,
42
+ })),
43
+ indexStats: {
44
+ model: index.model,
45
+ dim: index.dim,
46
+ entries: index.entries.length,
47
+ headSha: index.headSha.slice(0, 12),
48
+ },
49
+ };
50
+ }
51
+ //# sourceMappingURL=similar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similar.js","sourceRoot":"","sources":["../src/similar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { InsightCard, CardType } from './cards.js';
2
+ export type StaleStatus = 'fresh' | 'partial' | 'stale';
3
+ export type StalenessResult = {
4
+ cardId: string;
5
+ type: CardType;
6
+ title: string;
7
+ status: StaleStatus;
8
+ missingFiles: string[];
9
+ existingFiles: string[];
10
+ /** Whether referenced commits are still reachable in history */
11
+ commitsReachable: boolean;
12
+ /** HEAD at time of check */
13
+ checkedAtHead: string;
14
+ };
15
+ export type StalenessOptions = {
16
+ repoPath?: string;
17
+ };
18
+ export type StalenessSummary = {
19
+ repoRoot: string;
20
+ checkedAtHead: string;
21
+ total: number;
22
+ fresh: number;
23
+ partial: number;
24
+ stale: number;
25
+ results: StalenessResult[];
26
+ };
27
+ export declare function checkStaleness(cards: InsightCard[], options?: StalenessOptions): StalenessSummary;
28
+ export declare function formatStaleness(summary: StalenessSummary): string;
@@ -0,0 +1,110 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { runGit, resolveRepoRoot } from './git-history.js';
4
+ function fileExists(repoRoot, filePath) {
5
+ return fs.existsSync(path.join(repoRoot, filePath));
6
+ }
7
+ function commitsReachable(repoRoot, shas) {
8
+ if (shas.length === 0)
9
+ return true;
10
+ try {
11
+ runGit(repoRoot, ['cat-file', '--batch-check']);
12
+ // Check each sha individually
13
+ for (const sha of shas) {
14
+ try {
15
+ runGit(repoRoot, ['cat-file', '-e', sha]);
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ catch {
24
+ // fallback: try a single merge-base check
25
+ try {
26
+ runGit(repoRoot, ['merge-base', '--is-ancestor', shas[0], 'HEAD']);
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ }
34
+ export function checkStaleness(cards, options = {}) {
35
+ const repoRoot = resolveRepoRoot(options.repoPath);
36
+ const headSha = runGit(repoRoot, ['rev-parse', 'HEAD']).trim();
37
+ const results = cards.map(card => {
38
+ const existing = [];
39
+ const missing = [];
40
+ for (const file of card.affectedFiles) {
41
+ if (fileExists(repoRoot, file)) {
42
+ existing.push(file);
43
+ }
44
+ else {
45
+ missing.push(file);
46
+ }
47
+ }
48
+ let status = 'fresh';
49
+ if (missing.length > 0 && existing.length > 0) {
50
+ status = 'partial';
51
+ }
52
+ else if (missing.length > 0 && existing.length === 0) {
53
+ status = 'stale';
54
+ }
55
+ const commitShas = card.supportingCommits.map(c => c.sha);
56
+ return {
57
+ cardId: card.id,
58
+ type: card.type,
59
+ title: card.title,
60
+ status,
61
+ missingFiles: missing,
62
+ existingFiles: existing,
63
+ commitsReachable: commitsReachable(repoRoot, commitShas),
64
+ checkedAtHead: headSha,
65
+ };
66
+ });
67
+ const fresh = results.filter(r => r.status === 'fresh').length;
68
+ const partial = results.filter(r => r.status === 'partial').length;
69
+ const stale = results.filter(r => r.status === 'stale').length;
70
+ return {
71
+ repoRoot,
72
+ checkedAtHead: headSha,
73
+ total: results.length,
74
+ fresh,
75
+ partial,
76
+ stale,
77
+ results,
78
+ };
79
+ }
80
+ export function formatStaleness(summary) {
81
+ const lines = [];
82
+ if (summary.total === 0) {
83
+ lines.push('\n No cards to check.\n');
84
+ return lines.join('\n');
85
+ }
86
+ const statusIcon = (s) => s === 'fresh' ? '✅' : s === 'partial' ? '🟡' : '🔴';
87
+ lines.push(`\n Staleness check for ${summary.repoRoot}`);
88
+ lines.push(` ${summary.checkedAtHead.slice(0, 12)} | ${summary.total} cards`);
89
+ lines.push(` ${summary.fresh} fresh · ${summary.partial} partial · ${summary.stale} stale\n`);
90
+ for (const result of summary.results) {
91
+ if (result.status !== 'stale' && result.status !== 'partial')
92
+ continue;
93
+ lines.push(` ${statusIcon(result.status)} [${result.status.toUpperCase()}] ${result.title}`);
94
+ if (result.missingFiles.length > 0) {
95
+ lines.push(` Missing files:`);
96
+ for (const f of result.missingFiles) {
97
+ lines.push(` · ${f}`);
98
+ }
99
+ }
100
+ if (!result.commitsReachable) {
101
+ lines.push(` ⚠ Referenced commits no longer reachable in history`);
102
+ }
103
+ lines.push('');
104
+ }
105
+ if (summary.stale === 0 && summary.partial === 0) {
106
+ lines.push(` All cards reference files that still exist.\n`);
107
+ }
108
+ return lines.join('\n');
109
+ }
110
+ //# sourceMappingURL=staleness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staleness.js","sourceRoot":"","sources":["../src/staleness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { type InsightCard } from './cards.js';
2
+ export type DatasetExample = {
3
+ messages: {
4
+ role: 'user' | 'assistant';
5
+ content: string;
6
+ }[];
7
+ sourceCardId: string;
8
+ taskType: 'qa' | 'review-warning' | 'risk-classification' | 'negative';
9
+ };
10
+ export type DatasetOptions = {
11
+ repoPath?: string;
12
+ outPath?: string;
13
+ includeRejected?: boolean;
14
+ };
15
+ export type DatasetResult = {
16
+ repoRoot: string;
17
+ headSha: string;
18
+ totalCards: number;
19
+ acceptedCards: number;
20
+ examples: DatasetExample[];
21
+ counts: {
22
+ qa: number;
23
+ 'review-warning': number;
24
+ 'risk-classification': number;
25
+ negative: number;
26
+ };
27
+ };
28
+ export declare function generateQa(card: InsightCard): DatasetExample[];
29
+ export declare function generateReviewWarning(card: InsightCard): DatasetExample[];
30
+ export declare function generateRiskClassification(card: InsightCard): DatasetExample[];
31
+ export declare function generateDataset(options?: DatasetOptions): DatasetResult;
32
+ export declare function formatDataset(result: DatasetResult): string;
33
+ export type TrainOptions = {
34
+ repoPath?: string;
35
+ outPath?: string;
36
+ model?: string;
37
+ iters?: number;
38
+ adapterName?: string;
39
+ learningRate?: number;
40
+ /** Whether to actually run training (vs just printing the command) */
41
+ run?: boolean;
42
+ };
43
+ export type TrainPlan = {
44
+ dataDir: string;
45
+ trainFile: string;
46
+ validFile: string;
47
+ model: string;
48
+ adapterPath: string;
49
+ command: string;
50
+ examples: number;
51
+ };
52
+ export declare function prepareTrain(options?: TrainOptions): TrainPlan;
53
+ export declare function formatTrain(plan: TrainPlan): string;