@vpxa/kb 0.1.1 → 0.1.3

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 (138) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/packages/analyzers/dist/blast-radius-analyzer.js +13 -114
  4. package/packages/analyzers/dist/dependency-analyzer.js +11 -425
  5. package/packages/analyzers/dist/diagram-generator.js +4 -86
  6. package/packages/analyzers/dist/entry-point-analyzer.js +5 -239
  7. package/packages/analyzers/dist/index.js +1 -23
  8. package/packages/analyzers/dist/knowledge-producer.js +24 -113
  9. package/packages/analyzers/dist/pattern-analyzer.js +5 -359
  10. package/packages/analyzers/dist/regex-call-graph.js +1 -428
  11. package/packages/analyzers/dist/structure-analyzer.js +4 -258
  12. package/packages/analyzers/dist/symbol-analyzer.js +13 -442
  13. package/packages/analyzers/dist/ts-call-graph.js +1 -160
  14. package/packages/analyzers/dist/types.js +0 -1
  15. package/packages/chunker/dist/call-graph-extractor.js +1 -90
  16. package/packages/chunker/dist/chunker-factory.js +1 -36
  17. package/packages/chunker/dist/chunker.interface.js +0 -1
  18. package/packages/chunker/dist/code-chunker.js +14 -134
  19. package/packages/chunker/dist/generic-chunker.js +5 -72
  20. package/packages/chunker/dist/index.js +1 -21
  21. package/packages/chunker/dist/markdown-chunker.js +7 -119
  22. package/packages/chunker/dist/treesitter-chunker.js +8 -234
  23. package/packages/cli/dist/commands/analyze.js +3 -112
  24. package/packages/cli/dist/commands/context-cmds.js +1 -155
  25. package/packages/cli/dist/commands/environment.js +2 -204
  26. package/packages/cli/dist/commands/execution.js +1 -137
  27. package/packages/cli/dist/commands/graph.js +7 -81
  28. package/packages/cli/dist/commands/init.js +9 -87
  29. package/packages/cli/dist/commands/knowledge.js +1 -139
  30. package/packages/cli/dist/commands/search.js +8 -267
  31. package/packages/cli/dist/commands/system.js +4 -241
  32. package/packages/cli/dist/commands/workspace.js +2 -388
  33. package/packages/cli/dist/context.js +1 -14
  34. package/packages/cli/dist/helpers.js +3 -458
  35. package/packages/cli/dist/index.d.ts +1 -1
  36. package/packages/cli/dist/index.js +3 -69
  37. package/packages/cli/dist/kb-init.js +1 -82
  38. package/packages/cli/dist/types.js +0 -1
  39. package/packages/core/dist/constants.js +1 -43
  40. package/packages/core/dist/content-detector.js +1 -79
  41. package/packages/core/dist/errors.js +1 -40
  42. package/packages/core/dist/index.js +1 -9
  43. package/packages/core/dist/logger.js +1 -34
  44. package/packages/core/dist/types.js +0 -1
  45. package/packages/embeddings/dist/embedder.interface.js +0 -1
  46. package/packages/embeddings/dist/index.js +1 -5
  47. package/packages/embeddings/dist/onnx-embedder.js +1 -82
  48. package/packages/indexer/dist/file-hasher.js +1 -13
  49. package/packages/indexer/dist/filesystem-crawler.js +1 -125
  50. package/packages/indexer/dist/graph-extractor.js +1 -111
  51. package/packages/indexer/dist/incremental-indexer.js +1 -278
  52. package/packages/indexer/dist/index.js +1 -14
  53. package/packages/server/dist/api.js +1 -9
  54. package/packages/server/dist/config.js +1 -75
  55. package/packages/server/dist/curated-manager.js +9 -356
  56. package/packages/server/dist/index.js +1 -134
  57. package/packages/server/dist/replay-interceptor.js +1 -38
  58. package/packages/server/dist/resources/resources.js +2 -40
  59. package/packages/server/dist/server.js +1 -247
  60. package/packages/server/dist/tools/analyze.tools.js +1 -288
  61. package/packages/server/dist/tools/forge.tools.js +11 -499
  62. package/packages/server/dist/tools/forget.tool.js +3 -39
  63. package/packages/server/dist/tools/graph.tool.js +5 -110
  64. package/packages/server/dist/tools/list.tool.js +5 -53
  65. package/packages/server/dist/tools/lookup.tool.js +8 -51
  66. package/packages/server/dist/tools/onboard.tool.js +2 -112
  67. package/packages/server/dist/tools/produce.tool.js +4 -74
  68. package/packages/server/dist/tools/read.tool.js +4 -47
  69. package/packages/server/dist/tools/reindex.tool.js +2 -70
  70. package/packages/server/dist/tools/remember.tool.js +3 -42
  71. package/packages/server/dist/tools/replay.tool.js +6 -88
  72. package/packages/server/dist/tools/search.tool.js +17 -327
  73. package/packages/server/dist/tools/status.tool.js +3 -68
  74. package/packages/server/dist/tools/toolkit.tools.js +20 -1673
  75. package/packages/server/dist/tools/update.tool.js +3 -39
  76. package/packages/server/dist/tools/utility.tools.js +19 -456
  77. package/packages/store/dist/graph-store.interface.js +0 -1
  78. package/packages/store/dist/index.js +1 -9
  79. package/packages/store/dist/lance-store.js +1 -258
  80. package/packages/store/dist/sqlite-graph-store.js +8 -309
  81. package/packages/store/dist/store-factory.js +1 -14
  82. package/packages/store/dist/store.interface.js +0 -1
  83. package/packages/tools/dist/batch.js +1 -45
  84. package/packages/tools/dist/changelog.js +2 -112
  85. package/packages/tools/dist/check.js +2 -59
  86. package/packages/tools/dist/checkpoint.js +2 -43
  87. package/packages/tools/dist/codemod.js +2 -69
  88. package/packages/tools/dist/compact.js +3 -60
  89. package/packages/tools/dist/data-transform.js +1 -124
  90. package/packages/tools/dist/dead-symbols.js +2 -71
  91. package/packages/tools/dist/delegate.js +3 -128
  92. package/packages/tools/dist/diff-parse.js +3 -153
  93. package/packages/tools/dist/digest.js +7 -242
  94. package/packages/tools/dist/encode.js +1 -46
  95. package/packages/tools/dist/env-info.js +1 -58
  96. package/packages/tools/dist/eval.js +3 -79
  97. package/packages/tools/dist/evidence-map.js +3 -203
  98. package/packages/tools/dist/file-summary.js +2 -106
  99. package/packages/tools/dist/file-walk.js +1 -75
  100. package/packages/tools/dist/find-examples.js +3 -48
  101. package/packages/tools/dist/find.js +1 -120
  102. package/packages/tools/dist/forge-classify.js +2 -319
  103. package/packages/tools/dist/forge-ground.js +1 -184
  104. package/packages/tools/dist/git-context.js +3 -46
  105. package/packages/tools/dist/graph-query.js +1 -194
  106. package/packages/tools/dist/health.js +1 -118
  107. package/packages/tools/dist/http-request.js +1 -58
  108. package/packages/tools/dist/index.js +1 -273
  109. package/packages/tools/dist/lane.js +7 -227
  110. package/packages/tools/dist/measure.js +2 -119
  111. package/packages/tools/dist/onboard.js +42 -1136
  112. package/packages/tools/dist/parse-output.js +2 -158
  113. package/packages/tools/dist/process-manager.js +1 -69
  114. package/packages/tools/dist/queue.js +2 -126
  115. package/packages/tools/dist/regex-test.js +1 -39
  116. package/packages/tools/dist/rename.js +2 -70
  117. package/packages/tools/dist/replay.js +6 -108
  118. package/packages/tools/dist/schema-validate.js +1 -141
  119. package/packages/tools/dist/scope-map.js +1 -72
  120. package/packages/tools/dist/snippet.js +1 -80
  121. package/packages/tools/dist/stash.js +2 -60
  122. package/packages/tools/dist/stratum-card.js +5 -238
  123. package/packages/tools/dist/symbol.js +3 -87
  124. package/packages/tools/dist/test-run.js +2 -55
  125. package/packages/tools/dist/text-utils.js +2 -31
  126. package/packages/tools/dist/time-utils.js +1 -135
  127. package/packages/tools/dist/trace.js +2 -114
  128. package/packages/tools/dist/truncation.js +10 -41
  129. package/packages/tools/dist/watch.js +1 -61
  130. package/packages/tools/dist/web-fetch.js +9 -244
  131. package/packages/tools/dist/web-search.js +1 -46
  132. package/packages/tools/dist/workset.js +2 -77
  133. package/packages/tui/dist/App.js +260 -52468
  134. package/packages/tui/dist/index.js +286 -54551
  135. package/packages/tui/dist/panels/CuratedPanel.js +211 -34291
  136. package/packages/tui/dist/panels/LogPanel.js +259 -51703
  137. package/packages/tui/dist/panels/SearchPanel.js +212 -34824
  138. package/packages/tui/dist/panels/StatusPanel.js +211 -34304
@@ -1,1139 +1,45 @@
1
- import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
- import { readdir, readFile } from "node:fs/promises";
3
- import { basename, join, relative, resolve } from "node:path";
4
- import {
5
- DependencyAnalyzer,
6
- DiagramGenerator,
7
- EntryPointAnalyzer,
8
- extractRegexCallGraph,
9
- extractTsCallGraph,
10
- PatternAnalyzer,
11
- StructureAnalyzer,
12
- SymbolAnalyzer
13
- } from "@kb/analyzers";
14
- const DISPLAY_TITLES = {
15
- structure: "Project Structure",
16
- dependencies: "Dependencies",
17
- "entry-points": "Entry Points",
18
- symbols: "Symbols",
19
- patterns: "Patterns",
20
- diagram: "C4 Container Diagram",
21
- "code-map": "Code Map (Module Graph)",
22
- "config-values": "Configuration Values",
23
- "synthesis-guide": "Synthesis Guide"
24
- };
25
- function buildSynthesisGuide(steps, mode, projectName, dataMap) {
26
- const lines = [`Analysis baselines for **${projectName}** have been generated.`];
27
- if (mode === "generate") {
28
- lines.push(
29
- "Individual results are in the sibling `.md` and `.json` files in this directory.",
30
- ""
31
- );
32
- } else {
33
- lines.push("Results are stored in the KB vector store.", "");
34
- }
35
- const symbolsData = dataMap.get("symbols");
36
- const depsData = dataMap.get("dependencies");
37
- const patternsData = dataMap.get("patterns");
38
- const entryData = dataMap.get("entry-points");
39
- const totalSymbols = symbolsData?.totalCount ?? 0;
40
- const exportedSymbols = symbolsData?.exportedCount ?? 0;
41
- const totalImports = depsData?.totalImports ?? 0;
42
- const entryCount = entryData?.total ?? 0;
43
- const patternList = patternsData?.patterns ?? [];
44
- const patternNames = patternList.map((p) => p.pattern);
45
- const isSpring = patternNames.some((p) => p.startsWith("Spring"));
46
- const isCdk = patternNames.includes("AWS CDK") || patternNames.includes("CDK IaC");
47
- const isMaven = patternNames.includes("Maven");
48
- const isServerless = patternNames.includes("Serverless") || entryCount > 3;
49
- const externalPkgs = depsData?.external ? Object.keys(depsData.external) : [];
50
- const isNode = externalPkgs.some(
51
- (d) => ["express", "fastify", "next", "react", "vitest", "jest"].includes(d)
52
- ) || totalImports > 0;
53
- const isMonorepo = externalPkgs.some((d) => ["turbo", "lerna", "nx"].includes(d)) || patternNames.includes("Monorepo");
54
- lines.push("### Project Profile", "");
55
- lines.push(
56
- `- **${totalSymbols} symbols** (${exportedSymbols} exported), **${totalImports} imports**, **${entryCount} entry ${entryCount === 1 ? "point" : "points"}**`
57
- );
58
- if (patternNames.length > 0) {
59
- lines.push(`- **Detected**: ${patternNames.slice(0, 8).join(", ")}`);
60
- }
61
- lines.push("");
62
- if (totalSymbols === 0 && totalImports === 0 && entryCount === 0) {
63
- lines.push(
64
- "> **Note:** This project appears to be empty or contains no analyzable source code.",
65
- "> Run onboard again after adding source files."
66
- );
67
- return lines.join("\n");
68
- }
69
- const completed = steps.filter((s) => s.status === "success");
70
- const failed = steps.filter((s) => s.status === "failed");
71
- lines.push("### Completed Analyses", "");
72
- for (const step of completed) {
73
- const title = DISPLAY_TITLES[step.name] ?? step.name;
74
- const size = step.output.length > 1e3 ? `${Math.round(step.output.length / 1024)}KB` : `${step.output.length}B`;
75
- const ext = ".md";
76
- if (mode === "generate") {
77
- lines.push(`- \u2713 [${title}](./${step.name}${ext}) (${size})`);
78
- } else {
79
- lines.push(`- \u2713 ${title} (${size})`);
80
- }
81
- }
82
- if (failed.length > 0) {
83
- lines.push("", "### Failed Analyses", "");
84
- for (const step of failed) {
85
- lines.push(`- \u2717 ${step.name}: ${step.error}`);
86
- }
87
- }
88
- lines.push(
89
- "",
90
- "### Recommended Reading Order",
91
- "",
92
- "1. **Start with** `synthesis-guide.md` (this file) \u2192 `entry-points.md` \u2192 `patterns.md`",
93
- "2. **Module graph** via `code-map.md` \u2014 cross-package call edges with function names",
94
- "3. **Architecture** via `diagram.md` (C4 Container) \u2192 `dependencies.md`",
95
- "4. **Browse structure** via `structure.md` for file layout",
96
- "5. **API surface** via `symbols.md` \u2014 file paths + exported symbols (capped at 80KB)",
97
- "6. **Reference**: `config-values.md` (config reference)",
98
- "",
99
- "> **Size guidance:** Total output is ~"
100
- );
101
- const totalKB = completed.reduce((sum, s) => sum + s.output.length, 0) / 1024;
102
- lines[lines.length - 1] += `${Math.round(totalKB)}KB. Focus on code-map.md + entry-points.md + diagram.md (~${Math.round((completed.find((s) => s.name === "code-map")?.output.length ?? 0) / 1024 + (completed.find((s) => s.name === "entry-points")?.output.length ?? 0) / 1024 + (completed.find((s) => s.name === "diagram")?.output.length ?? 0) / 1024)}KB) for maximum signal-to-token ratio.`;
103
- lines.push(
104
- "",
105
- "### Synthesize Knowledge",
106
- "",
107
- "Produce the following `kb_remember` entries:",
108
- ""
109
- );
110
- lines.push("1. **Architecture Summary** (category: `architecture`)");
111
- if (isMonorepo) {
112
- lines.push(" - Package boundaries, dependency graph between packages");
113
- lines.push(" - Shared vs service-specific code");
114
- } else if (isServerless) {
115
- lines.push(" - Lambda functions, triggers, event flow");
116
- lines.push(" - Infrastructure patterns (queues, tables, APIs)");
117
- } else if (isSpring) {
118
- lines.push(" - Controller \u2192 Service \u2192 Repository layers");
119
- lines.push(" - Spring configuration and profiles");
120
- } else {
121
- lines.push(" - Layer structure, dependency flow");
122
- lines.push(" - Key design decisions");
123
- }
124
- lines.push("");
125
- lines.push("2. **Domain Model** (category: `architecture`)");
126
- lines.push(" - Key entities/types and their relationships");
127
- lines.push(" - Data flow from entry points through processing");
128
- lines.push("");
129
- lines.push("3. **Conventions** (category: `conventions`)");
130
- lines.push(" - Naming patterns, file organization, testing approach");
131
- if (isCdk) lines.push(" - CDK construct patterns and stack organization");
132
- if (isNode) lines.push(" - Build tooling, package manager, module system");
133
- if (isMaven) lines.push(" - Maven module structure, dependency management");
134
- lines.push(
135
- "",
136
- "### Using KB Tools",
137
- "",
138
- "This project has a KB MCP server with tools for search, analysis, memory, and more.",
139
- "Add the KB tools to your agent configuration so every session benefits from them.",
140
- "",
141
- "**Add to `.github/copilot-instructions.md`** or `AGENTS.md`:",
142
- "",
143
- "```markdown",
144
- "## KB Tools",
145
- "",
146
- `Before starting any task on **${projectName}**, use these MCP tools:`,
147
- "",
148
- "| Action | Tool | Example |",
149
- "|--------|------|---------|",
150
- `| Search code & decisions | \`kb_search\` | \`kb_search({ query: "..." })\` |`,
151
- '| Find symbol definition | `kb_symbol` | `kb_symbol({ name: "ClassName" })` |',
152
- '| Trace call chains | `kb_trace` | `kb_trace({ symbol: "fn", file: "path" })` |',
153
- '| Impact of a change | `kb_blast_radius` | `kb_blast_radius({ changed_files: ["..."] })` |',
154
- '| Persist what you learn | `kb_remember` | `kb_remember({ title: "...", category: "decisions" })` |',
155
- "| Typecheck + lint | `kb_check` | `kb_check({})` |",
156
- "| Run tests | `kb_test_run` | `kb_test_run({})` |",
157
- "```",
158
- "",
159
- "**Add to each `.github/agents/*.agent.md`** (Skills Reference table):",
160
- "",
161
- "```markdown",
162
- "| Context | Skill | Details |",
163
- "|---------|-------|---------|",
164
- "| KB search, analysis, memory | `kb` | `path/to/skills/kb/SKILL.md` |",
165
- "```",
166
- "",
167
- "The KB skill teaches agents the full tool set:",
168
- "",
169
- "| Category | Tools | Purpose |",
170
- "|----------|-------|---------|",
171
- "| Search & Discovery | `kb_search`, `kb_find`, `kb_symbol`, `kb_trace` | Find code, symbols, data flow |",
172
- "| Code Analysis | `kb_analyze_*`, `kb_blast_radius` | Structure, deps, patterns, impact |",
173
- "| Knowledge | `kb_remember`, `kb_read`, `kb_update`, `kb_forget` | Persistent cross-session memory |",
174
- "| Execution | `kb_check`, `kb_test_run`, `kb_eval` | Typecheck, lint, test, run code |",
175
- "| Refactoring | `kb_rename`, `kb_codemod`, `kb_dead_symbols` | Safe renames, transforms, cleanup |",
176
- "| Web & API | `kb_web_fetch`, `kb_web_search`, `kb_http` | Research, API testing |",
177
- "| Context | `kb_workset`, `kb_stash`, `kb_checkpoint` | Manage working sets, save progress |",
178
- "",
179
- "**Workflow pattern \u2014 use on every task:**",
180
- "",
181
- "```",
182
- `kb_search({ query: "your task keywords" }) # Recall prior decisions`,
183
- `kb_scope_map({ task: "what you are doing" }) # Get a reading plan`,
184
- "# ... do the work ...",
185
- `kb_remember({ title: "What I learned", category: "decisions" }) # Persist`,
186
- "```"
187
- );
188
- return lines.join("\n");
189
- }
190
- const TEST_SEGMENTS = /* @__PURE__ */ new Set([
191
- "test",
192
- "tests",
193
- "__tests__",
194
- "spec",
195
- "specs",
196
- "__mocks__",
197
- "__fixtures__",
198
- "fixtures",
199
- "test-utils"
200
- ]);
201
- function isTestPath(filePath) {
202
- const segments = filePath.replace(/\\/g, "/").split("/");
203
- return segments.some((s) => TEST_SEGMENTS.has(s)) || /\.(test|spec)\.[jt]sx?$/.test(filePath) || /Test\.java$/.test(filePath);
204
- }
205
- function buildCodeMap(dataMap, projectName, callGraph) {
206
- const depsData = dataMap.get("dependencies");
207
- const symbolsData = dataMap.get("symbols");
208
- const entryData = dataMap.get("entry-points");
209
- const lines = [`## Code Map: ${projectName}
210
- `];
211
- if (!depsData && !symbolsData) {
212
- lines.push("No dependency or symbol data available.");
213
- return lines.join("\n");
214
- }
215
- const reverseGraph = depsData?.reverseGraph ?? {};
216
- const symbols = symbolsData?.symbols ?? [];
217
- const entryPoints = entryData?.entryPoints ?? [];
218
- const exportsByFile = /* @__PURE__ */ new Map();
219
- for (const sym of symbols) {
220
- if (!sym.exported) continue;
221
- const fp = sym.filePath.replace(/\\/g, "/");
222
- if (isTestPath(fp)) continue;
223
- const existing = exportsByFile.get(fp);
224
- if (existing) {
225
- existing.push({ name: sym.name, kind: sym.kind });
226
- } else {
227
- exportsByFile.set(fp, [{ name: sym.name, kind: sym.kind }]);
228
- }
229
- }
230
- const normReverse = /* @__PURE__ */ new Map();
231
- for (const [file, importers] of Object.entries(reverseGraph)) {
232
- const nf = file.replace(/\\/g, "/");
233
- const resolved = resolveExtensionlessPath(nf, exportsByFile);
234
- const srcImporters = importers.map((i) => i.replace(/\\/g, "/")).filter((i) => !isTestPath(i));
235
- if (srcImporters.length === 0) continue;
236
- const existing = normReverse.get(resolved);
237
- if (existing) {
238
- for (const imp of srcImporters) existing.add(imp);
239
- } else {
240
- normReverse.set(resolved, new Set(srcImporters));
241
- }
242
- }
243
- const entryLookup = /* @__PURE__ */ new Map();
244
- for (const ep of entryPoints) {
245
- entryLookup.set(ep.filePath.replace(/\\/g, "/"), { name: ep.name, trigger: ep.trigger });
246
- }
247
- const crossCalls = /* @__PURE__ */ new Map();
248
- const crossCalledBy = /* @__PURE__ */ new Map();
249
- if (callGraph) {
250
- for (const [from, targets] of callGraph) {
251
- if (isTestPath(from)) continue;
252
- const fromPkg = getPackageKey(from);
253
- for (const [to, syms] of targets) {
254
- if (isTestPath(to)) continue;
255
- const toPkg = getPackageKey(to);
256
- if (fromPkg === toPkg) continue;
257
- let outMap = crossCalls.get(from);
258
- if (!outMap) {
259
- outMap = /* @__PURE__ */ new Map();
260
- crossCalls.set(from, outMap);
261
- }
262
- outMap.set(to, syms);
263
- const callers = crossCalledBy.get(to);
264
- const entry = { file: from, symbols: syms };
265
- if (callers) {
266
- callers.push(entry);
267
- } else {
268
- crossCalledBy.set(to, [entry]);
269
- }
270
- }
271
- }
272
- }
273
- const allFiles = /* @__PURE__ */ new Set();
274
- for (const fp of entryLookup.keys()) allFiles.add(fp);
275
- for (const fp of crossCalls.keys()) allFiles.add(fp);
276
- for (const fp of crossCalledBy.keys()) allFiles.add(fp);
277
- if (!callGraph) {
278
- for (const fp of exportsByFile.keys()) {
279
- const importers = normReverse.get(fp);
280
- if (importers && importers.size >= 3) allFiles.add(fp);
281
- }
282
- }
283
- const groups = /* @__PURE__ */ new Map();
284
- for (const fp of allFiles) {
285
- const group = getPackageKey(fp);
286
- const arr = groups.get(group);
287
- if (arr) {
288
- arr.push(fp);
289
- } else {
290
- groups.set(group, [fp]);
291
- }
292
- }
293
- const sortedGroups = [...groups.entries()].sort((a, b) => a[0].localeCompare(b[0]));
294
- const graphType = callGraph ? "AST call graph" : "import analysis";
295
- const crossCount = callGraph ? `, ${crossCalls.size} cross-package callers` : "";
296
- lines.push(`**${allFiles.size} key modules** (${graphType}${crossCount})
297
- `);
298
- lines.push(
299
- "**Legend:** \u26A1 Entry point | \u{1F4E4} Exports | \u2192 Calls (outgoing) | \u2190 Called by (incoming) | \u27A1 Used by (import)\n"
300
- );
301
- for (const [group, files] of sortedGroups) {
302
- files.sort();
303
- lines.push(`### ${group}/
304
- `);
305
- for (const fp of files) {
306
- const exports = exportsByFile.get(fp);
307
- const entry = entryLookup.get(fp);
308
- const outCalls = crossCalls.get(fp);
309
- const inCalls = crossCalledBy.get(fp);
310
- const importedBySet = normReverse.get(fp);
311
- const shortPath = fp.startsWith(`${group}/`) ? fp.slice(group.length + 1) : fp;
312
- lines.push(`**${shortPath}**`);
313
- if (entry) {
314
- lines.push(` \u26A1 Entry: \`${entry.name}\`${entry.trigger ? ` (${entry.trigger})` : ""}`);
315
- }
316
- if (exports && exports.length > 0) {
317
- const compact = exports.slice(0, 8).map((e) => `${e.name}`).join(", ");
318
- const more = exports.length > 8 ? ` (+${exports.length - 8})` : "";
319
- lines.push(` \u{1F4E4} ${compact}${more}`);
320
- }
321
- if (outCalls && outCalls.size > 0) {
322
- const sorted = [...outCalls.entries()].sort((a, b) => b[1].length - a[1].length);
323
- for (const [target, syms] of sorted.slice(0, 4)) {
324
- const shortTarget = target.startsWith(`${group}/`) ? target.slice(group.length + 1) : target;
325
- lines.push(
326
- ` \u2192 ${shortTarget}: ${syms.slice(0, 5).join(", ")}${syms.length > 5 ? "\u2026" : ""}`
327
- );
328
- }
329
- if (sorted.length > 4) lines.push(` \u2192 +${sorted.length - 4} more targets`);
330
- }
331
- if (inCalls && inCalls.length > 0) {
332
- for (const c of inCalls.slice(0, 4)) {
333
- const shortCaller = c.file.startsWith(`${group}/`) ? c.file.slice(group.length + 1) : c.file;
334
- lines.push(
335
- ` \u2190 ${shortCaller}: ${c.symbols.slice(0, 4).join(", ")}${c.symbols.length > 4 ? "\u2026" : ""}`
336
- );
337
- }
338
- if (inCalls.length > 4) lines.push(` \u2190 +${inCalls.length - 4} more callers`);
339
- } else if (!callGraph && importedBySet && importedBySet.size > 0) {
340
- const importedBy = [...importedBySet].filter((i) => !isTestPath(i));
341
- if (importedBy.length <= 3) {
342
- lines.push(` \u27A1 Used by: ${importedBy.join(", ")}`);
343
- } else {
344
- lines.push(
345
- ` \u27A1 Used by: ${importedBy.slice(0, 3).join(", ")} (+${importedBy.length - 3} more)`
346
- );
347
- }
348
- }
349
- lines.push("");
350
- }
351
- }
352
- return lines.join("\n");
353
- }
354
- function getPackageKey(fp) {
355
- const parts = fp.split("/");
356
- if (parts.length >= 2 && ["packages", "services", "providers", "apps", "libs"].includes(parts[0])) {
357
- return `${parts[0]}/${parts[1]}`;
358
- }
359
- const javaIdx = parts.indexOf("java");
360
- const kotlinIdx = parts.indexOf("kotlin");
361
- const langIdx = javaIdx >= 0 ? javaIdx : kotlinIdx;
362
- if (langIdx >= 0 && langIdx + 2 < parts.length) {
363
- const afterLang = parts.slice(langIdx + 1);
364
- if (["com", "org", "net", "io", "dev"].includes(afterLang[0]) && afterLang.length >= 3) {
365
- return afterLang.slice(0, 3).join("/");
366
- }
367
- return afterLang.slice(0, 2).join("/");
368
- }
369
- if (parts[0] === "src" && parts.length >= 3) {
370
- return `${parts[0]}/${parts[1]}`;
371
- }
372
- return parts[0];
373
- }
374
- function buildDiagrams(callGraph, dataMap, projectName) {
375
- const symbolsData = dataMap.get("symbols");
376
- const entryData = dataMap.get("entry-points");
377
- const depsData = dataMap.get("dependencies");
378
- const pkgEdges = /* @__PURE__ */ new Map();
379
- for (const [from, targets] of callGraph) {
380
- if (isTestPath(from)) continue;
381
- for (const [to, syms] of targets) {
382
- if (isTestPath(to)) continue;
383
- const fromPkg = getPackageKey(from);
384
- const toPkg = getPackageKey(to);
385
- if (fromPkg === toPkg) continue;
386
- const key = `${fromPkg}|${toPkg}`;
387
- pkgEdges.set(key, (pkgEdges.get(key) ?? 0) + syms.length);
388
- }
389
- }
390
- if (pkgEdges.size === 0)
391
- return "## Architecture Diagram\n\nNo cross-package dependencies detected.";
392
- const pkgNodes = /* @__PURE__ */ new Set();
393
- for (const key of pkgEdges.keys()) {
394
- const [from, to] = key.split("|");
395
- pkgNodes.add(from);
396
- pkgNodes.add(to);
397
- }
398
- const exportsByPkg = /* @__PURE__ */ new Map();
399
- if (symbolsData?.symbols) {
400
- for (const sym of symbolsData.symbols) {
401
- if (!sym.exported) continue;
402
- const fp = sym.filePath.replace(/\\/g, "/");
403
- if (isTestPath(fp)) continue;
404
- const pkg = getPackageKey(fp);
405
- exportsByPkg.set(pkg, (exportsByPkg.get(pkg) ?? 0) + 1);
406
- }
407
- }
408
- const entryByPkg = /* @__PURE__ */ new Map();
409
- if (entryData?.entryPoints) {
410
- for (const ep of entryData.entryPoints) {
411
- const fp = ep.filePath.replace(/\\/g, "/");
412
- const pkg = getPackageKey(fp);
413
- const existing = entryByPkg.get(pkg);
414
- if (existing) {
415
- existing.count++;
416
- if (ep.trigger) existing.triggers.add(ep.trigger);
417
- } else {
418
- entryByPkg.set(pkg, { count: 1, triggers: new Set(ep.trigger ? [ep.trigger] : []) });
419
- }
420
- }
421
- }
422
- const extSystems = [];
423
- if (depsData?.external) {
424
- const external = depsData.external;
425
- const awsServices = {
426
- "client-dynamodb": { id: "dynamodb", name: "DynamoDB" },
427
- "lib-dynamodb": { id: "dynamodb", name: "DynamoDB" },
428
- "client-sqs": { id: "sqs", name: "SQS" },
429
- "client-ses": { id: "ses", name: "SES" },
430
- "client-sesv2": { id: "ses", name: "SES" },
431
- "client-s3": { id: "s3", name: "S3" },
432
- "client-eventbridge": { id: "eventbridge", name: "EventBridge" },
433
- "client-sns": { id: "sns", name: "SNS" },
434
- "client-secrets-manager": { id: "secrets", name: "Secrets Manager" },
435
- "client-scheduler": { id: "scheduler", name: "EventBridge Scheduler" },
436
- "client-apigatewaymanagementapi": { id: "apigw", name: "API Gateway" },
437
- "client-cloudwatch": { id: "cloudwatch", name: "CloudWatch" }
438
- };
439
- const seen = /* @__PURE__ */ new Set();
440
- for (const depName of Object.keys(external)) {
441
- for (const [suffix, info] of Object.entries(awsServices)) {
442
- if (depName.includes(suffix) && !seen.has(info.id)) {
443
- seen.add(info.id);
444
- extSystems.push(info);
445
- }
446
- }
447
- }
448
- extSystems.sort((a, b) => a.name.localeCompare(b.name));
449
- }
450
- const boundaries = /* @__PURE__ */ new Map();
451
- for (const pkg of [...pkgNodes].sort()) {
452
- const boundary = pkg.split("/")[0];
453
- const arr = boundaries.get(boundary);
454
- if (arr) arr.push(pkg);
455
- else boundaries.set(boundary, [pkg]);
456
- }
457
- const langByPkg = /* @__PURE__ */ new Map();
458
- if (symbolsData?.symbols) {
459
- const extCounts = /* @__PURE__ */ new Map();
460
- for (const sym of symbolsData.symbols) {
461
- const fp = sym.filePath.replace(/\\/g, "/");
462
- const pkg = getPackageKey(fp);
463
- const ext = fp.match(/\.[^./]+$/)?.[0] || "";
464
- if (!extCounts.has(pkg)) extCounts.set(pkg, /* @__PURE__ */ new Map());
465
- const counts = extCounts.get(pkg);
466
- counts.set(ext, (counts.get(ext) ?? 0) + 1);
467
- }
468
- const EXT_LANG = {
469
- ".ts": "TypeScript",
470
- ".tsx": "TypeScript",
471
- ".js": "JavaScript",
472
- ".jsx": "JavaScript",
473
- ".java": "Java",
474
- ".kt": "Kotlin",
475
- ".scala": "Scala",
476
- ".py": "Python",
477
- ".go": "Go",
478
- ".rs": "Rust",
479
- ".cs": "C#",
480
- ".rb": "Ruby",
481
- ".php": "PHP",
482
- ".swift": "Swift"
483
- };
484
- for (const [pkg, counts] of extCounts) {
485
- let maxExt = "";
486
- let maxCount = 0;
487
- for (const [ext, count] of counts) {
488
- if (count > maxCount) {
489
- maxCount = count;
490
- maxExt = ext;
491
- }
492
- }
493
- langByPkg.set(pkg, EXT_LANG[maxExt] || "TypeScript");
494
- }
495
- }
496
- const nodeId = (pkg) => pkg.replace(/[^a-zA-Z0-9]/g, "_");
497
- const lines = [];
498
- lines.push("```mermaid");
499
- lines.push("C4Container");
500
- lines.push(` title C4 Container: ${projectName}`);
501
- lines.push("");
502
- const descFor = (pkg) => {
503
- const parts = [];
504
- const entry = entryByPkg.get(pkg);
505
- if (entry) {
506
- parts.push(`${entry.count} handlers`);
507
- if (entry.triggers.size > 0) parts.push([...entry.triggers].join(", "));
508
- }
509
- const exports = exportsByPkg.get(pkg);
510
- if (exports) parts.push(`${exports} exports`);
511
- return parts.join(" \xB7 ") || "";
512
- };
513
- const techFor = (pkg) => {
514
- const lang = langByPkg.get(pkg) || "TypeScript";
515
- if (pkg.startsWith("infra")) return `CDK/${lang}`;
516
- if (entryByPkg.has(pkg)) {
517
- const entry = entryByPkg.get(pkg);
518
- const hasSqs = entry?.triggers.has("SQS") || entry?.triggers.has("SNS");
519
- if (hasSqs || entry?.triggers.has("API Gateway")) return `Lambda/${lang}`;
520
- if (entry?.triggers.has("HTTP Server") || entry?.triggers.has("HTTP Endpoint"))
521
- return `Spring Boot/${lang}`;
522
- }
523
- return lang;
524
- };
525
- for (const [boundary, pkgs] of [...boundaries.entries()].sort()) {
526
- const genericBoundaries = /* @__PURE__ */ new Set(["com", "org", "net", "io", "dev", "src"]);
527
- const boundaryLabel = genericBoundaries.has(boundary) ? projectName.charAt(0).toUpperCase() + projectName.slice(1) : boundary.charAt(0).toUpperCase() + boundary.slice(1);
528
- if (pkgs.length === 1 && pkgs[0] === boundary) {
529
- const pkg = pkgs[0];
530
- lines.push(` Container(${nodeId(pkg)}, "${pkg}", "${techFor(pkg)}", "${descFor(pkg)}")`);
531
- } else {
532
- lines.push(` System_Boundary(${nodeId(boundary)}_boundary, "${boundaryLabel}") {`);
533
- for (const pkg of pkgs) {
534
- const shortName = pkg.split("/").slice(1).join("/") || pkg;
535
- lines.push(
536
- ` Container(${nodeId(pkg)}, "${shortName}", "${techFor(pkg)}", "${descFor(pkg)}")`
537
- );
538
- }
539
- lines.push(" }");
540
- }
541
- lines.push("");
542
- }
543
- if (extSystems.length > 0) {
544
- for (const ext of extSystems) {
545
- lines.push(` System_Ext(ext_${ext.id}, "${ext.name}", "AWS")`);
546
- }
547
- lines.push("");
548
- }
549
- const sortedEdges = [...pkgEdges.entries()].sort((a, b) => b[1] - a[1]);
550
- for (const [key, count] of sortedEdges.slice(0, 30)) {
551
- const [from, to] = key.split("|");
552
- lines.push(` Rel(${nodeId(from)}, ${nodeId(to)}, "Uses", "${count} calls")`);
553
- }
554
- lines.push("```");
555
- const c4Diagram = `## C4 Container Diagram
1
+ import{existsSync as W,mkdirSync as V,readdirSync as q,rmSync as U,writeFileSync as I}from"node:fs";import{readdir as J,readFile as Z}from"node:fs/promises";import{basename as L,join as K,relative as H,resolve as Q}from"node:path";import{DependencyAnalyzer as X,DiagramGenerator as Y,EntryPointAnalyzer as ee,extractRegexCallGraph as te,extractTsCallGraph as se,PatternAnalyzer as ne,StructureAnalyzer as oe,SymbolAnalyzer as re}from"../../analyzers/dist/index.js";const N={structure:"Project Structure",dependencies:"Dependencies","entry-points":"Entry Points",symbols:"Symbols",patterns:"Patterns",diagram:"C4 Container Diagram","code-map":"Code Map (Module Graph)","config-values":"Configuration Values","synthesis-guide":"Synthesis Guide"};function ie(g,u,t,p){const n=[`Analysis baselines for **${t}** have been generated.`];u==="generate"?n.push("Individual results are in the sibling `.md` and `.json` files in this directory.",""):n.push("Results are stored in the KB vector store.","");const a=p.get("symbols"),i=p.get("dependencies"),d=p.get("patterns"),y=p.get("entry-points"),b=a?.totalCount??0,k=a?.exportedCount??0,x=i?.totalImports??0,C=y?.total??0,r=(d?.patterns??[]).map(m=>m.pattern),w=r.some(m=>m.startsWith("Spring")),D=r.includes("AWS CDK")||r.includes("CDK IaC"),P=r.includes("Maven"),T=r.includes("Serverless")||C>3,$=i?.external?Object.keys(i.external):[],f=$.some(m=>["express","fastify","next","react","vitest","jest"].includes(m))||x>0,_=$.some(m=>["turbo","lerna","nx"].includes(m))||r.includes("Monorepo");if(n.push("### Project Profile",""),n.push(`- **${b} symbols** (${k} exported), **${x} imports**, **${C} entry ${C===1?"point":"points"}**`),r.length>0&&n.push(`- **Detected**: ${r.slice(0,8).join(", ")}`),n.push(""),b===0&&x===0&&C===0)return n.push("> **Note:** This project appears to be empty or contains no analyzable source code.","> Run onboard again after adding source files."),n.join(`
2
+ `);const v=g.filter(m=>m.status==="success"),M=g.filter(m=>m.status==="failed");n.push("### Completed Analyses","");for(const m of v){const S=N[m.name]??m.name,e=m.output.length>1e3?`${Math.round(m.output.length/1024)}KB`:`${m.output.length}B`;u==="generate"?n.push(`- \u2713 [${S}](./${m.name}.md) (${e})`):n.push(`- \u2713 ${S} (${e})`)}if(M.length>0){n.push("","### Failed Analyses","");for(const m of M)n.push(`- \u2717 ${m.name}: ${m.error}`)}n.push("","### Recommended Reading Order","","1. **Start with** `synthesis-guide.md` (this file) \u2192 `entry-points.md` \u2192 `patterns.md`","2. **Module graph** via `code-map.md` \u2014 cross-package call edges with function names","3. **Architecture** via `diagram.md` (C4 Container) \u2192 `dependencies.md`","4. **Browse structure** via `structure.md` for file layout","5. **API surface** via `symbols.md` \u2014 file paths + exported symbols (capped at 80KB)","6. **Reference**: `config-values.md` (config reference)","","> **Size guidance:** Total output is ~");const j=v.reduce((m,S)=>m+S.output.length,0)/1024;return n[n.length-1]+=`${Math.round(j)}KB. Focus on code-map.md + entry-points.md + diagram.md (~${Math.round((v.find(m=>m.name==="code-map")?.output.length??0)/1024+(v.find(m=>m.name==="entry-points")?.output.length??0)/1024+(v.find(m=>m.name==="diagram")?.output.length??0)/1024)}KB) for maximum signal-to-token ratio.`,n.push("","### Synthesize Knowledge","","Produce the following `kb_remember` entries:",""),n.push("1. **Architecture Summary** (category: `architecture`)"),_?(n.push(" - Package boundaries, dependency graph between packages"),n.push(" - Shared vs service-specific code")):T?(n.push(" - Lambda functions, triggers, event flow"),n.push(" - Infrastructure patterns (queues, tables, APIs)")):w?(n.push(" - Controller \u2192 Service \u2192 Repository layers"),n.push(" - Spring configuration and profiles")):(n.push(" - Layer structure, dependency flow"),n.push(" - Key design decisions")),n.push(""),n.push("2. **Domain Model** (category: `architecture`)"),n.push(" - Key entities/types and their relationships"),n.push(" - Data flow from entry points through processing"),n.push(""),n.push("3. **Conventions** (category: `conventions`)"),n.push(" - Naming patterns, file organization, testing approach"),D&&n.push(" - CDK construct patterns and stack organization"),f&&n.push(" - Build tooling, package manager, module system"),P&&n.push(" - Maven module structure, dependency management"),n.push("","### Using KB Tools","","This project has a KB MCP server with tools for search, analysis, memory, and more.","Add the KB tools to your agent configuration so every session benefits from them.","","**Add to `.github/copilot-instructions.md`** or `AGENTS.md`:","","```markdown","## KB Tools","",`Before starting any task on **${t}**, use these MCP tools:`,"","| Action | Tool | Example |","|--------|------|---------|",'| Search code & decisions | `kb_search` | `kb_search({ query: "..." })` |','| Find symbol definition | `kb_symbol` | `kb_symbol({ name: "ClassName" })` |','| Trace call chains | `kb_trace` | `kb_trace({ symbol: "fn", file: "path" })` |','| Impact of a change | `kb_blast_radius` | `kb_blast_radius({ changed_files: ["..."] })` |','| Persist what you learn | `kb_remember` | `kb_remember({ title: "...", category: "decisions" })` |',"| Typecheck + lint | `kb_check` | `kb_check({})` |","| Run tests | `kb_test_run` | `kb_test_run({})` |","```","","**Add to each `.github/agents/*.agent.md`** (Skills Reference table):","","```markdown","| Context | Skill | Details |","|---------|-------|---------|","| KB search, analysis, memory | `kb` | `path/to/skills/kb/SKILL.md` |","```","","The KB skill teaches agents the full tool set:","","| Category | Tools | Purpose |","|----------|-------|---------|","| Search & Discovery | `kb_search`, `kb_find`, `kb_symbol`, `kb_trace` | Find code, symbols, data flow |","| Code Analysis | `kb_analyze_*`, `kb_blast_radius` | Structure, deps, patterns, impact |","| Knowledge | `kb_remember`, `kb_read`, `kb_update`, `kb_forget` | Persistent cross-session memory |","| Execution | `kb_check`, `kb_test_run`, `kb_eval` | Typecheck, lint, test, run code |","| Refactoring | `kb_rename`, `kb_codemod`, `kb_dead_symbols` | Safe renames, transforms, cleanup |","| Web & API | `kb_web_fetch`, `kb_web_search`, `kb_http` | Research, API testing |","| Context | `kb_workset`, `kb_stash`, `kb_checkpoint` | Manage working sets, save progress |","","**Workflow pattern \u2014 use on every task:**","","```",'kb_search({ query: "your task keywords" }) # Recall prior decisions','kb_scope_map({ task: "what you are doing" }) # Get a reading plan',"# ... do the work ...",'kb_remember({ title: "What I learned", category: "decisions" }) # Persist',"```"),n.join(`
3
+ `)}const ae=new Set(["test","tests","__tests__","spec","specs","__mocks__","__fixtures__","fixtures","test-utils"]);function R(g){return g.replace(/\\/g,"/").split("/").some(t=>ae.has(t))||/\.(test|spec)\.[jt]sx?$/.test(g)||/Test\.java$/.test(g)}function ce(g,u,t){const p=g.get("dependencies"),n=g.get("symbols"),a=g.get("entry-points"),i=[`## Code Map: ${u}
4
+ `];if(!p&&!n)return i.push("No dependency or symbol data available."),i.join(`
5
+ `);const d=p?.reverseGraph??{},y=n?.symbols??[],b=a?.entryPoints??[],k=new Map;for(const f of y){if(!f.exported)continue;const _=f.filePath.replace(/\\/g,"/");if(R(_))continue;const v=k.get(_);v?v.push({name:f.name,kind:f.kind}):k.set(_,[{name:f.name,kind:f.kind}])}const x=new Map;for(const[f,_]of Object.entries(d)){const v=f.replace(/\\/g,"/"),M=ue(v,k),j=_.map(S=>S.replace(/\\/g,"/")).filter(S=>!R(S));if(j.length===0)continue;const m=x.get(M);if(m)for(const S of j)m.add(S);else x.set(M,new Set(j))}const C=new Map;for(const f of b)C.set(f.filePath.replace(/\\/g,"/"),{name:f.name,trigger:f.trigger});const l=new Map,r=new Map;if(t)for(const[f,_]of t){if(R(f))continue;const v=z(f);for(const[M,j]of _){if(R(M))continue;const m=z(M);if(v===m)continue;let S=l.get(f);S||(S=new Map,l.set(f,S)),S.set(M,j);const e=r.get(M),o={file:f,symbols:j};e?e.push(o):r.set(M,[o])}}const w=new Set;for(const f of C.keys())w.add(f);for(const f of l.keys())w.add(f);for(const f of r.keys())w.add(f);if(!t)for(const f of k.keys()){const _=x.get(f);_&&_.size>=3&&w.add(f)}const D=new Map;for(const f of w){const _=z(f),v=D.get(_);v?v.push(f):D.set(_,[f])}const P=[...D.entries()].sort((f,_)=>f[0].localeCompare(_[0])),T=t?"AST call graph":"import analysis",$=t?`, ${l.size} cross-package callers`:"";i.push(`**${w.size} key modules** (${T}${$})
6
+ `),i.push(`**Legend:** \u26A1 Entry point | \u{1F4E4} Exports | \u2192 Calls (outgoing) | \u2190 Called by (incoming) | \u27A1 Used by (import)
7
+ `);for(const[f,_]of P){_.sort(),i.push(`### ${f}/
8
+ `);for(const v of _){const M=k.get(v),j=C.get(v),m=l.get(v),S=r.get(v),e=x.get(v),o=v.startsWith(`${f}/`)?v.slice(f.length+1):v;if(i.push(`**${o}**`),j&&i.push(` \u26A1 Entry: \`${j.name}\`${j.trigger?` (${j.trigger})`:""}`),M&&M.length>0){const s=M.slice(0,8).map(h=>`${h.name}`).join(", "),c=M.length>8?` (+${M.length-8})`:"";i.push(` \u{1F4E4} ${s}${c}`)}if(m&&m.size>0){const s=[...m.entries()].sort((c,h)=>h[1].length-c[1].length);for(const[c,h]of s.slice(0,4)){const A=c.startsWith(`${f}/`)?c.slice(f.length+1):c;i.push(` \u2192 ${A}: ${h.slice(0,5).join(", ")}${h.length>5?"\u2026":""}`)}s.length>4&&i.push(` \u2192 +${s.length-4} more targets`)}if(S&&S.length>0){for(const s of S.slice(0,4)){const c=s.file.startsWith(`${f}/`)?s.file.slice(f.length+1):s.file;i.push(` \u2190 ${c}: ${s.symbols.slice(0,4).join(", ")}${s.symbols.length>4?"\u2026":""}`)}S.length>4&&i.push(` \u2190 +${S.length-4} more callers`)}else if(!t&&e&&e.size>0){const s=[...e].filter(c=>!R(c));s.length<=3?i.push(` \u27A1 Used by: ${s.join(", ")}`):i.push(` \u27A1 Used by: ${s.slice(0,3).join(", ")} (+${s.length-3} more)`)}i.push("")}}return i.join(`
9
+ `)}function z(g){const u=g.split("/");if(u.length>=2&&["packages","services","providers","apps","libs"].includes(u[0]))return`${u[0]}/${u[1]}`;const t=u.indexOf("java"),p=u.indexOf("kotlin"),n=t>=0?t:p;if(n>=0&&n+2<u.length){const a=u.slice(n+1);return["com","org","net","io","dev"].includes(a[0])&&a.length>=3?a.slice(0,3).join("/"):a.slice(0,2).join("/")}return u[0]==="src"&&u.length>=3?`${u[0]}/${u[1]}`:u[0]}function le(g,u,t){const p=u.get("symbols"),n=u.get("entry-points"),a=u.get("dependencies"),i=new Map;for(const[e,o]of g)if(!R(e))for(const[s,c]of o){if(R(s))continue;const h=z(e),A=z(s);if(h===A)continue;const E=`${h}|${A}`;i.set(E,(i.get(E)??0)+c.length)}if(i.size===0)return`## Architecture Diagram
556
10
 
557
- ${lines.join("\n")}`;
558
- const flowLines = [];
559
- flowLines.push("```mermaid");
560
- flowLines.push("graph TB");
561
- const allTriggers = /* @__PURE__ */ new Set();
562
- for (const [, info] of entryByPkg) {
563
- for (const t of info.triggers) allTriggers.add(t);
564
- }
565
- if (allTriggers.size > 0) {
566
- flowLines.push(' subgraph Triggers["External Triggers"]');
567
- for (const trigger of [...allTriggers].sort()) {
568
- const tId = `trigger_${trigger.replace(/[^a-zA-Z0-9]/g, "_")}`;
569
- flowLines.push(` ${tId}(("${trigger}"))`);
570
- }
571
- flowLines.push(" end");
572
- flowLines.push("");
573
- }
574
- const servicePkgs = [...pkgNodes].filter((p) => entryByPkg.has(p)).sort();
575
- const libraryPkgs = [...pkgNodes].filter((p) => !entryByPkg.has(p)).sort();
576
- if (servicePkgs.length > 0) {
577
- flowLines.push(' subgraph Services["Service Layer"]');
578
- for (const pkg of servicePkgs) {
579
- const id = `flow_${nodeId(pkg)}`;
580
- const shortName = pkg.includes("/") ? pkg.split("/").pop() ?? pkg : pkg;
581
- const entry = entryByPkg.get(pkg);
582
- flowLines.push(` ${id}["${shortName} (${entry?.count ?? 0} handlers)"]`);
583
- }
584
- flowLines.push(" end");
585
- flowLines.push("");
586
- }
587
- if (libraryPkgs.length > 0) {
588
- flowLines.push(' subgraph Libraries["Shared Libraries"]');
589
- for (const pkg of libraryPkgs) {
590
- const id = `flow_${nodeId(pkg)}`;
591
- const shortName = pkg.includes("/") ? pkg.split("/").pop() ?? pkg : pkg;
592
- flowLines.push(` ${id}["${shortName}"]`);
593
- }
594
- flowLines.push(" end");
595
- flowLines.push("");
596
- }
597
- if (extSystems.length > 0) {
598
- flowLines.push(' subgraph External["AWS Services"]');
599
- for (const ext of extSystems) {
600
- flowLines.push(` flow_ext_${ext.id}[("${ext.name}")]`);
601
- }
602
- flowLines.push(" end");
603
- flowLines.push("");
604
- }
605
- for (const pkg of servicePkgs) {
606
- const entry = entryByPkg.get(pkg);
607
- if (!entry) continue;
608
- const svcId = `flow_${nodeId(pkg)}`;
609
- for (const trigger of entry.triggers) {
610
- const tId = `trigger_${trigger.replace(/[^a-zA-Z0-9]/g, "_")}`;
611
- flowLines.push(` ${tId} --> ${svcId}`);
612
- }
613
- }
614
- const svcToLib = sortedEdges.filter(([key]) => {
615
- const [from, to] = key.split("|");
616
- return entryByPkg.has(from) && !entryByPkg.has(to);
617
- });
618
- for (const [key, count] of svcToLib.slice(0, 15)) {
619
- const [from, to] = key.split("|");
620
- flowLines.push(` flow_${nodeId(from)} -->|${count}| flow_${nodeId(to)}`);
621
- }
622
- const libToLib = sortedEdges.filter(([key]) => {
623
- const [from, to] = key.split("|");
624
- return !entryByPkg.has(from) && !entryByPkg.has(to);
625
- });
626
- for (const [key, count] of libToLib.slice(0, 10)) {
627
- const [from, to] = key.split("|");
628
- flowLines.push(` flow_${nodeId(from)} -->|${count}| flow_${nodeId(to)}`);
629
- }
630
- flowLines.push("```");
631
- const flowDiagram = `## Architectural Flow
11
+ No cross-package dependencies detected.`;const d=new Set;for(const e of i.keys()){const[o,s]=e.split("|");d.add(o),d.add(s)}const y=new Map;if(p?.symbols)for(const e of p.symbols){if(!e.exported)continue;const o=e.filePath.replace(/\\/g,"/");if(R(o))continue;const s=z(o);y.set(s,(y.get(s)??0)+1)}const b=new Map;if(n?.entryPoints)for(const e of n.entryPoints){const o=e.filePath.replace(/\\/g,"/"),s=z(o),c=b.get(s);c?(c.count++,e.trigger&&c.triggers.add(e.trigger)):b.set(s,{count:1,triggers:new Set(e.trigger?[e.trigger]:[])})}const k=[];if(a?.external){const e=a.external,o={"client-dynamodb":{id:"dynamodb",name:"DynamoDB"},"lib-dynamodb":{id:"dynamodb",name:"DynamoDB"},"client-sqs":{id:"sqs",name:"SQS"},"client-ses":{id:"ses",name:"SES"},"client-sesv2":{id:"ses",name:"SES"},"client-s3":{id:"s3",name:"S3"},"client-eventbridge":{id:"eventbridge",name:"EventBridge"},"client-sns":{id:"sns",name:"SNS"},"client-secrets-manager":{id:"secrets",name:"Secrets Manager"},"client-scheduler":{id:"scheduler",name:"EventBridge Scheduler"},"client-apigatewaymanagementapi":{id:"apigw",name:"API Gateway"},"client-cloudwatch":{id:"cloudwatch",name:"CloudWatch"}},s=new Set;for(const c of Object.keys(e))for(const[h,A]of Object.entries(o))c.includes(h)&&!s.has(A.id)&&(s.add(A.id),k.push(A));k.sort((c,h)=>c.name.localeCompare(h.name))}const x=new Map;for(const e of[...d].sort()){const o=e.split("/")[0],s=x.get(o);s?s.push(e):x.set(o,[e])}const C=new Map;if(p?.symbols){const e=new Map;for(const s of p.symbols){const c=s.filePath.replace(/\\/g,"/"),h=z(c),A=c.match(/\.[^./]+$/)?.[0]||"";e.has(h)||e.set(h,new Map);const E=e.get(h);E.set(A,(E.get(A)??0)+1)}const o={".ts":"TypeScript",".tsx":"TypeScript",".js":"JavaScript",".jsx":"JavaScript",".java":"Java",".kt":"Kotlin",".scala":"Scala",".py":"Python",".go":"Go",".rs":"Rust",".cs":"C#",".rb":"Ruby",".php":"PHP",".swift":"Swift"};for(const[s,c]of e){let h="",A=0;for(const[E,O]of c)O>A&&(A=O,h=E);C.set(s,o[h]||"TypeScript")}}const l=e=>e.replace(/[^a-zA-Z0-9]/g,"_"),r=[];r.push("```mermaid"),r.push("C4Container"),r.push(` title C4 Container: ${t}`),r.push("");const w=e=>{const o=[],s=b.get(e);s&&(o.push(`${s.count} handlers`),s.triggers.size>0&&o.push([...s.triggers].join(", ")));const c=y.get(e);return c&&o.push(`${c} exports`),o.join(" \xB7 ")||""},D=e=>{const o=C.get(e)||"TypeScript";if(e.startsWith("infra"))return`CDK/${o}`;if(b.has(e)){const s=b.get(e);if(s?.triggers.has("SQS")||s?.triggers.has("SNS")||s?.triggers.has("API Gateway"))return`Lambda/${o}`;if(s?.triggers.has("HTTP Server")||s?.triggers.has("HTTP Endpoint"))return`Spring Boot/${o}`}return o};for(const[e,o]of[...x.entries()].sort()){const c=new Set(["com","org","net","io","dev","src"]).has(e)?t.charAt(0).toUpperCase()+t.slice(1):e.charAt(0).toUpperCase()+e.slice(1);if(o.length===1&&o[0]===e){const h=o[0];r.push(` Container(${l(h)}, "${h}", "${D(h)}", "${w(h)}")`)}else{r.push(` System_Boundary(${l(e)}_boundary, "${c}") {`);for(const h of o){const A=h.split("/").slice(1).join("/")||h;r.push(` Container(${l(h)}, "${A}", "${D(h)}", "${w(h)}")`)}r.push(" }")}r.push("")}if(k.length>0){for(const e of k)r.push(` System_Ext(ext_${e.id}, "${e.name}", "AWS")`);r.push("")}const P=[...i.entries()].sort((e,o)=>o[1]-e[1]);for(const[e,o]of P.slice(0,30)){const[s,c]=e.split("|");r.push(` Rel(${l(s)}, ${l(c)}, "Uses", "${o} calls")`)}r.push("```");const T=`## C4 Container Diagram
632
12
 
633
- ${flowLines.join("\n")}`;
634
- const sections = [`# Architecture Diagrams: ${projectName}
635
- `];
636
- sections.push(c4Diagram);
637
- sections.push(flowDiagram);
638
- return sections.join("\n\n---\n\n");
639
- }
640
- function resolveExtensionlessPath(nf, exportsByFile) {
641
- if (exportsByFile.has(nf)) return nf;
642
- for (const ext of [".ts", ".tsx", ".js", ".jsx"]) {
643
- if (exportsByFile.has(`${nf}${ext}`)) return `${nf}${ext}`;
644
- }
645
- if (exportsByFile.has(`${nf}/index.ts`)) return `${nf}/index.ts`;
646
- return nf;
647
- }
648
- const CONFIG_EXCLUDES = /* @__PURE__ */ new Set([
649
- "node_modules",
650
- ".git",
651
- "dist",
652
- "build",
653
- "coverage",
654
- ".turbo",
655
- ".cache",
656
- "cdk.out",
657
- "__pycache__",
658
- ".venv",
659
- "target",
660
- "obj",
661
- ".gradle"
662
- ]);
663
- const CONFIG_FILE_PATTERNS = [
664
- { glob: /\.env(?:\.\w+)?$/, type: "env" },
665
- { glob: /\.env\.example$/, type: "env" },
666
- { glob: /package\.json$/, type: "package-json" },
667
- { glob: /^(?:app|config|settings|default)\.(?:json|ya?ml|toml)$/i, type: "config" },
668
- { glob: /docker-compose\.ya?ml$/, type: "docker" },
669
- { glob: /cdk\.json$/, type: "cdk" },
670
- { glob: /turbo\.json$/, type: "tooling" },
671
- { glob: /application\.(?:properties|ya?ml)$/i, type: "spring" },
672
- { glob: /settings\.py$/, type: "django" },
673
- { glob: /\.flaskenv$/, type: "env" },
674
- { glob: /appsettings\.(?:\w+\.)?json$/i, type: "dotnet" }
675
- ];
676
- async function extractConfigValues(rootPath, projectName) {
677
- const configs = [];
678
- const configFiles = await collectConfigFiles(rootPath);
679
- const KB_SKIP = /kb\.config\.json$/;
680
- for (const filePath of configFiles) {
681
- try {
682
- const relPath = relative(rootPath, filePath).replace(/\\/g, "/");
683
- if (KB_SKIP.test(relPath)) continue;
684
- const content = await readFile(filePath, "utf-8");
685
- const fileType = identifyConfigType(filePath);
686
- const depth = relPath.split("/").length - 1;
687
- if (depth > 1 && fileType === "tooling") continue;
688
- const values = parseConfigValues(content, fileType);
689
- if (values.length > 0) {
690
- configs.push({ file: relPath, type: fileType, values });
691
- }
692
- } catch {
693
- }
694
- }
695
- return formatConfigMarkdown(configs, projectName);
696
- }
697
- async function collectConfigFiles(rootPath) {
698
- const files = [];
699
- const walk = async (dir, depth) => {
700
- if (depth > 3) return;
701
- try {
702
- const entries = await readdir(dir, { withFileTypes: true });
703
- for (const entry of entries) {
704
- if (CONFIG_EXCLUDES.has(entry.name)) continue;
705
- const fullPath = join(dir, entry.name);
706
- if (entry.isDirectory() && !entry.name.startsWith(".")) {
707
- await walk(fullPath, depth + 1);
708
- } else if (entry.isFile()) {
709
- if (CONFIG_FILE_PATTERNS.some((p) => p.glob.test(entry.name))) {
710
- files.push(fullPath);
711
- }
712
- }
713
- }
714
- } catch {
715
- }
716
- };
717
- await walk(rootPath, 0);
718
- return files;
719
- }
720
- function identifyConfigType(filePath) {
721
- const name = basename(filePath);
722
- for (const p of CONFIG_FILE_PATTERNS) {
723
- if (p.glob.test(name)) return p.type;
724
- }
725
- return "unknown";
726
- }
727
- const SENSITIVE_KEYS = /(?:secret|password|token|key|api.?key|auth|credential|private)/i;
728
- function parseConfigValues(content, type) {
729
- const values = [];
730
- if (type === "env") {
731
- for (const line of content.split("\n")) {
732
- const trimmed = line.trim();
733
- if (!trimmed || trimmed.startsWith("#")) continue;
734
- const eqIdx = trimmed.indexOf("=");
735
- if (eqIdx === -1) continue;
736
- const key = trimmed.slice(0, eqIdx).trim();
737
- const rawValue = trimmed.slice(eqIdx + 1).trim();
738
- const sensitive = SENSITIVE_KEYS.test(key);
739
- values.push({ key, value: sensitive ? "***" : rawValue, sensitive });
740
- }
741
- } else if (type === "package-json") {
742
- try {
743
- const pkg = JSON.parse(content);
744
- if (pkg.scripts) {
745
- for (const [key, value] of Object.entries(pkg.scripts)) {
746
- values.push({ key: `scripts.${key}`, value: String(value), sensitive: false });
747
- }
748
- }
749
- if (pkg.engines) {
750
- for (const [key, value] of Object.entries(pkg.engines)) {
751
- values.push({ key: `engines.${key}`, value: String(value), sensitive: false });
752
- }
753
- }
754
- } catch {
755
- }
756
- } else if (type === "spring") {
757
- for (const line of content.split("\n")) {
758
- const trimmed = line.trim();
759
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("---")) continue;
760
- const match = trimmed.match(/^([\w.[\]-]+)\s*[=:]\s*(.*)$/);
761
- if (match) {
762
- const key = match[1];
763
- const rawValue = match[2].trim();
764
- const sensitive = SENSITIVE_KEYS.test(key);
765
- values.push({ key, value: sensitive ? "***" : rawValue, sensitive });
766
- }
767
- }
768
- } else if (type === "json" || type === "config" || type === "cdk" || type === "tooling" || type === "dotnet") {
769
- try {
770
- const obj = JSON.parse(content);
771
- flattenJson(obj, "", values, 0);
772
- } catch {
773
- }
774
- } else if (type === "django") {
775
- for (const line of content.split("\n")) {
776
- const match = line.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.+)$/);
777
- if (match) {
778
- const key = match[1];
779
- const rawValue = match[2].trim();
780
- const sensitive = SENSITIVE_KEYS.test(key);
781
- values.push({
782
- key,
783
- value: sensitive ? "***" : rawValue.slice(0, 100),
784
- sensitive
785
- });
786
- }
787
- }
788
- }
789
- return values;
790
- }
791
- function flattenJson(obj, prefix, values, depth) {
792
- if (depth > 3) return;
793
- if (obj === null || obj === void 0) return;
794
- if (typeof obj === "object" && !Array.isArray(obj)) {
795
- for (const [key, val] of Object.entries(obj)) {
796
- const fullKey = prefix ? `${prefix}.${key}` : key;
797
- if (typeof val === "object" && val !== null && !Array.isArray(val)) {
798
- flattenJson(val, fullKey, values, depth + 1);
799
- } else {
800
- const sensitive = SENSITIVE_KEYS.test(key);
801
- const strVal = Array.isArray(val) ? `[${val.length} items]` : String(val);
802
- values.push({ key: fullKey, value: sensitive ? "***" : strVal.slice(0, 120), sensitive });
803
- }
804
- }
805
- }
806
- }
807
- function formatConfigMarkdown(configs, projectName) {
808
- const lines = [];
809
- lines.push(`## Configuration Values: ${projectName}
810
- `);
811
- if (configs.length === 0) {
812
- lines.push("No configuration files detected.");
813
- return lines.join("\n");
814
- }
815
- lines.push(`**${configs.length} config files** found
816
- `);
817
- const byType = /* @__PURE__ */ new Map();
818
- for (const c of configs) {
819
- if (!byType.has(c.type)) byType.set(c.type, []);
820
- byType.get(c.type)?.push(c);
821
- }
822
- for (const [type, entries] of byType) {
823
- if (type === "package-json" && entries.length > 2) {
824
- lines.push(`### ${type}
825
- `);
826
- const root = entries.find((e) => e.file === "package.json");
827
- if (root) {
828
- lines.push(`#### ${root.file}
829
- `);
830
- lines.push("| Key | Value | Sensitive |");
831
- lines.push("|-----|-------|-----------|");
832
- for (const v of root.values.slice(0, 50)) {
833
- const escaped = v.value.replace(/\|/g, "\\|");
834
- lines.push(`| ${v.key} | ${escaped} | ${v.sensitive ? "\u26A0\uFE0F yes" : "no"} |`);
835
- }
836
- lines.push("");
837
- }
838
- const subEntries = entries.filter((e) => e.file !== "package.json");
839
- if (subEntries.length > 0) {
840
- const scriptFreq = /* @__PURE__ */ new Map();
841
- for (const entry of subEntries) {
842
- for (const v of entry.values) {
843
- const k = `${v.key}=${v.value}`;
844
- scriptFreq.set(k, (scriptFreq.get(k) ?? 0) + 1);
845
- }
846
- }
847
- const commonThreshold = Math.max(2, Math.floor(subEntries.length * 0.5));
848
- lines.push(`#### Sub-packages (${subEntries.length} packages)
849
- `);
850
- const commonScripts = [...scriptFreq.entries()].filter(([, count]) => count >= commonThreshold).map(([kv]) => {
851
- const [key, ...rest] = kv.split("=");
852
- return { key, value: rest.join("=") };
853
- });
854
- if (commonScripts.length > 0) {
855
- lines.push("**Common scripts** (shared by most sub-packages):\n");
856
- lines.push("| Key | Value |");
857
- lines.push("|-----|-------|");
858
- for (const s of commonScripts) {
859
- lines.push(`| ${s.key} | ${s.value.replace(/\|/g, "\\|")} |`);
860
- }
861
- lines.push("");
862
- }
863
- const uniqueByPkg = /* @__PURE__ */ new Map();
864
- for (const entry of subEntries) {
865
- const unique = entry.values.filter((v) => {
866
- const k = `${v.key}=${v.value}`;
867
- return (scriptFreq.get(k) ?? 0) < commonThreshold;
868
- });
869
- if (unique.length === 0) continue;
870
- const fingerprint = unique.map((v) => `${v.key}=${v.value}`).sort().join("||");
871
- const existing = uniqueByPkg.get(fingerprint);
872
- if (existing) {
873
- existing.files.push(entry.file);
874
- } else {
875
- uniqueByPkg.set(fingerprint, {
876
- files: [entry.file],
877
- entries: unique.map((v) => ({ key: v.key, value: v.value }))
878
- });
879
- }
880
- }
881
- for (const [, group] of uniqueByPkg) {
882
- if (group.files.length > 1) {
883
- lines.push(
884
- `**${group.files.length} packages** (${group.files.map((f) => f.split("/").slice(-2, -1)[0] || f).join(", ")}):`
885
- );
886
- } else {
887
- lines.push(`**${group.files[0]}**:`);
888
- }
889
- lines.push("| Key | Value |");
890
- lines.push("|-----|-------|");
891
- for (const v of group.entries) {
892
- lines.push(`| ${v.key} | ${v.value.replace(/\|/g, "\\|")} |`);
893
- }
894
- lines.push("");
895
- }
896
- }
897
- continue;
898
- }
899
- if (entries.length > 3) {
900
- const valueSets = entries.map(
901
- (e) => e.values.map((v) => `${v.key}=${v.value}`).sort().join("||")
902
- );
903
- const mostCommon = valueSets.sort(
904
- (a, b) => valueSets.filter((v) => v === b).length - valueSets.filter((v) => v === a).length
905
- )[0];
906
- const identicalCount = valueSets.filter((v) => v === mostCommon).length;
907
- if (identicalCount > 2) {
908
- lines.push(`### ${type}
909
- `);
910
- const representative = entries[valueSets.indexOf(mostCommon)];
911
- const identicalFiles = entries.filter((_, i) => valueSets[i] === mostCommon).map((e) => e.file);
912
- const differentEntries = entries.filter((_, i) => valueSets[i] !== mostCommon);
913
- lines.push(`**${identicalFiles.length} identical files**: ${identicalFiles.join(", ")}
914
- `);
915
- lines.push("| Key | Value | Sensitive |");
916
- lines.push("|-----|-------|-----------|");
917
- for (const v of representative.values.slice(0, 30)) {
918
- const escaped = v.value.replace(/\|/g, "\\|");
919
- lines.push(`| ${v.key} | ${escaped} | ${v.sensitive ? "\u26A0\uFE0F yes" : "no"} |`);
920
- }
921
- lines.push("");
922
- for (const entry of differentEntries) {
923
- lines.push(`#### ${entry.file}
924
- `);
925
- lines.push("| Key | Value | Sensitive |");
926
- lines.push("|-----|-------|-----------|");
927
- for (const v of entry.values.slice(0, 30)) {
928
- const escaped = v.value.replace(/\|/g, "\\|");
929
- lines.push(`| ${v.key} | ${escaped} | ${v.sensitive ? "\u26A0\uFE0F yes" : "no"} |`);
930
- }
931
- lines.push("");
932
- }
933
- continue;
934
- }
935
- }
936
- lines.push(`### ${type}
937
- `);
938
- for (const entry of entries) {
939
- lines.push(`#### ${entry.file}
940
- `);
941
- lines.push("| Key | Value | Sensitive |");
942
- lines.push("|-----|-------|-----------|");
943
- for (const v of entry.values.slice(0, 50)) {
944
- const escaped = v.value.replace(/\|/g, "\\|");
945
- lines.push(`| ${v.key} | ${escaped} | ${v.sensitive ? "\u26A0\uFE0F yes" : "no"} |`);
946
- }
947
- if (entry.values.length > 50) {
948
- lines.push(`
949
- _...and ${entry.values.length - 50} more values._`);
950
- }
951
- lines.push("");
952
- }
953
- }
954
- const sensitiveCount = configs.reduce(
955
- (sum, c) => sum + c.values.filter((v) => v.sensitive).length,
956
- 0
957
- );
958
- if (sensitiveCount > 0) {
959
- lines.push(`
960
- **\u26A0\uFE0F ${sensitiveCount} sensitive values detected** (values masked).`);
961
- }
962
- return lines.join("\n");
963
- }
964
- async function onboard(options) {
965
- const totalStart = Date.now();
966
- const rootPath = resolve(options.path);
967
- const projectName = basename(rootPath);
968
- const mode = options.mode ?? "memory";
969
- const outDir = options.outDir ?? join(rootPath, ".ai", "kb");
970
- const structureAnalyzer = new StructureAnalyzer();
971
- const dependencyAnalyzer = new DependencyAnalyzer();
972
- const symbolAnalyzer = new SymbolAnalyzer();
973
- const patternAnalyzer = new PatternAnalyzer();
974
- const entryPointAnalyzer = new EntryPointAnalyzer();
975
- const diagramGenerator = new DiagramGenerator();
976
- const tasks = [
977
- {
978
- name: "structure",
979
- fn: () => structureAnalyzer.analyze(rootPath, { format: "markdown", maxDepth: 3, sourceOnly: true })
980
- },
981
- {
982
- name: "dependencies",
983
- fn: () => dependencyAnalyzer.analyze(rootPath, { format: "markdown" })
984
- },
985
- { name: "entry-points", fn: () => entryPointAnalyzer.analyze(rootPath) },
986
- { name: "symbols", fn: () => symbolAnalyzer.analyze(rootPath, { format: "markdown" }) },
987
- { name: "patterns", fn: () => patternAnalyzer.analyze(rootPath) },
988
- {
989
- name: "diagram",
990
- fn: () => diagramGenerator.analyze(rootPath, { diagramType: "architecture" })
991
- }
992
- ];
993
- const settled = await Promise.allSettled(
994
- tasks.map(async (task) => {
995
- const taskStart = Date.now();
996
- const result = await task.fn();
997
- return { name: task.name, result, durationMs: Date.now() - taskStart };
998
- })
999
- );
1000
- const steps = [];
1001
- const outputs = /* @__PURE__ */ new Map();
1002
- const dataMap = /* @__PURE__ */ new Map();
1003
- for (const entry of settled) {
1004
- if (entry.status === "fulfilled") {
1005
- const { name, result, durationMs } = entry.value;
1006
- const analysisResult = result;
1007
- steps.push({ name, status: "success", output: analysisResult.output, durationMs });
1008
- outputs.set(name, analysisResult.output);
1009
- dataMap.set(name, analysisResult.data);
1010
- } else {
1011
- const reason = entry.reason;
1012
- const idx = settled.indexOf(entry);
1013
- const name = tasks[idx].name;
1014
- steps.push({
1015
- name,
1016
- status: "failed",
1017
- output: "",
1018
- durationMs: 0,
1019
- error: reason.message
1020
- });
1021
- }
1022
- }
1023
- const callGraphStart = Date.now();
1024
- let callGraph = null;
1025
- try {
1026
- let result = await extractTsCallGraph(rootPath);
1027
- if (!result || result.edges.length === 0) {
1028
- result = await extractRegexCallGraph(rootPath);
1029
- }
1030
- if (result && result.edges.length > 0) {
1031
- callGraph = /* @__PURE__ */ new Map();
1032
- for (const edge of result.edges) {
1033
- let targets = callGraph.get(edge.from);
1034
- if (!targets) {
1035
- targets = /* @__PURE__ */ new Map();
1036
- callGraph.set(edge.from, targets);
1037
- }
1038
- const existing = targets.get(edge.to);
1039
- if (existing) {
1040
- for (const s of edge.symbols) {
1041
- if (!existing.includes(s)) existing.push(s);
1042
- }
1043
- } else {
1044
- targets.set(edge.to, [...edge.symbols]);
1045
- }
1046
- }
1047
- }
1048
- } catch {
1049
- }
1050
- const callGraphDurationMs = Date.now() - callGraphStart;
1051
- const codeMapStart = Date.now();
1052
- const codeMap = buildCodeMap(dataMap, projectName, callGraph);
1053
- const codeMapDurationMs = Date.now() - codeMapStart + callGraphDurationMs;
1054
- steps.push({
1055
- name: "code-map",
1056
- status: "success",
1057
- output: codeMap,
1058
- durationMs: codeMapDurationMs
1059
- });
1060
- outputs.set("code-map", codeMap);
1061
- if (callGraph && callGraph.size > 0) {
1062
- const enhancedDiagram = buildDiagrams(callGraph, dataMap, projectName);
1063
- const diagramStep = steps.find((s) => s.name === "diagram");
1064
- if (diagramStep) {
1065
- diagramStep.output = enhancedDiagram;
1066
- outputs.set("diagram", enhancedDiagram);
1067
- }
1068
- }
1069
- const configStart = Date.now();
1070
- const configValues = await extractConfigValues(rootPath, projectName);
1071
- const configDurationMs = Date.now() - configStart;
1072
- steps.push({
1073
- name: "config-values",
1074
- status: "success",
1075
- output: configValues,
1076
- durationMs: configDurationMs
1077
- });
1078
- outputs.set("config-values", configValues);
1079
- const synthesisGuide = buildSynthesisGuide(steps, mode, projectName, dataMap);
1080
- steps.push({
1081
- name: "synthesis-guide",
1082
- status: "success",
1083
- output: synthesisGuide,
1084
- durationMs: 0
1085
- });
1086
- outputs.set("synthesis-guide", synthesisGuide);
1087
- if (mode === "generate") {
1088
- if (existsSync(outDir)) {
1089
- for (const file of readdirSync(outDir)) {
1090
- if (file.endsWith(".md") || file.endsWith(".json")) {
1091
- rmSync(join(outDir, file), { force: true });
1092
- }
1093
- }
1094
- }
1095
- mkdirSync(outDir, { recursive: true });
1096
- const now = (/* @__PURE__ */ new Date()).toISOString();
1097
- for (const [name, output] of outputs) {
1098
- const fileName = `${name}.md`;
1099
- const filePath = join(outDir, fileName);
1100
- const cleaned = output.replaceAll(rootPath, projectName);
1101
- const timestamp = `<!-- Generated: ${now} -->
1102
- <!-- Project: ${projectName} -->
1103
- <!-- Source: ${rootPath} -->
13
+ ${r.join(`
14
+ `)}`,$=[];$.push("```mermaid"),$.push("graph TB");const f=new Set;for(const[,e]of b)for(const o of e.triggers)f.add(o);if(f.size>0){$.push(' subgraph Triggers["External Triggers"]');for(const e of[...f].sort()){const o=`trigger_${e.replace(/[^a-zA-Z0-9]/g,"_")}`;$.push(` ${o}(("${e}"))`)}$.push(" end"),$.push("")}const _=[...d].filter(e=>b.has(e)).sort(),v=[...d].filter(e=>!b.has(e)).sort();if(_.length>0){$.push(' subgraph Services["Service Layer"]');for(const e of _){const o=`flow_${l(e)}`,s=e.includes("/")?e.split("/").pop()??e:e,c=b.get(e);$.push(` ${o}["${s} (${c?.count??0} handlers)"]`)}$.push(" end"),$.push("")}if(v.length>0){$.push(' subgraph Libraries["Shared Libraries"]');for(const e of v){const o=`flow_${l(e)}`,s=e.includes("/")?e.split("/").pop()??e:e;$.push(` ${o}["${s}"]`)}$.push(" end"),$.push("")}if(k.length>0){$.push(' subgraph External["AWS Services"]');for(const e of k)$.push(` flow_ext_${e.id}[("${e.name}")]`);$.push(" end"),$.push("")}for(const e of _){const o=b.get(e);if(!o)continue;const s=`flow_${l(e)}`;for(const c of o.triggers){const h=`trigger_${c.replace(/[^a-zA-Z0-9]/g,"_")}`;$.push(` ${h} --> ${s}`)}}const M=P.filter(([e])=>{const[o,s]=e.split("|");return b.has(o)&&!b.has(s)});for(const[e,o]of M.slice(0,15)){const[s,c]=e.split("|");$.push(` flow_${l(s)} -->|${o}| flow_${l(c)}`)}const j=P.filter(([e])=>{const[o,s]=e.split("|");return!b.has(o)&&!b.has(s)});for(const[e,o]of j.slice(0,10)){const[s,c]=e.split("|");$.push(` flow_${l(s)} -->|${o}| flow_${l(c)}`)}$.push("```");const m=`## Architectural Flow
1104
15
 
1105
- `;
1106
- writeFileSync(filePath, timestamp + cleaned, "utf-8");
1107
- }
1108
- const indexLines = [
1109
- `<!-- Generated: ${now} -->`,
1110
- `<!-- Project: ${projectName} -->`,
1111
- `<!-- Source: ${rootPath} -->`,
1112
- "",
1113
- `# ${projectName} \u2014 Codebase Knowledge`,
1114
- "",
1115
- "## Contents",
1116
- ""
1117
- ];
1118
- for (const step of steps) {
1119
- const fileName = `${step.name}.md`;
1120
- const title = DISPLAY_TITLES[step.name] ?? step.name;
1121
- const status = step.status === "success" ? "\u2713" : "\u2717";
1122
- const timing = step.durationMs > 0 ? ` (${step.durationMs}ms)` : "";
1123
- indexLines.push(`- ${status} [${title}](./${fileName})${timing}`);
1124
- }
1125
- indexLines.push("");
1126
- writeFileSync(join(outDir, "README.md"), indexLines.join("\n"), "utf-8");
1127
- }
1128
- return {
1129
- path: rootPath,
1130
- mode,
1131
- steps,
1132
- outDir: mode === "generate" ? outDir : void 0,
1133
- totalDurationMs: Date.now() - totalStart
1134
- };
1135
- }
1136
- export {
1137
- onboard
1138
- };
1139
- //# sourceMappingURL=onboard.js.map
16
+ ${$.join(`
17
+ `)}`,S=[`# Architecture Diagrams: ${t}
18
+ `];return S.push(T),S.push(m),S.join(`
19
+
20
+ ---
21
+
22
+ `)}function ue(g,u){if(u.has(g))return g;for(const t of[".ts",".tsx",".js",".jsx"])if(u.has(`${g}${t}`))return`${g}${t}`;return u.has(`${g}/index.ts`)?`${g}/index.ts`:g}const ge=new Set(["node_modules",".git","dist","build","coverage",".turbo",".cache","cdk.out","__pycache__",".venv","target","obj",".gradle"]),G=[{glob:/\.env(?:\.\w+)?$/,type:"env"},{glob:/\.env\.example$/,type:"env"},{glob:/package\.json$/,type:"package-json"},{glob:/^(?:app|config|settings|default)\.(?:json|ya?ml|toml)$/i,type:"config"},{glob:/docker-compose\.ya?ml$/,type:"docker"},{glob:/cdk\.json$/,type:"cdk"},{glob:/turbo\.json$/,type:"tooling"},{glob:/application\.(?:properties|ya?ml)$/i,type:"spring"},{glob:/settings\.py$/,type:"django"},{glob:/\.flaskenv$/,type:"env"},{glob:/appsettings\.(?:\w+\.)?json$/i,type:"dotnet"}];async function pe(g,u){const t=[],p=await fe(g),n=/kb\.config\.json$/;for(const a of p)try{const i=H(g,a).replace(/\\/g,"/");if(n.test(i))continue;const d=await Z(a,"utf-8"),y=de(a);if(i.split("/").length-1>1&&y==="tooling")continue;const k=he(d,y);k.length>0&&t.push({file:i,type:y,values:k})}catch{}return me(t,u)}async function fe(g){const u=[],t=async(p,n)=>{if(!(n>3))try{const a=await J(p,{withFileTypes:!0});for(const i of a){if(ge.has(i.name))continue;const d=K(p,i.name);i.isDirectory()&&!i.name.startsWith(".")?await t(d,n+1):i.isFile()&&G.some(y=>y.glob.test(i.name))&&u.push(d)}}catch{}};return await t(g,0),u}function de(g){const u=L(g);for(const t of G)if(t.glob.test(u))return t.type;return"unknown"}const B=/(?:secret|password|token|key|api.?key|auth|credential|private)/i;function he(g,u){const t=[];if(u==="env")for(const p of g.split(`
23
+ `)){const n=p.trim();if(!n||n.startsWith("#"))continue;const a=n.indexOf("=");if(a===-1)continue;const i=n.slice(0,a).trim(),d=n.slice(a+1).trim(),y=B.test(i);t.push({key:i,value:y?"***":d,sensitive:y})}else if(u==="package-json")try{const p=JSON.parse(g);if(p.scripts)for(const[n,a]of Object.entries(p.scripts))t.push({key:`scripts.${n}`,value:String(a),sensitive:!1});if(p.engines)for(const[n,a]of Object.entries(p.engines))t.push({key:`engines.${n}`,value:String(a),sensitive:!1})}catch{}else if(u==="spring")for(const p of g.split(`
24
+ `)){const n=p.trim();if(!n||n.startsWith("#")||n.startsWith("---"))continue;const a=n.match(/^([\w.[\]-]+)\s*[=:]\s*(.*)$/);if(a){const i=a[1],d=a[2].trim(),y=B.test(i);t.push({key:i,value:y?"***":d,sensitive:y})}}else if(u==="json"||u==="config"||u==="cdk"||u==="tooling"||u==="dotnet")try{const p=JSON.parse(g);F(p,"",t,0)}catch{}else if(u==="django")for(const p of g.split(`
25
+ `)){const n=p.match(/^([A-Z_][A-Z0-9_]*)\s*=\s*(.+)$/);if(n){const a=n[1],i=n[2].trim(),d=B.test(a);t.push({key:a,value:d?"***":i.slice(0,100),sensitive:d})}}return t}function F(g,u,t,p){if(!(p>3)&&g!=null&&typeof g=="object"&&!Array.isArray(g))for(const[n,a]of Object.entries(g)){const i=u?`${u}.${n}`:n;if(typeof a=="object"&&a!==null&&!Array.isArray(a))F(a,i,t,p+1);else{const d=B.test(n),y=Array.isArray(a)?`[${a.length} items]`:String(a);t.push({key:i,value:d?"***":y.slice(0,120),sensitive:d})}}}function me(g,u){const t=[];if(t.push(`## Configuration Values: ${u}
26
+ `),g.length===0)return t.push("No configuration files detected."),t.join(`
27
+ `);t.push(`**${g.length} config files** found
28
+ `);const p=new Map;for(const a of g)p.has(a.type)||p.set(a.type,[]),p.get(a.type)?.push(a);for(const[a,i]of p){if(a==="package-json"&&i.length>2){t.push(`### ${a}
29
+ `);const d=i.find(b=>b.file==="package.json");if(d){t.push(`#### ${d.file}
30
+ `),t.push("| Key | Value | Sensitive |"),t.push("|-----|-------|-----------|");for(const b of d.values.slice(0,50)){const k=b.value.replace(/\|/g,"\\|");t.push(`| ${b.key} | ${k} | ${b.sensitive?"\u26A0\uFE0F yes":"no"} |`)}t.push("")}const y=i.filter(b=>b.file!=="package.json");if(y.length>0){const b=new Map;for(const l of y)for(const r of l.values){const w=`${r.key}=${r.value}`;b.set(w,(b.get(w)??0)+1)}const k=Math.max(2,Math.floor(y.length*.5));t.push(`#### Sub-packages (${y.length} packages)
31
+ `);const x=[...b.entries()].filter(([,l])=>l>=k).map(([l])=>{const[r,...w]=l.split("=");return{key:r,value:w.join("=")}});if(x.length>0){t.push(`**Common scripts** (shared by most sub-packages):
32
+ `),t.push("| Key | Value |"),t.push("|-----|-------|");for(const l of x)t.push(`| ${l.key} | ${l.value.replace(/\|/g,"\\|")} |`);t.push("")}const C=new Map;for(const l of y){const r=l.values.filter(P=>{const T=`${P.key}=${P.value}`;return(b.get(T)??0)<k});if(r.length===0)continue;const w=r.map(P=>`${P.key}=${P.value}`).sort().join("||"),D=C.get(w);D?D.files.push(l.file):C.set(w,{files:[l.file],entries:r.map(P=>({key:P.key,value:P.value}))})}for(const[,l]of C){l.files.length>1?t.push(`**${l.files.length} packages** (${l.files.map(r=>r.split("/").slice(-2,-1)[0]||r).join(", ")}):`):t.push(`**${l.files[0]}**:`),t.push("| Key | Value |"),t.push("|-----|-------|");for(const r of l.entries)t.push(`| ${r.key} | ${r.value.replace(/\|/g,"\\|")} |`);t.push("")}}continue}if(i.length>3){const d=i.map(k=>k.values.map(x=>`${x.key}=${x.value}`).sort().join("||")),y=d.sort((k,x)=>d.filter(C=>C===x).length-d.filter(C=>C===k).length)[0];if(d.filter(k=>k===y).length>2){t.push(`### ${a}
33
+ `);const k=i[d.indexOf(y)],x=i.filter((l,r)=>d[r]===y).map(l=>l.file),C=i.filter((l,r)=>d[r]!==y);t.push(`**${x.length} identical files**: ${x.join(", ")}
34
+ `),t.push("| Key | Value | Sensitive |"),t.push("|-----|-------|-----------|");for(const l of k.values.slice(0,30)){const r=l.value.replace(/\|/g,"\\|");t.push(`| ${l.key} | ${r} | ${l.sensitive?"\u26A0\uFE0F yes":"no"} |`)}t.push("");for(const l of C){t.push(`#### ${l.file}
35
+ `),t.push("| Key | Value | Sensitive |"),t.push("|-----|-------|-----------|");for(const r of l.values.slice(0,30)){const w=r.value.replace(/\|/g,"\\|");t.push(`| ${r.key} | ${w} | ${r.sensitive?"\u26A0\uFE0F yes":"no"} |`)}t.push("")}continue}}t.push(`### ${a}
36
+ `);for(const d of i){t.push(`#### ${d.file}
37
+ `),t.push("| Key | Value | Sensitive |"),t.push("|-----|-------|-----------|");for(const y of d.values.slice(0,50)){const b=y.value.replace(/\|/g,"\\|");t.push(`| ${y.key} | ${b} | ${y.sensitive?"\u26A0\uFE0F yes":"no"} |`)}d.values.length>50&&t.push(`
38
+ _...and ${d.values.length-50} more values._`),t.push("")}}const n=g.reduce((a,i)=>a+i.values.filter(d=>d.sensitive).length,0);return n>0&&t.push(`
39
+ **\u26A0\uFE0F ${n} sensitive values detected** (values masked).`),t.join(`
40
+ `)}async function ve(g){const u=Date.now(),t=Q(g.path),p=L(t),n=g.mode??"memory",a=g.outDir??K(t,".ai","kb"),i=new oe,d=new X,y=new re,b=new ne,k=new ee,x=new Y,C=[{name:"structure",fn:()=>i.analyze(t,{format:"markdown",maxDepth:3,sourceOnly:!0})},{name:"dependencies",fn:()=>d.analyze(t,{format:"markdown"})},{name:"entry-points",fn:()=>k.analyze(t)},{name:"symbols",fn:()=>y.analyze(t,{format:"markdown"})},{name:"patterns",fn:()=>b.analyze(t)},{name:"diagram",fn:()=>x.analyze(t,{diagramType:"architecture"})}],l=await Promise.allSettled(C.map(async e=>{const o=Date.now(),s=await e.fn();return{name:e.name,result:s,durationMs:Date.now()-o}})),r=[],w=new Map,D=new Map;for(const e of l)if(e.status==="fulfilled"){const{name:o,result:s,durationMs:c}=e.value,h=s;r.push({name:o,status:"success",output:h.output,durationMs:c}),w.set(o,h.output),D.set(o,h.data)}else{const o=e.reason,s=l.indexOf(e),c=C[s].name;r.push({name:c,status:"failed",output:"",durationMs:0,error:o.message})}const P=Date.now();let T=null;try{let e=await se(t);if((!e||e.edges.length===0)&&(e=await te(t)),e&&e.edges.length>0){T=new Map;for(const o of e.edges){let s=T.get(o.from);s||(s=new Map,T.set(o.from,s));const c=s.get(o.to);if(c)for(const h of o.symbols)c.includes(h)||c.push(h);else s.set(o.to,[...o.symbols])}}}catch{}const $=Date.now()-P,f=Date.now(),_=ce(D,p,T),v=Date.now()-f+$;if(r.push({name:"code-map",status:"success",output:_,durationMs:v}),w.set("code-map",_),T&&T.size>0){const e=le(T,D,p),o=r.find(s=>s.name==="diagram");o&&(o.output=e,w.set("diagram",e))}const M=Date.now(),j=await pe(t,p),m=Date.now()-M;r.push({name:"config-values",status:"success",output:j,durationMs:m}),w.set("config-values",j);const S=ie(r,n,p,D);if(r.push({name:"synthesis-guide",status:"success",output:S,durationMs:0}),w.set("synthesis-guide",S),n==="generate"){if(W(a))for(const s of q(a))(s.endsWith(".md")||s.endsWith(".json"))&&U(K(a,s),{force:!0});V(a,{recursive:!0});const e=new Date().toISOString();for(const[s,c]of w){const h=`${s}.md`,A=K(a,h),E=c.replaceAll(t,p),O=`<!-- Generated: ${e} -->
41
+ <!-- Project: ${p} -->
42
+ <!-- Source: ${t} -->
43
+
44
+ `;I(A,O+E,"utf-8")}const o=[`<!-- Generated: ${e} -->`,`<!-- Project: ${p} -->`,`<!-- Source: ${t} -->`,"",`# ${p} \u2014 Codebase Knowledge`,"","## Contents",""];for(const s of r){const c=`${s.name}.md`,h=N[s.name]??s.name,A=s.status==="success"?"\u2713":"\u2717",E=s.durationMs>0?` (${s.durationMs}ms)`:"";o.push(`- ${A} [${h}](./${c})${E}`)}o.push(""),I(K(a,"README.md"),o.join(`
45
+ `),"utf-8")}return{path:t,mode:n,steps:r,outDir:n==="generate"?a:void 0,totalDurationMs:Date.now()-u}}export{ve as onboard};