@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.
Files changed (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +285 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/adapters/filesystem/fs-adapter.d.ts +50 -0
  5. package/dist/adapters/filesystem/fs-adapter.d.ts.map +1 -0
  6. package/dist/adapters/filesystem/fs-adapter.js +168 -0
  7. package/dist/adapters/filesystem/fs-adapter.js.map +1 -0
  8. package/dist/adapters/filesystem/sensitive.d.ts +26 -0
  9. package/dist/adapters/filesystem/sensitive.d.ts.map +1 -0
  10. package/dist/adapters/filesystem/sensitive.js +67 -0
  11. package/dist/adapters/filesystem/sensitive.js.map +1 -0
  12. package/dist/adapters/git/git-adapter.d.ts +47 -0
  13. package/dist/adapters/git/git-adapter.d.ts.map +1 -0
  14. package/dist/adapters/git/git-adapter.js +109 -0
  15. package/dist/adapters/git/git-adapter.js.map +1 -0
  16. package/dist/adapters/language/json/adapter.d.ts +24 -0
  17. package/dist/adapters/language/json/adapter.d.ts.map +1 -0
  18. package/dist/adapters/language/json/adapter.js +54 -0
  19. package/dist/adapters/language/json/adapter.js.map +1 -0
  20. package/dist/adapters/language/markdown/adapter.d.ts +41 -0
  21. package/dist/adapters/language/markdown/adapter.d.ts.map +1 -0
  22. package/dist/adapters/language/markdown/adapter.js +124 -0
  23. package/dist/adapters/language/markdown/adapter.js.map +1 -0
  24. package/dist/adapters/language/package-json/adapter.d.ts +47 -0
  25. package/dist/adapters/language/package-json/adapter.d.ts.map +1 -0
  26. package/dist/adapters/language/package-json/adapter.js +153 -0
  27. package/dist/adapters/language/package-json/adapter.js.map +1 -0
  28. package/dist/adapters/language/registry.d.ts +35 -0
  29. package/dist/adapters/language/registry.d.ts.map +1 -0
  30. package/dist/adapters/language/registry.js +79 -0
  31. package/dist/adapters/language/registry.js.map +1 -0
  32. package/dist/adapters/language/ts-js/adapter.d.ts +14 -0
  33. package/dist/adapters/language/ts-js/adapter.d.ts.map +1 -0
  34. package/dist/adapters/language/ts-js/adapter.js +59 -0
  35. package/dist/adapters/language/ts-js/adapter.js.map +1 -0
  36. package/dist/adapters/language/ts-js/extractor.d.ts +21 -0
  37. package/dist/adapters/language/ts-js/extractor.d.ts.map +1 -0
  38. package/dist/adapters/language/ts-js/extractor.js +779 -0
  39. package/dist/adapters/language/ts-js/extractor.js.map +1 -0
  40. package/dist/adapters/language/ts-js/lexer.d.ts +30 -0
  41. package/dist/adapters/language/ts-js/lexer.d.ts.map +1 -0
  42. package/dist/adapters/language/ts-js/lexer.js +446 -0
  43. package/dist/adapters/language/ts-js/lexer.js.map +1 -0
  44. package/dist/adapters/language/ts-js/package-signals.d.ts +5 -0
  45. package/dist/adapters/language/ts-js/package-signals.d.ts.map +1 -0
  46. package/dist/adapters/language/ts-js/package-signals.js +113 -0
  47. package/dist/adapters/language/ts-js/package-signals.js.map +1 -0
  48. package/dist/adapters/language/tsconfig/adapter.d.ts +38 -0
  49. package/dist/adapters/language/tsconfig/adapter.d.ts.map +1 -0
  50. package/dist/adapters/language/tsconfig/adapter.js +130 -0
  51. package/dist/adapters/language/tsconfig/adapter.js.map +1 -0
  52. package/dist/adapters/language/types.d.ts +152 -0
  53. package/dist/adapters/language/types.d.ts.map +1 -0
  54. package/dist/adapters/language/types.js +51 -0
  55. package/dist/adapters/language/types.js.map +1 -0
  56. package/dist/agent-context.d.ts +122 -0
  57. package/dist/agent-context.d.ts.map +1 -0
  58. package/dist/agent-context.js +243 -0
  59. package/dist/agent-context.js.map +1 -0
  60. package/dist/config/defaults.d.ts +13 -0
  61. package/dist/config/defaults.d.ts.map +1 -0
  62. package/dist/config/defaults.js +98 -0
  63. package/dist/config/defaults.js.map +1 -0
  64. package/dist/config/load.d.ts +25 -0
  65. package/dist/config/load.d.ts.map +1 -0
  66. package/dist/config/load.js +106 -0
  67. package/dist/config/load.js.map +1 -0
  68. package/dist/config/types.d.ts +95 -0
  69. package/dist/config/types.d.ts.map +1 -0
  70. package/dist/config/types.js +8 -0
  71. package/dist/config/types.js.map +1 -0
  72. package/dist/config/validate.d.ts +15 -0
  73. package/dist/config/validate.d.ts.map +1 -0
  74. package/dist/config/validate.js +202 -0
  75. package/dist/config/validate.js.map +1 -0
  76. package/dist/core/architecture/detector.d.ts +10 -0
  77. package/dist/core/architecture/detector.d.ts.map +1 -0
  78. package/dist/core/architecture/detector.js +37 -0
  79. package/dist/core/architecture/detector.js.map +1 -0
  80. package/dist/core/architecture/entry-points.d.ts +20 -0
  81. package/dist/core/architecture/entry-points.d.ts.map +1 -0
  82. package/dist/core/architecture/entry-points.js +190 -0
  83. package/dist/core/architecture/entry-points.js.map +1 -0
  84. package/dist/core/architecture/role-detector.d.ts +32 -0
  85. package/dist/core/architecture/role-detector.d.ts.map +1 -0
  86. package/dist/core/architecture/role-detector.js +240 -0
  87. package/dist/core/architecture/role-detector.js.map +1 -0
  88. package/dist/core/context/explain.d.ts +36 -0
  89. package/dist/core/context/explain.d.ts.map +1 -0
  90. package/dist/core/context/explain.js +199 -0
  91. package/dist/core/context/explain.js.map +1 -0
  92. package/dist/core/context/repository-context.d.ts +16 -0
  93. package/dist/core/context/repository-context.d.ts.map +1 -0
  94. package/dist/core/context/repository-context.js +134 -0
  95. package/dist/core/context/repository-context.js.map +1 -0
  96. package/dist/core/context/types.d.ts +94 -0
  97. package/dist/core/context/types.d.ts.map +1 -0
  98. package/dist/core/context/types.js +2 -0
  99. package/dist/core/context/types.js.map +1 -0
  100. package/dist/core/conventions/detector.d.ts +55 -0
  101. package/dist/core/conventions/detector.d.ts.map +1 -0
  102. package/dist/core/conventions/detector.js +438 -0
  103. package/dist/core/conventions/detector.js.map +1 -0
  104. package/dist/core/conventions/renderer.d.ts +16 -0
  105. package/dist/core/conventions/renderer.d.ts.map +1 -0
  106. package/dist/core/conventions/renderer.js +42 -0
  107. package/dist/core/conventions/renderer.js.map +1 -0
  108. package/dist/core/decisions/detector.d.ts +45 -0
  109. package/dist/core/decisions/detector.d.ts.map +1 -0
  110. package/dist/core/decisions/detector.js +256 -0
  111. package/dist/core/decisions/detector.js.map +1 -0
  112. package/dist/core/diff/differ.d.ts +26 -0
  113. package/dist/core/diff/differ.d.ts.map +1 -0
  114. package/dist/core/diff/differ.js +159 -0
  115. package/dist/core/diff/differ.js.map +1 -0
  116. package/dist/core/git-analysis/activity.d.ts +35 -0
  117. package/dist/core/git-analysis/activity.d.ts.map +1 -0
  118. package/dist/core/git-analysis/activity.js +123 -0
  119. package/dist/core/git-analysis/activity.js.map +1 -0
  120. package/dist/core/graph/aliases.d.ts +20 -0
  121. package/dist/core/graph/aliases.d.ts.map +1 -0
  122. package/dist/core/graph/aliases.js +99 -0
  123. package/dist/core/graph/aliases.js.map +1 -0
  124. package/dist/core/graph/knowledge-graph.d.ts +91 -0
  125. package/dist/core/graph/knowledge-graph.d.ts.map +1 -0
  126. package/dist/core/graph/knowledge-graph.js +572 -0
  127. package/dist/core/graph/knowledge-graph.js.map +1 -0
  128. package/dist/core/graph/modules.d.ts +28 -0
  129. package/dist/core/graph/modules.d.ts.map +1 -0
  130. package/dist/core/graph/modules.js +139 -0
  131. package/dist/core/graph/modules.js.map +1 -0
  132. package/dist/core/graph/resolver.d.ts +50 -0
  133. package/dist/core/graph/resolver.d.ts.map +1 -0
  134. package/dist/core/graph/resolver.js +180 -0
  135. package/dist/core/graph/resolver.js.map +1 -0
  136. package/dist/core/scanner/cache.d.ts +49 -0
  137. package/dist/core/scanner/cache.d.ts.map +1 -0
  138. package/dist/core/scanner/cache.js +101 -0
  139. package/dist/core/scanner/cache.js.map +1 -0
  140. package/dist/core/scanner/scanner.d.ts +60 -0
  141. package/dist/core/scanner/scanner.d.ts.map +1 -0
  142. package/dist/core/scanner/scanner.js +304 -0
  143. package/dist/core/scanner/scanner.js.map +1 -0
  144. package/dist/core/scanner/types.d.ts +47 -0
  145. package/dist/core/scanner/types.d.ts.map +1 -0
  146. package/dist/core/scanner/types.js +18 -0
  147. package/dist/core/scanner/types.js.map +1 -0
  148. package/dist/core/search/analyzer.d.ts +16 -0
  149. package/dist/core/search/analyzer.d.ts.map +1 -0
  150. package/dist/core/search/analyzer.js +165 -0
  151. package/dist/core/search/analyzer.js.map +1 -0
  152. package/dist/core/search/bm25.d.ts +21 -0
  153. package/dist/core/search/bm25.d.ts.map +1 -0
  154. package/dist/core/search/bm25.js +108 -0
  155. package/dist/core/search/bm25.js.map +1 -0
  156. package/dist/core/search/documents.d.ts +20 -0
  157. package/dist/core/search/documents.d.ts.map +1 -0
  158. package/dist/core/search/documents.js +106 -0
  159. package/dist/core/search/documents.js.map +1 -0
  160. package/dist/core/search/service.d.ts +30 -0
  161. package/dist/core/search/service.d.ts.map +1 -0
  162. package/dist/core/search/service.js +54 -0
  163. package/dist/core/search/service.js.map +1 -0
  164. package/dist/core/search/types.d.ts +44 -0
  165. package/dist/core/search/types.d.ts.map +1 -0
  166. package/dist/core/search/types.js +2 -0
  167. package/dist/core/search/types.js.map +1 -0
  168. package/dist/core/serialization/documents.d.ts +26 -0
  169. package/dist/core/serialization/documents.d.ts.map +1 -0
  170. package/dist/core/serialization/documents.js +224 -0
  171. package/dist/core/serialization/documents.js.map +1 -0
  172. package/dist/core/serialization/reader.d.ts +22 -0
  173. package/dist/core/serialization/reader.d.ts.map +1 -0
  174. package/dist/core/serialization/reader.js +65 -0
  175. package/dist/core/serialization/reader.js.map +1 -0
  176. package/dist/core/serialization/types.d.ts +184 -0
  177. package/dist/core/serialization/types.d.ts.map +1 -0
  178. package/dist/core/serialization/types.js +2 -0
  179. package/dist/core/serialization/types.js.map +1 -0
  180. package/dist/core/serialization/writer.d.ts +17 -0
  181. package/dist/core/serialization/writer.d.ts.map +1 -0
  182. package/dist/core/serialization/writer.js +64 -0
  183. package/dist/core/serialization/writer.js.map +1 -0
  184. package/dist/core/workspace/package-facts.d.ts +12 -0
  185. package/dist/core/workspace/package-facts.d.ts.map +1 -0
  186. package/dist/core/workspace/package-facts.js +50 -0
  187. package/dist/core/workspace/package-facts.js.map +1 -0
  188. package/dist/core/workspace/workspace.d.ts +32 -0
  189. package/dist/core/workspace/workspace.d.ts.map +1 -0
  190. package/dist/core/workspace/workspace.js +167 -0
  191. package/dist/core/workspace/workspace.js.map +1 -0
  192. package/dist/errors.d.ts +50 -0
  193. package/dist/errors.d.ts.map +1 -0
  194. package/dist/errors.js +64 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/index.d.ts +80 -0
  197. package/dist/index.d.ts.map +1 -0
  198. package/dist/index.js +66 -0
  199. package/dist/index.js.map +1 -0
  200. package/dist/logging/diagnostics.d.ts +64 -0
  201. package/dist/logging/diagnostics.d.ts.map +1 -0
  202. package/dist/logging/diagnostics.js +92 -0
  203. package/dist/logging/diagnostics.js.map +1 -0
  204. package/dist/model/canonical.d.ts +24 -0
  205. package/dist/model/canonical.d.ts.map +1 -0
  206. package/dist/model/canonical.js +69 -0
  207. package/dist/model/canonical.js.map +1 -0
  208. package/dist/model/schema.d.ts +37 -0
  209. package/dist/model/schema.d.ts.map +1 -0
  210. package/dist/model/schema.js +42 -0
  211. package/dist/model/schema.js.map +1 -0
  212. package/dist/model/types.d.ts +402 -0
  213. package/dist/model/types.d.ts.map +1 -0
  214. package/dist/model/types.js +10 -0
  215. package/dist/model/types.js.map +1 -0
  216. package/dist/pipeline/scan-pipeline.d.ts +69 -0
  217. package/dist/pipeline/scan-pipeline.d.ts.map +1 -0
  218. package/dist/pipeline/scan-pipeline.js +270 -0
  219. package/dist/pipeline/scan-pipeline.js.map +1 -0
  220. package/dist/util/concurrency.d.ts +17 -0
  221. package/dist/util/concurrency.d.ts.map +1 -0
  222. package/dist/util/concurrency.js +46 -0
  223. package/dist/util/concurrency.js.map +1 -0
  224. package/dist/util/glob.d.ts +26 -0
  225. package/dist/util/glob.d.ts.map +1 -0
  226. package/dist/util/glob.js +198 -0
  227. package/dist/util/glob.js.map +1 -0
  228. package/dist/util/paths.d.ts +23 -0
  229. package/dist/util/paths.d.ts.map +1 -0
  230. package/dist/util/paths.js +115 -0
  231. package/dist/util/paths.js.map +1 -0
  232. package/dist/util/text.d.ts +22 -0
  233. package/dist/util/text.d.ts.map +1 -0
  234. package/dist/util/text.js +66 -0
  235. package/dist/util/text.js.map +1 -0
  236. 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