@ai-agent-context/core 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 +285 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/adapters/filesystem/fs-adapter.d.ts +50 -0
- package/dist/adapters/filesystem/fs-adapter.d.ts.map +1 -0
- package/dist/adapters/filesystem/fs-adapter.js +168 -0
- package/dist/adapters/filesystem/fs-adapter.js.map +1 -0
- package/dist/adapters/filesystem/sensitive.d.ts +26 -0
- package/dist/adapters/filesystem/sensitive.d.ts.map +1 -0
- package/dist/adapters/filesystem/sensitive.js +67 -0
- package/dist/adapters/filesystem/sensitive.js.map +1 -0
- package/dist/adapters/git/git-adapter.d.ts +47 -0
- package/dist/adapters/git/git-adapter.d.ts.map +1 -0
- package/dist/adapters/git/git-adapter.js +109 -0
- package/dist/adapters/git/git-adapter.js.map +1 -0
- package/dist/adapters/language/json/adapter.d.ts +24 -0
- package/dist/adapters/language/json/adapter.d.ts.map +1 -0
- package/dist/adapters/language/json/adapter.js +54 -0
- package/dist/adapters/language/json/adapter.js.map +1 -0
- package/dist/adapters/language/markdown/adapter.d.ts +41 -0
- package/dist/adapters/language/markdown/adapter.d.ts.map +1 -0
- package/dist/adapters/language/markdown/adapter.js +124 -0
- package/dist/adapters/language/markdown/adapter.js.map +1 -0
- package/dist/adapters/language/package-json/adapter.d.ts +47 -0
- package/dist/adapters/language/package-json/adapter.d.ts.map +1 -0
- package/dist/adapters/language/package-json/adapter.js +153 -0
- package/dist/adapters/language/package-json/adapter.js.map +1 -0
- package/dist/adapters/language/registry.d.ts +35 -0
- package/dist/adapters/language/registry.d.ts.map +1 -0
- package/dist/adapters/language/registry.js +79 -0
- package/dist/adapters/language/registry.js.map +1 -0
- package/dist/adapters/language/ts-js/adapter.d.ts +14 -0
- package/dist/adapters/language/ts-js/adapter.d.ts.map +1 -0
- package/dist/adapters/language/ts-js/adapter.js +59 -0
- package/dist/adapters/language/ts-js/adapter.js.map +1 -0
- package/dist/adapters/language/ts-js/extractor.d.ts +21 -0
- package/dist/adapters/language/ts-js/extractor.d.ts.map +1 -0
- package/dist/adapters/language/ts-js/extractor.js +779 -0
- package/dist/adapters/language/ts-js/extractor.js.map +1 -0
- package/dist/adapters/language/ts-js/lexer.d.ts +30 -0
- package/dist/adapters/language/ts-js/lexer.d.ts.map +1 -0
- package/dist/adapters/language/ts-js/lexer.js +446 -0
- package/dist/adapters/language/ts-js/lexer.js.map +1 -0
- package/dist/adapters/language/ts-js/package-signals.d.ts +5 -0
- package/dist/adapters/language/ts-js/package-signals.d.ts.map +1 -0
- package/dist/adapters/language/ts-js/package-signals.js +113 -0
- package/dist/adapters/language/ts-js/package-signals.js.map +1 -0
- package/dist/adapters/language/tsconfig/adapter.d.ts +38 -0
- package/dist/adapters/language/tsconfig/adapter.d.ts.map +1 -0
- package/dist/adapters/language/tsconfig/adapter.js +130 -0
- package/dist/adapters/language/tsconfig/adapter.js.map +1 -0
- package/dist/adapters/language/types.d.ts +152 -0
- package/dist/adapters/language/types.d.ts.map +1 -0
- package/dist/adapters/language/types.js +51 -0
- package/dist/adapters/language/types.js.map +1 -0
- package/dist/agent-context.d.ts +122 -0
- package/dist/agent-context.d.ts.map +1 -0
- package/dist/agent-context.js +243 -0
- package/dist/agent-context.js.map +1 -0
- package/dist/config/defaults.d.ts +13 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +98 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/load.d.ts +25 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +106 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/types.d.ts +95 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +8 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validate.d.ts +15 -0
- package/dist/config/validate.d.ts.map +1 -0
- package/dist/config/validate.js +202 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/core/architecture/detector.d.ts +10 -0
- package/dist/core/architecture/detector.d.ts.map +1 -0
- package/dist/core/architecture/detector.js +37 -0
- package/dist/core/architecture/detector.js.map +1 -0
- package/dist/core/architecture/entry-points.d.ts +20 -0
- package/dist/core/architecture/entry-points.d.ts.map +1 -0
- package/dist/core/architecture/entry-points.js +190 -0
- package/dist/core/architecture/entry-points.js.map +1 -0
- package/dist/core/architecture/role-detector.d.ts +32 -0
- package/dist/core/architecture/role-detector.d.ts.map +1 -0
- package/dist/core/architecture/role-detector.js +240 -0
- package/dist/core/architecture/role-detector.js.map +1 -0
- package/dist/core/context/explain.d.ts +36 -0
- package/dist/core/context/explain.d.ts.map +1 -0
- package/dist/core/context/explain.js +199 -0
- package/dist/core/context/explain.js.map +1 -0
- package/dist/core/context/repository-context.d.ts +16 -0
- package/dist/core/context/repository-context.d.ts.map +1 -0
- package/dist/core/context/repository-context.js +134 -0
- package/dist/core/context/repository-context.js.map +1 -0
- package/dist/core/context/types.d.ts +94 -0
- package/dist/core/context/types.d.ts.map +1 -0
- package/dist/core/context/types.js +2 -0
- package/dist/core/context/types.js.map +1 -0
- package/dist/core/conventions/detector.d.ts +55 -0
- package/dist/core/conventions/detector.d.ts.map +1 -0
- package/dist/core/conventions/detector.js +438 -0
- package/dist/core/conventions/detector.js.map +1 -0
- package/dist/core/conventions/renderer.d.ts +16 -0
- package/dist/core/conventions/renderer.d.ts.map +1 -0
- package/dist/core/conventions/renderer.js +42 -0
- package/dist/core/conventions/renderer.js.map +1 -0
- package/dist/core/decisions/detector.d.ts +45 -0
- package/dist/core/decisions/detector.d.ts.map +1 -0
- package/dist/core/decisions/detector.js +256 -0
- package/dist/core/decisions/detector.js.map +1 -0
- package/dist/core/diff/differ.d.ts +26 -0
- package/dist/core/diff/differ.d.ts.map +1 -0
- package/dist/core/diff/differ.js +159 -0
- package/dist/core/diff/differ.js.map +1 -0
- package/dist/core/git-analysis/activity.d.ts +35 -0
- package/dist/core/git-analysis/activity.d.ts.map +1 -0
- package/dist/core/git-analysis/activity.js +123 -0
- package/dist/core/git-analysis/activity.js.map +1 -0
- package/dist/core/graph/aliases.d.ts +20 -0
- package/dist/core/graph/aliases.d.ts.map +1 -0
- package/dist/core/graph/aliases.js +99 -0
- package/dist/core/graph/aliases.js.map +1 -0
- package/dist/core/graph/knowledge-graph.d.ts +91 -0
- package/dist/core/graph/knowledge-graph.d.ts.map +1 -0
- package/dist/core/graph/knowledge-graph.js +572 -0
- package/dist/core/graph/knowledge-graph.js.map +1 -0
- package/dist/core/graph/modules.d.ts +28 -0
- package/dist/core/graph/modules.d.ts.map +1 -0
- package/dist/core/graph/modules.js +139 -0
- package/dist/core/graph/modules.js.map +1 -0
- package/dist/core/graph/resolver.d.ts +50 -0
- package/dist/core/graph/resolver.d.ts.map +1 -0
- package/dist/core/graph/resolver.js +180 -0
- package/dist/core/graph/resolver.js.map +1 -0
- package/dist/core/scanner/cache.d.ts +49 -0
- package/dist/core/scanner/cache.d.ts.map +1 -0
- package/dist/core/scanner/cache.js +101 -0
- package/dist/core/scanner/cache.js.map +1 -0
- package/dist/core/scanner/scanner.d.ts +60 -0
- package/dist/core/scanner/scanner.d.ts.map +1 -0
- package/dist/core/scanner/scanner.js +304 -0
- package/dist/core/scanner/scanner.js.map +1 -0
- package/dist/core/scanner/types.d.ts +47 -0
- package/dist/core/scanner/types.d.ts.map +1 -0
- package/dist/core/scanner/types.js +18 -0
- package/dist/core/scanner/types.js.map +1 -0
- package/dist/core/search/analyzer.d.ts +16 -0
- package/dist/core/search/analyzer.d.ts.map +1 -0
- package/dist/core/search/analyzer.js +165 -0
- package/dist/core/search/analyzer.js.map +1 -0
- package/dist/core/search/bm25.d.ts +21 -0
- package/dist/core/search/bm25.d.ts.map +1 -0
- package/dist/core/search/bm25.js +108 -0
- package/dist/core/search/bm25.js.map +1 -0
- package/dist/core/search/documents.d.ts +20 -0
- package/dist/core/search/documents.d.ts.map +1 -0
- package/dist/core/search/documents.js +106 -0
- package/dist/core/search/documents.js.map +1 -0
- package/dist/core/search/service.d.ts +30 -0
- package/dist/core/search/service.d.ts.map +1 -0
- package/dist/core/search/service.js +54 -0
- package/dist/core/search/service.js.map +1 -0
- package/dist/core/search/types.d.ts +44 -0
- package/dist/core/search/types.d.ts.map +1 -0
- package/dist/core/search/types.js +2 -0
- package/dist/core/search/types.js.map +1 -0
- package/dist/core/serialization/documents.d.ts +26 -0
- package/dist/core/serialization/documents.d.ts.map +1 -0
- package/dist/core/serialization/documents.js +224 -0
- package/dist/core/serialization/documents.js.map +1 -0
- package/dist/core/serialization/reader.d.ts +22 -0
- package/dist/core/serialization/reader.d.ts.map +1 -0
- package/dist/core/serialization/reader.js +65 -0
- package/dist/core/serialization/reader.js.map +1 -0
- package/dist/core/serialization/types.d.ts +184 -0
- package/dist/core/serialization/types.d.ts.map +1 -0
- package/dist/core/serialization/types.js +2 -0
- package/dist/core/serialization/types.js.map +1 -0
- package/dist/core/serialization/writer.d.ts +17 -0
- package/dist/core/serialization/writer.d.ts.map +1 -0
- package/dist/core/serialization/writer.js +64 -0
- package/dist/core/serialization/writer.js.map +1 -0
- package/dist/core/workspace/package-facts.d.ts +12 -0
- package/dist/core/workspace/package-facts.d.ts.map +1 -0
- package/dist/core/workspace/package-facts.js +50 -0
- package/dist/core/workspace/package-facts.js.map +1 -0
- package/dist/core/workspace/workspace.d.ts +32 -0
- package/dist/core/workspace/workspace.d.ts.map +1 -0
- package/dist/core/workspace/workspace.js +167 -0
- package/dist/core/workspace/workspace.js.map +1 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +64 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/diagnostics.d.ts +64 -0
- package/dist/logging/diagnostics.d.ts.map +1 -0
- package/dist/logging/diagnostics.js +92 -0
- package/dist/logging/diagnostics.js.map +1 -0
- package/dist/model/canonical.d.ts +24 -0
- package/dist/model/canonical.d.ts.map +1 -0
- package/dist/model/canonical.js +69 -0
- package/dist/model/canonical.js.map +1 -0
- package/dist/model/schema.d.ts +37 -0
- package/dist/model/schema.d.ts.map +1 -0
- package/dist/model/schema.js +42 -0
- package/dist/model/schema.js.map +1 -0
- package/dist/model/types.d.ts +402 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +10 -0
- package/dist/model/types.js.map +1 -0
- package/dist/pipeline/scan-pipeline.d.ts +69 -0
- package/dist/pipeline/scan-pipeline.d.ts.map +1 -0
- package/dist/pipeline/scan-pipeline.js +270 -0
- package/dist/pipeline/scan-pipeline.js.map +1 -0
- package/dist/util/concurrency.d.ts +17 -0
- package/dist/util/concurrency.d.ts.map +1 -0
- package/dist/util/concurrency.js +46 -0
- package/dist/util/concurrency.js.map +1 -0
- package/dist/util/glob.d.ts +26 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +198 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/paths.d.ts +23 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +115 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/text.d.ts +22 -0
- package/dist/util/text.d.ts.map +1 -0
- package/dist/util/text.js +66 -0
- package/dist/util/text.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { compareStrings, shortHash } from "../../model/canonical.js";
|
|
2
|
+
import { redactSecrets } from "../../adapters/filesystem/sensitive.js";
|
|
3
|
+
import { truncate, formatDate } from "../../util/text.js";
|
|
4
|
+
import { basename, stem } from "../../util/paths.js";
|
|
5
|
+
const ADR_DIRECTORY = /(^|\/)(adr|adrs|decisions?|architecture\/decisions?)\//i;
|
|
6
|
+
const ADR_FILENAME = /^(adr[-_]?)?(\d{2,4})[-_.]|^adr[-_]?\d*[-_.]/i;
|
|
7
|
+
const ADR_HEADING = /^(adr[-_ ]?\d{1,4}|decision[-_ ]?\d{1,4})\s*[:\-–—]?\s*(.*)$/i;
|
|
8
|
+
const STATUS_PATTERN = /\bstatus\b\s*[:\-]\s*(accepted|proposed|deprecated|superseded|rejected|draft|approved)/i;
|
|
9
|
+
const DECISION_SECTION = /\b(decision|decisions|architecture decision|design decision|invariant|invariants|architecture)\b/i;
|
|
10
|
+
const STATUS_MAP = {
|
|
11
|
+
accepted: 'accepted',
|
|
12
|
+
approved: 'accepted',
|
|
13
|
+
proposed: 'proposed',
|
|
14
|
+
draft: 'proposed',
|
|
15
|
+
deprecated: 'deprecated',
|
|
16
|
+
superseded: 'superseded',
|
|
17
|
+
rejected: 'rejected',
|
|
18
|
+
};
|
|
19
|
+
function readStringArray(value) {
|
|
20
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
21
|
+
}
|
|
22
|
+
function normalizeStatus(raw) {
|
|
23
|
+
if (raw === undefined)
|
|
24
|
+
return 'unknown';
|
|
25
|
+
return STATUS_MAP[raw.toLowerCase()] ?? 'unknown';
|
|
26
|
+
}
|
|
27
|
+
/** Extracts an ADR from a markdown document, when the document looks like one. */
|
|
28
|
+
export function parseAdrDocument(path, file) {
|
|
29
|
+
const headings = readStringArray(file.metadata['markdown.headings']);
|
|
30
|
+
const frontMatter = file.metadata['markdown.frontMatter'];
|
|
31
|
+
const front = typeof frontMatter === 'object' && frontMatter !== null ? frontMatter : {};
|
|
32
|
+
const paragraph = typeof file.metadata['markdown.firstParagraph'] === 'string'
|
|
33
|
+
? file.metadata['markdown.firstParagraph']
|
|
34
|
+
: '';
|
|
35
|
+
const firstHeading = headings[0] ?? '';
|
|
36
|
+
const looksLikeAdr = ADR_DIRECTORY.test(path) ||
|
|
37
|
+
ADR_FILENAME.test(basename(path)) ||
|
|
38
|
+
ADR_HEADING.test(firstHeading) ||
|
|
39
|
+
front.status !== undefined;
|
|
40
|
+
if (!looksLikeAdr)
|
|
41
|
+
return null;
|
|
42
|
+
const filenameDigits = /(\d{2,4})/.exec(stem(path));
|
|
43
|
+
const headingMatch = ADR_HEADING.exec(firstHeading);
|
|
44
|
+
let id = null;
|
|
45
|
+
if (filenameDigits !== null)
|
|
46
|
+
id = `ADR-${filenameDigits[1].padStart(3, '0')}`;
|
|
47
|
+
else if (headingMatch !== null && /\d/.test(headingMatch[1])) {
|
|
48
|
+
id = `ADR-${headingMatch[1].replace(/[^0-9]/g, '').padStart(3, '0')}`;
|
|
49
|
+
}
|
|
50
|
+
else if (front.id !== undefined)
|
|
51
|
+
id = front.id.toUpperCase();
|
|
52
|
+
const cleanedHeading = headingMatch !== null ? headingMatch[2] : firstHeading;
|
|
53
|
+
const title = (front.title ?? cleanedHeading).trim();
|
|
54
|
+
const status = normalizeStatus(front.status ?? STATUS_PATTERN.exec(paragraph)?.[1]);
|
|
55
|
+
const excerptSource = paragraph.length > 0 ? paragraph : title;
|
|
56
|
+
return {
|
|
57
|
+
path,
|
|
58
|
+
id: id ?? `ADR-${shortHash(path, 6).toUpperCase()}`,
|
|
59
|
+
title: title.length > 0 ? title : `Decision recorded in ${path}`,
|
|
60
|
+
status,
|
|
61
|
+
excerpt: truncate(redactSecrets(excerptSource), 320),
|
|
62
|
+
explicitId: id !== null,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Explicit decisions: ADR documents and dedicated architecture documentation. */
|
|
66
|
+
export function detectExplicitDecisions(input) {
|
|
67
|
+
const decisions = [];
|
|
68
|
+
const markdownFiles = [...input.parsed.values()]
|
|
69
|
+
.filter((file) => file.language === 'markdown')
|
|
70
|
+
.sort((a, b) => compareStrings(a.path, b.path));
|
|
71
|
+
for (const file of markdownFiles) {
|
|
72
|
+
const adr = parseAdrDocument(file.path, file);
|
|
73
|
+
if (adr === null)
|
|
74
|
+
continue;
|
|
75
|
+
decisions.push({
|
|
76
|
+
id: adr.id,
|
|
77
|
+
title: truncate(adr.title, 160),
|
|
78
|
+
status: adr.status,
|
|
79
|
+
source: adr.path,
|
|
80
|
+
origin: 'explicit',
|
|
81
|
+
kind: 'adr',
|
|
82
|
+
confidence: adr.status === 'unknown' ? 0.7 : 0.95,
|
|
83
|
+
evidence: [
|
|
84
|
+
{
|
|
85
|
+
source: adr.path,
|
|
86
|
+
detail: adr.explicitId ? 'document carries an ADR identifier' : 'document lives in a decision directory',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
source: adr.path,
|
|
90
|
+
detail: adr.status === 'unknown'
|
|
91
|
+
? 'no explicit status found in front matter or body'
|
|
92
|
+
: `status found in the document: ${adr.status}`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
excerpt: adr.excerpt,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Decision-like sections inside regular documentation (README, docs/*).
|
|
99
|
+
for (const file of markdownFiles) {
|
|
100
|
+
if (parseAdrDocument(file.path, file) !== null)
|
|
101
|
+
continue;
|
|
102
|
+
const headingDetails = file.metadata['markdown.headingDetails'];
|
|
103
|
+
if (!Array.isArray(headingDetails))
|
|
104
|
+
continue;
|
|
105
|
+
for (const heading of headingDetails) {
|
|
106
|
+
if (heading.level > 3 || !DECISION_SECTION.test(heading.title))
|
|
107
|
+
continue;
|
|
108
|
+
const isDedicatedDoc = /(^|\/)(architecture|design)(\.|\/)/i.test(file.path);
|
|
109
|
+
decisions.push({
|
|
110
|
+
id: `DOC-${shortHash(`${file.path}:${heading.line}`, 6).toUpperCase()}`,
|
|
111
|
+
title: truncate(heading.title, 160),
|
|
112
|
+
status: 'unknown',
|
|
113
|
+
source: `${file.path}#L${heading.line}`,
|
|
114
|
+
origin: isDedicatedDoc ? 'explicit' : 'inferred',
|
|
115
|
+
kind: 'documented-section',
|
|
116
|
+
confidence: isDedicatedDoc ? 0.6 : 0.45,
|
|
117
|
+
evidence: [
|
|
118
|
+
{ source: file.path, detail: `section "${heading.title}" (line ${heading.line}) matched decision vocabulary` },
|
|
119
|
+
{
|
|
120
|
+
source: file.path,
|
|
121
|
+
detail: isDedicatedDoc
|
|
122
|
+
? 'document is a dedicated architecture document'
|
|
123
|
+
: 'section may describe design context rather than a decision; verify before treating it as an ADR',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return decisions;
|
|
131
|
+
}
|
|
132
|
+
/** Configuration and dependency facts presented as *inferred* decisions. */
|
|
133
|
+
export function detectConfigDecisions(input) {
|
|
134
|
+
const decisions = [];
|
|
135
|
+
const workspace = input.repository.workspace;
|
|
136
|
+
if (workspace.rootPackage !== null) {
|
|
137
|
+
decisions.push({
|
|
138
|
+
id: 'INF-MODULE-FORMAT',
|
|
139
|
+
title: workspace.rootPackage.type === 'module'
|
|
140
|
+
? 'Packages use ES modules ("type": "module")'
|
|
141
|
+
: 'Packages use CommonJS ("type": "commonjs")',
|
|
142
|
+
status: 'accepted',
|
|
143
|
+
source: 'package.json',
|
|
144
|
+
origin: 'inferred',
|
|
145
|
+
kind: 'config-signal',
|
|
146
|
+
confidence: 0.9,
|
|
147
|
+
evidence: [{ source: 'package.json', detail: `"type": "${workspace.rootPackage.type ?? 'commonjs'}"` }],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (workspace.packageManager !== 'unknown') {
|
|
151
|
+
decisions.push({
|
|
152
|
+
id: 'INF-PACKAGE-MANAGER',
|
|
153
|
+
title: `Repository is maintained with ${workspace.packageManager}`,
|
|
154
|
+
status: 'accepted',
|
|
155
|
+
source: workspace.lockfiles[0] ?? 'package.json',
|
|
156
|
+
origin: 'inferred',
|
|
157
|
+
kind: 'config-signal',
|
|
158
|
+
confidence: 0.8,
|
|
159
|
+
evidence: workspace.packageManagerEvidence.slice(0, 3),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (workspace.isMonorepo) {
|
|
163
|
+
decisions.push({
|
|
164
|
+
id: 'INF-WORKSPACES',
|
|
165
|
+
title: `Repository is organised as a workspace monorepo (${workspace.workspaceGlobs.join(', ')})`,
|
|
166
|
+
status: 'accepted',
|
|
167
|
+
source: 'package.json',
|
|
168
|
+
origin: 'inferred',
|
|
169
|
+
kind: 'config-signal',
|
|
170
|
+
confidence: 0.95,
|
|
171
|
+
evidence: [{ source: 'package.json', detail: `workspaces: ${workspace.workspaceGlobs.join(', ')}` }],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const strictConfigs = [...input.parsed.values()].filter((file) => file.metadata['tsconfig.strict'] === true);
|
|
175
|
+
if (strictConfigs.length > 0) {
|
|
176
|
+
decisions.push({
|
|
177
|
+
id: 'INF-STRICT-TS',
|
|
178
|
+
title: 'TypeScript strict mode is mandatory',
|
|
179
|
+
status: 'accepted',
|
|
180
|
+
source: strictConfigs[0].path,
|
|
181
|
+
origin: 'inferred',
|
|
182
|
+
kind: 'config-signal',
|
|
183
|
+
confidence: 0.95,
|
|
184
|
+
evidence: strictConfigs.slice(0, 3).map((file) => ({ source: file.path, detail: '"strict": true' })),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const noteworthy = input.repository.externalDependencies.filter((entry) => entry.importance.value === 'framework' || entry.importance.value === 'infrastructure');
|
|
188
|
+
for (const dependency of noteworthy.slice(0, 4)) {
|
|
189
|
+
decisions.push({
|
|
190
|
+
id: `INF-DEP-${dependency.name.replace(/[^A-Za-z0-9]+/g, '-').toUpperCase()}`,
|
|
191
|
+
title: `The repository builds on ${dependency.name} (heuristic: ${dependency.importance.value})`,
|
|
192
|
+
status: 'accepted',
|
|
193
|
+
source: 'package.json',
|
|
194
|
+
origin: 'inferred',
|
|
195
|
+
kind: 'dependency-signal',
|
|
196
|
+
confidence: Math.min(dependency.importance.confidence, 0.7),
|
|
197
|
+
evidence: [
|
|
198
|
+
{ source: 'package.json', detail: `declared by ${dependency.declaredBy.join(', ') || 'the source code'}` },
|
|
199
|
+
{ source: '<graph>', detail: `imported by ${dependency.importedByModules} module(s)` },
|
|
200
|
+
],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return decisions;
|
|
204
|
+
}
|
|
205
|
+
/** Architectural change signals from git history, clearly marked as inferred. */
|
|
206
|
+
export function detectGitDecisions(input) {
|
|
207
|
+
if (input.architectureSignalPatterns.length === 0)
|
|
208
|
+
return [];
|
|
209
|
+
const patterns = input.architectureSignalPatterns.map((pattern) => pattern.toLowerCase());
|
|
210
|
+
const signals = [];
|
|
211
|
+
const sortedCommits = [...input.gitCommits].sort((a, b) => b.dateMs - a.dateMs);
|
|
212
|
+
for (const commit of sortedCommits) {
|
|
213
|
+
const subject = commit.subject.toLowerCase();
|
|
214
|
+
if (!patterns.some((pattern) => subject.includes(pattern)))
|
|
215
|
+
continue;
|
|
216
|
+
signals.push({
|
|
217
|
+
id: `GIT-${commit.sha.slice(0, 8)}`,
|
|
218
|
+
title: truncate(redactSecrets(commit.subject), 160),
|
|
219
|
+
status: 'unknown',
|
|
220
|
+
source: `git:${commit.sha.slice(0, 8)}`,
|
|
221
|
+
origin: 'inferred',
|
|
222
|
+
kind: 'git-signal',
|
|
223
|
+
confidence: 0.45,
|
|
224
|
+
evidence: [
|
|
225
|
+
{
|
|
226
|
+
source: `git:${commit.sha.slice(0, 8)}`,
|
|
227
|
+
detail: `commit message matched an architectural keyword (${formatDate(commit.dateMs)}, ${commit.files.length} file(s) changed)`,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
if (signals.length >= 5)
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
return signals;
|
|
235
|
+
}
|
|
236
|
+
/** Runs every decision detector; explicit decisions win id conflicts. */
|
|
237
|
+
export function detectDecisions(input) {
|
|
238
|
+
const combined = [...detectExplicitDecisions(input), ...detectConfigDecisions(input), ...detectGitDecisions(input)];
|
|
239
|
+
const seenIds = new Set();
|
|
240
|
+
const result = [];
|
|
241
|
+
for (const decision of combined) {
|
|
242
|
+
if (seenIds.has(decision.id))
|
|
243
|
+
continue;
|
|
244
|
+
seenIds.add(decision.id);
|
|
245
|
+
result.push(decision);
|
|
246
|
+
}
|
|
247
|
+
return result.sort((a, b) => (a.origin === b.origin ? compareStrings(a.id, b.id) : a.origin === 'explicit' ? -1 : 1));
|
|
248
|
+
}
|
|
249
|
+
/** Paths of ADR documents, used by `explain` and search. */
|
|
250
|
+
export function collectAdrPaths(parsed) {
|
|
251
|
+
return [...parsed.values()]
|
|
252
|
+
.filter((file) => file.language === 'markdown' && parseAdrDocument(file.path, file) !== null)
|
|
253
|
+
.map((file) => file.path)
|
|
254
|
+
.sort(compareStrings);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../../../src/core/decisions/detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context differ.
|
|
3
|
+
*
|
|
4
|
+
* Compares the previously persisted `.agent/` context with the freshly computed
|
|
5
|
+
* one and answers the questions an engineer has before committing: which files
|
|
6
|
+
* changed, which modules appeared, which architectural dependency moved, which
|
|
7
|
+
* conventions/decisions changed.
|
|
8
|
+
*
|
|
9
|
+
* Wording stays neutral: no "critical", no "unsafe" — just what changed.
|
|
10
|
+
*/
|
|
11
|
+
import type { ContextDiff } from '../../model/types.ts';
|
|
12
|
+
import type { PersistedContext } from '../serialization/reader.ts';
|
|
13
|
+
import type { ContextDocuments } from '../serialization/types.ts';
|
|
14
|
+
export interface DiffOptions {
|
|
15
|
+
/** Previously persisted context; `null` when there is nothing to compare against. */
|
|
16
|
+
previous: PersistedContext | null;
|
|
17
|
+
/** Freshly computed documents. */
|
|
18
|
+
current: ContextDocuments;
|
|
19
|
+
/** Hash of the previous canonical context (from the local state). */
|
|
20
|
+
baselineHash?: string | null;
|
|
21
|
+
}
|
|
22
|
+
/** Computes the context diff. */
|
|
23
|
+
export declare function diffContext(options: DiffOptions): ContextDiff;
|
|
24
|
+
/** Stable hash of the generated documents, stored locally as the diff baseline. */
|
|
25
|
+
export declare function hashDocuments(documents: ContextDocuments): string;
|
|
26
|
+
//# sourceMappingURL=differ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../../../src/core/diff/differ.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAWlE,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,kCAAkC;IAClC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAqBD,iCAAiC;AACjC,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAgI7D;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,SAAS,EAAE,gBAAgB,GAAG,MAAM,CAQjE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { compareStrings, hashValue } from "../../model/canonical.js";
|
|
2
|
+
function difference(current, previous) {
|
|
3
|
+
const previousSet = new Set(previous);
|
|
4
|
+
return [...current].filter((entry) => !previousSet.has(entry)).sort(compareStrings);
|
|
5
|
+
}
|
|
6
|
+
function edgeKey(edge) {
|
|
7
|
+
return `${edge.from} -> ${edge.to}`;
|
|
8
|
+
}
|
|
9
|
+
function emptyDiff(current, baseline) {
|
|
10
|
+
return {
|
|
11
|
+
files: { added: [], modified: [], removed: [] },
|
|
12
|
+
modules: { added: [], removed: [], roleChanges: [] },
|
|
13
|
+
entryPoints: { added: [], removed: [] },
|
|
14
|
+
dependencies: { added: [], removed: [], externalAdded: [], externalRemoved: [] },
|
|
15
|
+
conventions: { added: [], removed: [], changed: [] },
|
|
16
|
+
decisions: { added: [], removed: [] },
|
|
17
|
+
statistics: {
|
|
18
|
+
files: current.index.repository.counts.files,
|
|
19
|
+
modules: current.index.repository.counts.modules,
|
|
20
|
+
dependencies: current.index.repository.counts.dependencies,
|
|
21
|
+
entries: current.index.repository.counts.entryPoints,
|
|
22
|
+
},
|
|
23
|
+
hasChanges: false,
|
|
24
|
+
baseline,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Computes the context diff. */
|
|
28
|
+
export function diffContext(options) {
|
|
29
|
+
const { previous, current } = options;
|
|
30
|
+
const baseline = options.baselineHash ?? null;
|
|
31
|
+
if (previous === null || previous.index === null)
|
|
32
|
+
return emptyDiff(current, baseline);
|
|
33
|
+
const previousFiles = new Map(previous.index.files.entries.map((entry) => [entry.path, entry.hash]));
|
|
34
|
+
const currentFiles = new Map(current.index.files.entries.map((entry) => [entry.path, entry.hash]));
|
|
35
|
+
const addedFiles = [];
|
|
36
|
+
const modifiedFiles = [];
|
|
37
|
+
for (const [file, hash] of currentFiles) {
|
|
38
|
+
const before = previousFiles.get(file);
|
|
39
|
+
if (before === undefined)
|
|
40
|
+
addedFiles.push(file);
|
|
41
|
+
else if (before !== hash)
|
|
42
|
+
modifiedFiles.push(file);
|
|
43
|
+
}
|
|
44
|
+
const removedFiles = [...previousFiles.keys()].filter((file) => !currentFiles.has(file));
|
|
45
|
+
const previousModules = new Map((previous.architecture?.modules ?? []).map((module) => [module.id, module]));
|
|
46
|
+
const currentModules = new Map(current.architecture.modules.map((module) => [module.id, module]));
|
|
47
|
+
const modulesAdded = difference([...currentModules.keys()], [...previousModules.keys()]);
|
|
48
|
+
const modulesRemoved = difference([...previousModules.keys()], [...currentModules.keys()]);
|
|
49
|
+
const roleChanges = [];
|
|
50
|
+
for (const [id, module] of currentModules) {
|
|
51
|
+
const before = previousModules.get(id);
|
|
52
|
+
if (before === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
const beforeRoles = before.roles.map((role) => role.value);
|
|
55
|
+
const afterRoles = module.roles.map((role) => role.value);
|
|
56
|
+
const addedRoles = difference(afterRoles, beforeRoles);
|
|
57
|
+
const removedRoles = difference(beforeRoles, afterRoles);
|
|
58
|
+
if (addedRoles.length > 0 || removedRoles.length > 0) {
|
|
59
|
+
roleChanges.push({ moduleId: id, added: addedRoles, removed: removedRoles });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
roleChanges.sort((a, b) => compareStrings(a.moduleId, b.moduleId));
|
|
63
|
+
const previousEntries = (previous.architecture?.entryPoints ?? []).map((entry) => `${entry.path} (${entry.type})`);
|
|
64
|
+
const currentEntries = current.architecture.entryPoints.map((entry) => `${entry.path} (${entry.type})`);
|
|
65
|
+
const previousEdges = (previous.dependencies?.internal ?? []).map((edge) => edgeKey(edge));
|
|
66
|
+
const currentEdges = current.dependencies.internal.map((edge) => edgeKey(edge));
|
|
67
|
+
const addedEdgeKeys = difference(currentEdges, previousEdges);
|
|
68
|
+
const removedEdgeKeys = difference(previousEdges, currentEdges);
|
|
69
|
+
const currentEdgeByKey = new Map(current.dependencies.internal.map((edge) => [edgeKey(edge), edge]));
|
|
70
|
+
const previousEdgeByKey = new Map((previous.dependencies?.internal ?? []).map((edge) => [edgeKey(edge), edge]));
|
|
71
|
+
const previousExternal = (previous.dependencies?.external ?? []).map((entry) => entry.name);
|
|
72
|
+
const currentExternal = current.dependencies.external.map((entry) => entry.name);
|
|
73
|
+
const previousConventions = new Map((previous.conventions?.conventions ?? []).map((entry) => [entry.id, entry.statement]));
|
|
74
|
+
const currentConventions = new Map(current.conventions.conventions.map((entry) => [entry.id, entry.statement]));
|
|
75
|
+
const conventionsChanged = [];
|
|
76
|
+
for (const [id, statement] of currentConventions) {
|
|
77
|
+
const before = previousConventions.get(id);
|
|
78
|
+
if (before !== undefined && before !== statement) {
|
|
79
|
+
conventionsChanged.push(`${id}: "${before}" → "${statement}"`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const previousDecisions = (previous.decisions?.decisions ?? []).map((entry) => `${entry.id} ${entry.title}`);
|
|
83
|
+
const currentDecisions = current.decisions.decisions.map((entry) => `${entry.id} ${entry.title}`);
|
|
84
|
+
const diff = {
|
|
85
|
+
files: {
|
|
86
|
+
added: addedFiles.sort(compareStrings),
|
|
87
|
+
modified: modifiedFiles.sort(compareStrings),
|
|
88
|
+
removed: removedFiles.sort(compareStrings),
|
|
89
|
+
},
|
|
90
|
+
modules: { added: modulesAdded, removed: modulesRemoved, roleChanges },
|
|
91
|
+
entryPoints: {
|
|
92
|
+
added: difference(currentEntries, previousEntries),
|
|
93
|
+
removed: difference(previousEntries, currentEntries),
|
|
94
|
+
},
|
|
95
|
+
dependencies: {
|
|
96
|
+
added: addedEdgeKeys.flatMap((key) => {
|
|
97
|
+
const edge = currentEdgeByKey.get(key);
|
|
98
|
+
return edge === undefined
|
|
99
|
+
? []
|
|
100
|
+
: [{ from: edge.from, to: edge.to, relationship: 'depends-on', kind: edge.kind }];
|
|
101
|
+
}),
|
|
102
|
+
removed: removedEdgeKeys.flatMap((key) => {
|
|
103
|
+
const edge = previousEdgeByKey.get(key);
|
|
104
|
+
return edge === undefined
|
|
105
|
+
? []
|
|
106
|
+
: [{ from: edge.from, to: edge.to, relationship: 'depends-on', kind: edge.kind }];
|
|
107
|
+
}),
|
|
108
|
+
externalAdded: difference(currentExternal, previousExternal),
|
|
109
|
+
externalRemoved: difference(previousExternal, currentExternal),
|
|
110
|
+
},
|
|
111
|
+
conventions: {
|
|
112
|
+
added: difference([...currentConventions.keys()], [...previousConventions.keys()]),
|
|
113
|
+
removed: difference([...previousConventions.keys()], [...currentConventions.keys()]),
|
|
114
|
+
changed: conventionsChanged.sort(compareStrings),
|
|
115
|
+
},
|
|
116
|
+
decisions: {
|
|
117
|
+
added: difference(currentDecisions, previousDecisions),
|
|
118
|
+
removed: difference(previousDecisions, currentDecisions),
|
|
119
|
+
},
|
|
120
|
+
statistics: {
|
|
121
|
+
files: current.index.repository.counts.files,
|
|
122
|
+
modules: current.index.repository.counts.modules,
|
|
123
|
+
dependencies: current.index.repository.counts.dependencies,
|
|
124
|
+
entries: current.index.repository.counts.entryPoints,
|
|
125
|
+
},
|
|
126
|
+
hasChanges: false,
|
|
127
|
+
baseline,
|
|
128
|
+
};
|
|
129
|
+
diff.hasChanges =
|
|
130
|
+
diff.files.added.length > 0 ||
|
|
131
|
+
diff.files.modified.length > 0 ||
|
|
132
|
+
diff.files.removed.length > 0 ||
|
|
133
|
+
diff.modules.added.length > 0 ||
|
|
134
|
+
diff.modules.removed.length > 0 ||
|
|
135
|
+
diff.modules.roleChanges.length > 0 ||
|
|
136
|
+
diff.entryPoints.added.length > 0 ||
|
|
137
|
+
diff.entryPoints.removed.length > 0 ||
|
|
138
|
+
diff.dependencies.added.length > 0 ||
|
|
139
|
+
diff.dependencies.removed.length > 0 ||
|
|
140
|
+
diff.dependencies.externalAdded.length > 0 ||
|
|
141
|
+
diff.dependencies.externalRemoved.length > 0 ||
|
|
142
|
+
diff.conventions.added.length > 0 ||
|
|
143
|
+
diff.conventions.removed.length > 0 ||
|
|
144
|
+
diff.conventions.changed.length > 0 ||
|
|
145
|
+
diff.decisions.added.length > 0 ||
|
|
146
|
+
diff.decisions.removed.length > 0;
|
|
147
|
+
return diff;
|
|
148
|
+
}
|
|
149
|
+
/** Stable hash of the generated documents, stored locally as the diff baseline. */
|
|
150
|
+
export function hashDocuments(documents) {
|
|
151
|
+
return hashValue({
|
|
152
|
+
index: { ...documents.index, generatedAt: undefined },
|
|
153
|
+
architecture: { ...documents.architecture, generatedAt: undefined },
|
|
154
|
+
dependencies: { ...documents.dependencies, generatedAt: undefined },
|
|
155
|
+
conventions: { ...documents.conventions, generatedAt: undefined },
|
|
156
|
+
decisions: { ...documents.decisions, generatedAt: undefined },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=differ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.js","sourceRoot":"","sources":["../../../src/core/diff/differ.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git activity analysis.
|
|
3
|
+
*
|
|
4
|
+
* Terminology is deliberately neutral: `historicalChanges`, `recentChanges`,
|
|
5
|
+
* `changeFrequency`, `lastChanged`. A frequently changed file is *not* labelled
|
|
6
|
+
* critical — the numbers are reported and the reader decides.
|
|
7
|
+
*
|
|
8
|
+
* All data comes from `git log`, executed locally by GitAdapter.
|
|
9
|
+
*/
|
|
10
|
+
import type { ChangeFrequency, FileNode, GitFileSignals, GitModuleSignals, ModuleNode } from '../../model/types.ts';
|
|
11
|
+
import type { GitCommit } from '../../adapters/git/git-adapter.ts';
|
|
12
|
+
export interface GitActivityInput {
|
|
13
|
+
commits: GitCommit[];
|
|
14
|
+
files: FileNode[];
|
|
15
|
+
modules: ModuleNode[];
|
|
16
|
+
/** Window (days) counted as "recent activity". */
|
|
17
|
+
recentDays: number;
|
|
18
|
+
/** Patterns that mark a commit message as an architectural change signal. */
|
|
19
|
+
architectureSignalPatterns: string[];
|
|
20
|
+
/** Injectable clock (tests). */
|
|
21
|
+
now?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface GitActivityResult {
|
|
24
|
+
fileSignals: Map<string, GitFileSignals>;
|
|
25
|
+
moduleSignals: Map<string, GitModuleSignals>;
|
|
26
|
+
/** Number of commits inspected. */
|
|
27
|
+
commitCount: number;
|
|
28
|
+
}
|
|
29
|
+
/** Buckets a count against the quantiles of the repository distribution. */
|
|
30
|
+
export declare function bucket(count: number, lowThreshold: number, highThreshold: number): ChangeFrequency;
|
|
31
|
+
/** Computes per-file and per-module git signals from the parsed commit history. */
|
|
32
|
+
export declare function computeGitActivity(input: GitActivityInput): GitActivityResult;
|
|
33
|
+
/** Attaches the computed module signals to the repository modules. */
|
|
34
|
+
export declare function applyGitSignals(modules: ModuleNode[], activity: GitActivityResult): void;
|
|
35
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../../src/core/git-analysis/activity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACpH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAanE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,0BAA0B,EAAE,MAAM,EAAE,CAAC;IACrC,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC7C,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,eAAe,CAKlG;AAQD,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAoG7E;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAIxF"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { compareStrings } from "../../model/canonical.js";
|
|
2
|
+
import { formatDate, truncate } from "../../util/text.js";
|
|
3
|
+
const MS_PER_DAY = 86_400_000;
|
|
4
|
+
/** Buckets a count against the quantiles of the repository distribution. */
|
|
5
|
+
export function bucket(count, lowThreshold, highThreshold) {
|
|
6
|
+
if (count <= 0)
|
|
7
|
+
return 'none';
|
|
8
|
+
if (count >= highThreshold)
|
|
9
|
+
return 'high';
|
|
10
|
+
if (count >= lowThreshold)
|
|
11
|
+
return 'medium';
|
|
12
|
+
return 'low';
|
|
13
|
+
}
|
|
14
|
+
function quantile(sorted, fraction) {
|
|
15
|
+
if (sorted.length === 0)
|
|
16
|
+
return 0;
|
|
17
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.round(fraction * (sorted.length - 1))));
|
|
18
|
+
return sorted[index] ?? 0;
|
|
19
|
+
}
|
|
20
|
+
/** Computes per-file and per-module git signals from the parsed commit history. */
|
|
21
|
+
export function computeGitActivity(input) {
|
|
22
|
+
const now = input.now ?? Date.now();
|
|
23
|
+
const recentWindowStart = now - input.recentDays * MS_PER_DAY;
|
|
24
|
+
const moduleByFile = new Map();
|
|
25
|
+
for (const module of input.modules) {
|
|
26
|
+
for (const file of module.files)
|
|
27
|
+
moduleByFile.set(file, module.id);
|
|
28
|
+
}
|
|
29
|
+
const knownFiles = new Set(input.files.map((file) => file.path));
|
|
30
|
+
const fileActivity = new Map();
|
|
31
|
+
const moduleActivity = new Map();
|
|
32
|
+
const architectureSignals = new Map();
|
|
33
|
+
const patterns = input.architectureSignalPatterns.map((pattern) => pattern.toLowerCase());
|
|
34
|
+
const ensure = (map, key) => {
|
|
35
|
+
let record = map.get(key);
|
|
36
|
+
if (record === undefined) {
|
|
37
|
+
record = { shas: new Set(), recentShas: new Set(), lastDateMs: 0, authors: new Map() };
|
|
38
|
+
map.set(key, record);
|
|
39
|
+
}
|
|
40
|
+
return record;
|
|
41
|
+
};
|
|
42
|
+
let commitCount = 0;
|
|
43
|
+
for (const commit of input.commits) {
|
|
44
|
+
commitCount += 1;
|
|
45
|
+
const isRecent = commit.dateMs >= recentWindowStart;
|
|
46
|
+
const touchesArchitecture = patterns.some((pattern) => commit.subject.toLowerCase().includes(pattern));
|
|
47
|
+
const modulesTouched = new Set();
|
|
48
|
+
for (const rawPath of commit.files) {
|
|
49
|
+
const path = rawPath.replace(/\\/g, '/');
|
|
50
|
+
if (!knownFiles.has(path))
|
|
51
|
+
continue;
|
|
52
|
+
const activity = ensure(fileActivity, path);
|
|
53
|
+
activity.shas.add(commit.sha);
|
|
54
|
+
if (isRecent)
|
|
55
|
+
activity.recentShas.add(commit.sha);
|
|
56
|
+
activity.lastDateMs = Math.max(activity.lastDateMs, commit.dateMs);
|
|
57
|
+
activity.authors.set(commit.author, (activity.authors.get(commit.author) ?? 0) + 1);
|
|
58
|
+
const moduleId = moduleByFile.get(path);
|
|
59
|
+
if (moduleId === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
const moduleRecord = ensure(moduleActivity, moduleId);
|
|
62
|
+
moduleRecord.shas.add(commit.sha);
|
|
63
|
+
if (isRecent)
|
|
64
|
+
moduleRecord.recentShas.add(commit.sha);
|
|
65
|
+
moduleRecord.lastDateMs = Math.max(moduleRecord.lastDateMs, commit.dateMs);
|
|
66
|
+
moduleRecord.authors.set(commit.author, (moduleRecord.authors.get(commit.author) ?? 0) + 1);
|
|
67
|
+
modulesTouched.add(moduleId);
|
|
68
|
+
}
|
|
69
|
+
if (touchesArchitecture && commit.sha.length > 0) {
|
|
70
|
+
for (const moduleId of modulesTouched) {
|
|
71
|
+
const list = architectureSignals.get(moduleId) ?? [];
|
|
72
|
+
if (list.length < 5) {
|
|
73
|
+
list.push({
|
|
74
|
+
sha: commit.sha.slice(0, 8),
|
|
75
|
+
date: formatDate(commit.dateMs),
|
|
76
|
+
subject: truncate(commit.subject, 120),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
architectureSignals.set(moduleId, list);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const fileCounts = [...fileActivity.values()].map((activity) => activity.shas.size).sort((a, b) => a - b);
|
|
84
|
+
const moduleCounts = [...moduleActivity.values()].map((activity) => activity.shas.size).sort((a, b) => a - b);
|
|
85
|
+
const fileLow = quantile(fileCounts, 0.33);
|
|
86
|
+
const fileHigh = quantile(fileCounts, 0.66);
|
|
87
|
+
const moduleLow = quantile(moduleCounts, 0.33);
|
|
88
|
+
const moduleHigh = quantile(moduleCounts, 0.66);
|
|
89
|
+
const fileSignals = new Map();
|
|
90
|
+
for (const [path, activity] of fileActivity) {
|
|
91
|
+
fileSignals.set(path, {
|
|
92
|
+
historicalChanges: activity.shas.size,
|
|
93
|
+
recentChanges: activity.recentShas.size,
|
|
94
|
+
lastChanged: activity.lastDateMs > 0 ? formatDate(activity.lastDateMs) : null,
|
|
95
|
+
changeFrequency: bucket(activity.shas.size, Math.max(1, fileLow), Math.max(2, fileHigh)),
|
|
96
|
+
contributorCount: activity.authors.size,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const moduleSignals = new Map();
|
|
100
|
+
for (const [moduleId, activity] of moduleActivity) {
|
|
101
|
+
const topContributors = [...activity.authors.entries()]
|
|
102
|
+
.map(([name, commits]) => ({ name, commits }))
|
|
103
|
+
.sort((a, b) => (b.commits === a.commits ? compareStrings(a.name, b.name) : b.commits - a.commits))
|
|
104
|
+
.slice(0, 3);
|
|
105
|
+
moduleSignals.set(moduleId, {
|
|
106
|
+
historicalChanges: activity.shas.size,
|
|
107
|
+
recentChanges: activity.recentShas.size,
|
|
108
|
+
lastChanged: activity.lastDateMs > 0 ? formatDate(activity.lastDateMs) : null,
|
|
109
|
+
changeFrequency: bucket(activity.shas.size, Math.max(1, moduleLow), Math.max(2, moduleHigh)),
|
|
110
|
+
contributorCount: activity.authors.size,
|
|
111
|
+
topContributors,
|
|
112
|
+
architectureChangeSignals: architectureSignals.get(moduleId) ?? [],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return { fileSignals, moduleSignals, commitCount };
|
|
116
|
+
}
|
|
117
|
+
/** Attaches the computed module signals to the repository modules. */
|
|
118
|
+
export function applyGitSignals(modules, activity) {
|
|
119
|
+
for (const module of modules) {
|
|
120
|
+
module.git = activity.moduleSignals.get(module.id) ?? null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=activity.js.map
|