archbyte 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 (142) hide show
  1. package/README.md +282 -0
  2. package/bin/archbyte.js +213 -0
  3. package/dist/agents/core/component-detector.d.ts +2 -0
  4. package/dist/agents/core/component-detector.js +57 -0
  5. package/dist/agents/core/connection-mapper.d.ts +2 -0
  6. package/dist/agents/core/connection-mapper.js +77 -0
  7. package/dist/agents/core/doc-parser.d.ts +2 -0
  8. package/dist/agents/core/doc-parser.js +64 -0
  9. package/dist/agents/core/env-detector.d.ts +2 -0
  10. package/dist/agents/core/env-detector.js +51 -0
  11. package/dist/agents/core/event-detector.d.ts +2 -0
  12. package/dist/agents/core/event-detector.js +59 -0
  13. package/dist/agents/core/infra-analyzer.d.ts +2 -0
  14. package/dist/agents/core/infra-analyzer.js +72 -0
  15. package/dist/agents/core/structure-scanner.d.ts +2 -0
  16. package/dist/agents/core/structure-scanner.js +55 -0
  17. package/dist/agents/core/validator.d.ts +2 -0
  18. package/dist/agents/core/validator.js +74 -0
  19. package/dist/agents/index.d.ts +24 -0
  20. package/dist/agents/index.js +73 -0
  21. package/dist/agents/llm/index.d.ts +8 -0
  22. package/dist/agents/llm/index.js +185 -0
  23. package/dist/agents/llm/prompt-builder.d.ts +3 -0
  24. package/dist/agents/llm/prompt-builder.js +251 -0
  25. package/dist/agents/llm/response-parser.d.ts +6 -0
  26. package/dist/agents/llm/response-parser.js +174 -0
  27. package/dist/agents/llm/types.d.ts +31 -0
  28. package/dist/agents/llm/types.js +2 -0
  29. package/dist/agents/pipeline/agents/component-identifier.d.ts +3 -0
  30. package/dist/agents/pipeline/agents/component-identifier.js +102 -0
  31. package/dist/agents/pipeline/agents/connection-mapper.d.ts +3 -0
  32. package/dist/agents/pipeline/agents/connection-mapper.js +126 -0
  33. package/dist/agents/pipeline/agents/flow-detector.d.ts +3 -0
  34. package/dist/agents/pipeline/agents/flow-detector.js +101 -0
  35. package/dist/agents/pipeline/agents/service-describer.d.ts +3 -0
  36. package/dist/agents/pipeline/agents/service-describer.js +100 -0
  37. package/dist/agents/pipeline/agents/validator.d.ts +3 -0
  38. package/dist/agents/pipeline/agents/validator.js +102 -0
  39. package/dist/agents/pipeline/index.d.ts +13 -0
  40. package/dist/agents/pipeline/index.js +128 -0
  41. package/dist/agents/pipeline/merger.d.ts +7 -0
  42. package/dist/agents/pipeline/merger.js +212 -0
  43. package/dist/agents/pipeline/response-parser.d.ts +5 -0
  44. package/dist/agents/pipeline/response-parser.js +43 -0
  45. package/dist/agents/pipeline/types.d.ts +92 -0
  46. package/dist/agents/pipeline/types.js +3 -0
  47. package/dist/agents/prompt-data.d.ts +1 -0
  48. package/dist/agents/prompt-data.js +15 -0
  49. package/dist/agents/prompts-encode.d.ts +9 -0
  50. package/dist/agents/prompts-encode.js +26 -0
  51. package/dist/agents/prompts.d.ts +12 -0
  52. package/dist/agents/prompts.js +30 -0
  53. package/dist/agents/providers/anthropic.d.ts +10 -0
  54. package/dist/agents/providers/anthropic.js +117 -0
  55. package/dist/agents/providers/google.d.ts +10 -0
  56. package/dist/agents/providers/google.js +136 -0
  57. package/dist/agents/providers/ollama.d.ts +9 -0
  58. package/dist/agents/providers/ollama.js +162 -0
  59. package/dist/agents/providers/openai.d.ts +9 -0
  60. package/dist/agents/providers/openai.js +142 -0
  61. package/dist/agents/providers/router.d.ts +7 -0
  62. package/dist/agents/providers/router.js +55 -0
  63. package/dist/agents/runtime/orchestrator.d.ts +34 -0
  64. package/dist/agents/runtime/orchestrator.js +193 -0
  65. package/dist/agents/runtime/registry.d.ts +23 -0
  66. package/dist/agents/runtime/registry.js +56 -0
  67. package/dist/agents/runtime/types.d.ts +117 -0
  68. package/dist/agents/runtime/types.js +29 -0
  69. package/dist/agents/static/code-sampler.d.ts +3 -0
  70. package/dist/agents/static/code-sampler.js +153 -0
  71. package/dist/agents/static/component-detector.d.ts +3 -0
  72. package/dist/agents/static/component-detector.js +404 -0
  73. package/dist/agents/static/connection-mapper.d.ts +3 -0
  74. package/dist/agents/static/connection-mapper.js +280 -0
  75. package/dist/agents/static/doc-parser.d.ts +3 -0
  76. package/dist/agents/static/doc-parser.js +358 -0
  77. package/dist/agents/static/env-detector.d.ts +3 -0
  78. package/dist/agents/static/env-detector.js +73 -0
  79. package/dist/agents/static/event-detector.d.ts +3 -0
  80. package/dist/agents/static/event-detector.js +70 -0
  81. package/dist/agents/static/file-tree-collector.d.ts +3 -0
  82. package/dist/agents/static/file-tree-collector.js +51 -0
  83. package/dist/agents/static/index.d.ts +19 -0
  84. package/dist/agents/static/index.js +307 -0
  85. package/dist/agents/static/infra-analyzer.d.ts +3 -0
  86. package/dist/agents/static/infra-analyzer.js +208 -0
  87. package/dist/agents/static/structure-scanner.d.ts +3 -0
  88. package/dist/agents/static/structure-scanner.js +195 -0
  89. package/dist/agents/static/types.d.ts +165 -0
  90. package/dist/agents/static/types.js +2 -0
  91. package/dist/agents/static/utils.d.ts +21 -0
  92. package/dist/agents/static/utils.js +146 -0
  93. package/dist/agents/static/validator.d.ts +2 -0
  94. package/dist/agents/static/validator.js +75 -0
  95. package/dist/agents/tools/claude-code.d.ts +38 -0
  96. package/dist/agents/tools/claude-code.js +129 -0
  97. package/dist/agents/tools/local-fs.d.ts +12 -0
  98. package/dist/agents/tools/local-fs.js +112 -0
  99. package/dist/agents/tools/tool-definitions.d.ts +6 -0
  100. package/dist/agents/tools/tool-definitions.js +66 -0
  101. package/dist/cli/analyze.d.ts +27 -0
  102. package/dist/cli/analyze.js +586 -0
  103. package/dist/cli/auth.d.ts +46 -0
  104. package/dist/cli/auth.js +397 -0
  105. package/dist/cli/config.d.ts +11 -0
  106. package/dist/cli/config.js +177 -0
  107. package/dist/cli/diff.d.ts +10 -0
  108. package/dist/cli/diff.js +144 -0
  109. package/dist/cli/export.d.ts +10 -0
  110. package/dist/cli/export.js +321 -0
  111. package/dist/cli/gate.d.ts +13 -0
  112. package/dist/cli/gate.js +131 -0
  113. package/dist/cli/generate.d.ts +10 -0
  114. package/dist/cli/generate.js +213 -0
  115. package/dist/cli/license-gate.d.ts +27 -0
  116. package/dist/cli/license-gate.js +121 -0
  117. package/dist/cli/patrol.d.ts +15 -0
  118. package/dist/cli/patrol.js +212 -0
  119. package/dist/cli/run.d.ts +11 -0
  120. package/dist/cli/run.js +24 -0
  121. package/dist/cli/serve.d.ts +9 -0
  122. package/dist/cli/serve.js +65 -0
  123. package/dist/cli/setup.d.ts +1 -0
  124. package/dist/cli/setup.js +233 -0
  125. package/dist/cli/shared.d.ts +68 -0
  126. package/dist/cli/shared.js +275 -0
  127. package/dist/cli/stats.d.ts +9 -0
  128. package/dist/cli/stats.js +158 -0
  129. package/dist/cli/ui.d.ts +18 -0
  130. package/dist/cli/ui.js +144 -0
  131. package/dist/cli/validate.d.ts +54 -0
  132. package/dist/cli/validate.js +315 -0
  133. package/dist/cli/workflow.d.ts +10 -0
  134. package/dist/cli/workflow.js +594 -0
  135. package/dist/server/src/generator/index.d.ts +123 -0
  136. package/dist/server/src/generator/index.js +254 -0
  137. package/dist/server/src/index.d.ts +8 -0
  138. package/dist/server/src/index.js +1311 -0
  139. package/package.json +62 -0
  140. package/ui/dist/assets/index-B66Til39.js +70 -0
  141. package/ui/dist/assets/index-BE2OWbzu.css +1 -0
  142. package/ui/dist/index.html +14 -0
@@ -0,0 +1,358 @@
1
+ // Static Analysis — Doc Parser
2
+ // Deep-scans README, CLAUDE.md, architecture docs, API docs, mermaid diagrams
3
+ // to extract project description, components, connections, endpoints, and external deps
4
+ const KNOWN_SERVICES = [
5
+ { pattern: /aws\s*s3|amazon\s*s3|@aws-sdk\/client-s3/i, name: "AWS S3" },
6
+ { pattern: /dynamodb|@aws-sdk\/client-dynamodb/i, name: "DynamoDB" },
7
+ { pattern: /aws\s*lambda|@aws-sdk\/client-lambda/i, name: "AWS Lambda" },
8
+ { pattern: /sqs|@aws-sdk\/client-sqs/i, name: "AWS SQS" },
9
+ { pattern: /sns|@aws-sdk\/client-sns/i, name: "AWS SNS" },
10
+ { pattern: /cloudflare\s*workers|wrangler/i, name: "Cloudflare Workers" },
11
+ { pattern: /redis|ioredis|@upstash\/redis/i, name: "Redis" },
12
+ { pattern: /postgres(?:ql)?|pg\b|@prisma|prisma|tokio-postgres|neon/i, name: "PostgreSQL" },
13
+ { pattern: /mysql|mariadb/i, name: "MySQL" },
14
+ { pattern: /mongodb|mongoose/i, name: "MongoDB" },
15
+ { pattern: /firebase|firestore/i, name: "Firebase" },
16
+ { pattern: /supabase/i, name: "Supabase" },
17
+ { pattern: /stripe/i, name: "Stripe" },
18
+ { pattern: /twilio/i, name: "Twilio" },
19
+ { pattern: /sendgrid|@sendgrid/i, name: "SendGrid" },
20
+ { pattern: /elasticsearch|opensearch/i, name: "Elasticsearch" },
21
+ { pattern: /rabbitmq|amqplib/i, name: "RabbitMQ" },
22
+ { pattern: /kafka|kafkajs/i, name: "Kafka" },
23
+ { pattern: /nats\.io|nats/i, name: "NATS" },
24
+ { pattern: /auth0/i, name: "Auth0" },
25
+ { pattern: /clerk/i, name: "Clerk" },
26
+ { pattern: /sentry/i, name: "Sentry" },
27
+ { pattern: /datadog/i, name: "Datadog" },
28
+ { pattern: /vercel/i, name: "Vercel" },
29
+ { pattern: /netlify/i, name: "Netlify" },
30
+ { pattern: /openai|gpt-4|gpt-3/i, name: "OpenAI" },
31
+ { pattern: /anthropic|claude/i, name: "Anthropic" },
32
+ { pattern: /mapbox/i, name: "Mapbox" },
33
+ { pattern: /google\s*places|google\s*maps/i, name: "Google Maps/Places" },
34
+ { pattern: /nginx/i, name: "NGINX" },
35
+ ];
36
+ export async function parseDocs(tk) {
37
+ const result = {
38
+ projectDescription: "",
39
+ architectureNotes: [],
40
+ apiEndpoints: [],
41
+ externalDependencies: [],
42
+ };
43
+ // Discover all markdown files worth reading
44
+ const docFiles = await discoverDocFiles(tk);
45
+ // Read root README first for project description
46
+ const readme = await tk.readFileSafe("README.md");
47
+ if (readme) {
48
+ result.projectDescription = extractDescription(readme);
49
+ scanForExternalDeps(readme, result);
50
+ }
51
+ // Read all discovered doc files in parallel (batch of 15)
52
+ const contents = await Promise.all(docFiles.slice(0, 15).map(async (f) => ({
53
+ path: f,
54
+ content: await tk.readFileSafe(f),
55
+ })));
56
+ for (const { path, content } of contents) {
57
+ if (!content)
58
+ continue;
59
+ // Extract architecture notes from architecture/design docs
60
+ if (isArchitectureDoc(path)) {
61
+ const notes = extractArchitectureNotes(path, content);
62
+ result.architectureNotes.push(...notes);
63
+ }
64
+ // Extract API endpoints from docs
65
+ const endpoints = extractAPIEndpoints(content);
66
+ result.apiEndpoints.push(...endpoints);
67
+ // Scan all docs for external service mentions
68
+ scanForExternalDeps(content, result);
69
+ }
70
+ // Parse OpenAPI spec if available
71
+ const [openApiYaml, openApiJson] = await Promise.all([
72
+ tk.readYAML("openapi.yaml"),
73
+ tk.readJSON("openapi.json"),
74
+ ]);
75
+ parseOpenAPI(openApiYaml ?? openApiJson, result);
76
+ // Also scan package.json/Cargo.toml deps for known services
77
+ await scanBuildDepsForServices(tk, result);
78
+ return result;
79
+ }
80
+ /**
81
+ * Find all documentation files worth reading — READMEs, CLAUDE.md, docs/, architecture docs
82
+ */
83
+ async function discoverDocFiles(tk) {
84
+ const files = [];
85
+ // Root-level docs
86
+ const rootEntries = await tk.listDir(".");
87
+ const rootDirs = rootEntries.filter((e) => e.type === "directory").map((e) => e.name);
88
+ // Root markdown files
89
+ for (const entry of rootEntries) {
90
+ if (entry.type === "file" && /\.(md|markdown)$/i.test(entry.name)) {
91
+ files.push(entry.name);
92
+ }
93
+ }
94
+ // docs/ directory (central documentation hub)
95
+ if (rootDirs.includes("docs")) {
96
+ const docEntries = await tk.listDir("docs");
97
+ for (const entry of docEntries) {
98
+ if (entry.type === "file" && /\.(md|markdown)$/i.test(entry.name)) {
99
+ files.push(`docs/${entry.name}`);
100
+ }
101
+ }
102
+ }
103
+ // Scan each top-level directory for CLAUDE.md, README.md, and docs/ subdirs
104
+ for (const dir of rootDirs) {
105
+ if (dir.startsWith(".") || dir === "node_modules" || dir === "dist" || dir === "target")
106
+ continue;
107
+ const entries = await tk.listDir(dir);
108
+ const fileNames = entries.map((e) => e.name);
109
+ // CLAUDE.md — often has the best architecture descriptions
110
+ if (fileNames.includes("CLAUDE.md")) {
111
+ files.push(`${dir}/CLAUDE.md`);
112
+ }
113
+ // Sub-README
114
+ if (fileNames.includes("README.md")) {
115
+ files.push(`${dir}/README.md`);
116
+ }
117
+ // Component docs/ subdirectory (e.g., server/docs/architecture.md)
118
+ if (fileNames.includes("docs") && entries.find((e) => e.name === "docs")?.type === "directory") {
119
+ const subDocs = await tk.listDir(`${dir}/docs`);
120
+ for (const entry of subDocs) {
121
+ if (entry.type === "file" && /\.(md|markdown)$/i.test(entry.name)) {
122
+ files.push(`${dir}/docs/${entry.name}`);
123
+ }
124
+ }
125
+ }
126
+ }
127
+ // Deduplicate
128
+ return [...new Set(files)];
129
+ }
130
+ function isArchitectureDoc(path) {
131
+ const lower = path.toLowerCase();
132
+ return (lower.includes("architect") ||
133
+ lower.includes("design") ||
134
+ lower.includes("overview") ||
135
+ lower.includes("claude.md") ||
136
+ lower.includes("system") ||
137
+ lower.includes("api.md") ||
138
+ lower.includes("readme.md"));
139
+ }
140
+ function extractDescription(readme) {
141
+ const lines = readme.split("\n").slice(0, 500);
142
+ let capturing = false;
143
+ const descLines = [];
144
+ for (const line of lines) {
145
+ if (!capturing && /^#\s+/.test(line)) {
146
+ capturing = true;
147
+ continue;
148
+ }
149
+ if (capturing && /^##\s+/.test(line))
150
+ break;
151
+ if (capturing)
152
+ descLines.push(line);
153
+ }
154
+ return descLines
155
+ .join("\n")
156
+ .trim()
157
+ .replace(/\n{3,}/g, "\n\n")
158
+ .slice(0, 500);
159
+ }
160
+ /**
161
+ * Extract architecture notes from a documentation file.
162
+ * Looks for: technology tables, ASCII diagrams, component descriptions,
163
+ * section headers mentioning architecture/components/services.
164
+ */
165
+ function extractArchitectureNotes(path, content) {
166
+ const notes = [];
167
+ const lines = content.split("\n");
168
+ // Extract technology stack tables (markdown tables with | Component | Technology |)
169
+ const tableBlocks = extractMarkdownTables(content);
170
+ if (tableBlocks.length > 0) {
171
+ notes.push(`[${path}] Tech stack: ${tableBlocks[0].slice(0, 400)}`);
172
+ }
173
+ // Extract ASCII architecture diagrams (blocks between ``` that contain box-drawing chars or arrows)
174
+ const codeBlocks = extractCodeBlocks(content);
175
+ for (const block of codeBlocks) {
176
+ if (isAsciiDiagram(block)) {
177
+ notes.push(`[${path}] Architecture diagram: ${block.slice(0, 500)}`);
178
+ }
179
+ }
180
+ // Extract key sections: Architecture, Components, Services, Overview, Stack
181
+ const sections = extractSections(content, [
182
+ "architecture", "components", "services", "overview",
183
+ "technology stack", "tech stack", "system design",
184
+ "quick overview", "core components", "key directories",
185
+ ]);
186
+ for (const section of sections.slice(0, 3)) {
187
+ notes.push(`[${path}] ${section.title}: ${section.content.slice(0, 400)}`);
188
+ }
189
+ // Extract service connection descriptions (patterns like "X calls Y", "X → Y")
190
+ const connectionDescs = extractConnectionDescriptions(content);
191
+ if (connectionDescs.length > 0) {
192
+ notes.push(`[${path}] Connections: ${connectionDescs.join("; ").slice(0, 400)}`);
193
+ }
194
+ return notes;
195
+ }
196
+ /**
197
+ * Extract API endpoints documented in markdown.
198
+ * Looks for: GET /path, POST /path patterns, markdown tables with endpoints
199
+ */
200
+ function extractAPIEndpoints(content) {
201
+ const endpoints = [];
202
+ const seen = new Set();
203
+ // Pattern 1: HTTP method + path (e.g., "GET /api/health", "POST /api/login")
204
+ const methodPathRegex = /\b(GET|POST|PUT|PATCH|DELETE)\s+(\/[a-zA-Z0-9/_\-{}:.*]+)/g;
205
+ let match;
206
+ while ((match = methodPathRegex.exec(content)) !== null) {
207
+ const key = `${match[1]}:${match[2]}`;
208
+ if (seen.has(key))
209
+ continue;
210
+ seen.add(key);
211
+ // Try to find description on the same or next line
212
+ const lineStart = content.lastIndexOf("\n", match.index) + 1;
213
+ const lineEnd = content.indexOf("\n", match.index + match[0].length);
214
+ const line = content.slice(lineStart, lineEnd > 0 ? lineEnd : undefined).trim();
215
+ // Strip the method+path to get remaining description
216
+ const desc = line
217
+ .replace(match[0], "")
218
+ .replace(/^[\s|*-]+/, "")
219
+ .replace(/[|`]+/g, "")
220
+ .trim()
221
+ .slice(0, 100);
222
+ endpoints.push({
223
+ method: match[1],
224
+ path: match[2],
225
+ description: desc,
226
+ });
227
+ }
228
+ return endpoints;
229
+ }
230
+ function extractMarkdownTables(content) {
231
+ const tables = [];
232
+ const lines = content.split("\n");
233
+ let inTable = false;
234
+ let tableLines = [];
235
+ for (const line of lines) {
236
+ if (line.includes("|") && line.trim().startsWith("|")) {
237
+ inTable = true;
238
+ tableLines.push(line);
239
+ }
240
+ else if (inTable) {
241
+ if (tableLines.length >= 2) {
242
+ tables.push(tableLines.join("\n"));
243
+ }
244
+ inTable = false;
245
+ tableLines = [];
246
+ }
247
+ }
248
+ if (inTable && tableLines.length >= 2) {
249
+ tables.push(tableLines.join("\n"));
250
+ }
251
+ return tables;
252
+ }
253
+ function extractCodeBlocks(content) {
254
+ const blocks = [];
255
+ const regex = /```[^\n]*\n([\s\S]*?)```/g;
256
+ let match;
257
+ while ((match = regex.exec(content)) !== null) {
258
+ blocks.push(match[1]);
259
+ }
260
+ return blocks;
261
+ }
262
+ function isAsciiDiagram(block) {
263
+ // Contains box-drawing characters, arrows, or flow indicators
264
+ return (/[┌┐└┘│─├┤┬┴┼╔╗╚╝║═]/.test(block) ||
265
+ (/[→←↓↑▶◀]/.test(block) && block.includes("|")) ||
266
+ (block.includes("-->") && block.includes("[")) || // mermaid-like
267
+ (/\+-+\+/.test(block) && block.includes("|")) // ASCII box
268
+ );
269
+ }
270
+ function extractSections(content, keywords) {
271
+ const sections = [];
272
+ const lines = content.split("\n");
273
+ for (let i = 0; i < lines.length; i++) {
274
+ const line = lines[i];
275
+ const headingMatch = line.match(/^(#{1,3})\s+(.+)/);
276
+ if (!headingMatch)
277
+ continue;
278
+ const title = headingMatch[2].trim().toLowerCase();
279
+ if (!keywords.some((kw) => title.includes(kw)))
280
+ continue;
281
+ // Collect content until next heading of same or higher level
282
+ const level = headingMatch[1].length;
283
+ const contentLines = [];
284
+ for (let j = i + 1; j < lines.length; j++) {
285
+ const nextHeading = lines[j].match(/^(#{1,3})\s+/);
286
+ if (nextHeading && nextHeading[1].length <= level)
287
+ break;
288
+ contentLines.push(lines[j]);
289
+ }
290
+ sections.push({
291
+ title: headingMatch[2].trim(),
292
+ content: contentLines.join("\n").trim(),
293
+ });
294
+ }
295
+ return sections;
296
+ }
297
+ /**
298
+ * Extract service connection descriptions from text.
299
+ * Looks for patterns like "X calls Y", "X → Y", "X sends to Y"
300
+ */
301
+ function extractConnectionDescriptions(content) {
302
+ const descs = [];
303
+ // "ServiceA calls ServiceB", "frontend talks to backend"
304
+ const callPatterns = content.match(/\b\w+\s+(?:calls?|connects?\s+to|talks?\s+to|sends?\s+to|communicates?\s+with|depends?\s+on|queries)\s+\w+/gi);
305
+ if (callPatterns) {
306
+ descs.push(...callPatterns.slice(0, 10));
307
+ }
308
+ // Arrow patterns in prose: "Backend → Database", "Client → Server"
309
+ const arrowPatterns = content.match(/\b\w+\s*[→→>-]+\s*\w+/g);
310
+ if (arrowPatterns) {
311
+ for (const p of arrowPatterns.slice(0, 10)) {
312
+ if (p.length < 50)
313
+ descs.push(p);
314
+ }
315
+ }
316
+ return descs;
317
+ }
318
+ function scanForExternalDeps(content, result) {
319
+ for (const svc of KNOWN_SERVICES) {
320
+ if (svc.pattern.test(content) && !result.externalDependencies.includes(svc.name)) {
321
+ result.externalDependencies.push(svc.name);
322
+ }
323
+ }
324
+ }
325
+ function parseOpenAPI(spec, result) {
326
+ if (!spec || typeof spec !== "object")
327
+ return;
328
+ const s = spec;
329
+ const paths = s.paths;
330
+ if (!paths)
331
+ return;
332
+ for (const [path, methods] of Object.entries(paths)) {
333
+ for (const [method, def] of Object.entries(methods)) {
334
+ if (["get", "post", "put", "patch", "delete"].includes(method)) {
335
+ const op = def;
336
+ result.apiEndpoints.push({
337
+ method: method.toUpperCase(),
338
+ path,
339
+ description: op.summary ?? op.description ?? "",
340
+ });
341
+ }
342
+ }
343
+ }
344
+ }
345
+ async function scanBuildDepsForServices(tk, result) {
346
+ const pkg = await tk.readJSON("package.json");
347
+ if (pkg) {
348
+ const depString = Object.keys({
349
+ ...pkg.dependencies,
350
+ ...pkg.devDependencies,
351
+ }).join(" ");
352
+ scanForExternalDeps(depString, result);
353
+ }
354
+ // Also check Cargo.toml at root
355
+ const cargo = await tk.readFileSafe("Cargo.toml");
356
+ if (cargo)
357
+ scanForExternalDeps(cargo, result);
358
+ }
@@ -0,0 +1,3 @@
1
+ import type { EnvResult } from "./types.js";
2
+ import type { StaticToolkit } from "./utils.js";
3
+ export declare function detectEnvironments(tk: StaticToolkit): Promise<EnvResult>;
@@ -0,0 +1,73 @@
1
+ // Static Analysis — Environment Detector
2
+ // Detects environments, config patterns, and secrets management from .env files
3
+ const SECRET_DEPS = [
4
+ "dotenv", "@aws-sdk/client-secrets-manager", "vault", "node-vault",
5
+ "@google-cloud/secret-manager", "@azure/keyvault-secrets", "infisical-sdk",
6
+ ];
7
+ export async function detectEnvironments(tk) {
8
+ const result = {
9
+ environments: [],
10
+ configPattern: null,
11
+ hasSecrets: false,
12
+ };
13
+ // Find .env files
14
+ const envFiles = await tk.globFiles(".env*");
15
+ // Parse environment names from file names
16
+ const seenEnvs = new Set();
17
+ for (const file of envFiles) {
18
+ const name = file.replace(/^\.env\.?/, "").replace(/\.example$|\.sample$|\.template$/, "");
19
+ const envName = name || "default";
20
+ // Skip example/sample templates for environment detection, but still parse vars
21
+ const isTemplate = /\.example$|\.sample$|\.template$/.test(file);
22
+ if (!seenEnvs.has(envName) && !isTemplate) {
23
+ seenEnvs.add(envName);
24
+ }
25
+ // Read variable NAMES (not values)
26
+ const content = await tk.readFileSafe(file);
27
+ if (content) {
28
+ const varNames = content
29
+ .split("\n")
30
+ .filter((line) => /^[A-Z_][A-Z0-9_]*=/.test(line.trim()))
31
+ .map((line) => line.split("=")[0].trim());
32
+ // Look for the matching environment or create one
33
+ const existing = result.environments.find((e) => e.name === envName);
34
+ if (existing) {
35
+ for (const v of varNames) {
36
+ if (!existing.variables.includes(v))
37
+ existing.variables.push(v);
38
+ }
39
+ }
40
+ else {
41
+ result.environments.push({ name: envName, variables: varNames });
42
+ }
43
+ }
44
+ }
45
+ // If no .env files, check for config directories
46
+ if (result.environments.length === 0) {
47
+ const configFiles = await tk.globFiles("config/**/*");
48
+ if (configFiles.length > 0) {
49
+ result.configPattern = "config-directory";
50
+ // Try to detect env names from config file names
51
+ for (const f of configFiles) {
52
+ const match = f.match(/(?:config\/)?(development|production|staging|test|local)\./);
53
+ if (match && !seenEnvs.has(match[1])) {
54
+ seenEnvs.add(match[1]);
55
+ result.environments.push({ name: match[1], variables: [] });
56
+ }
57
+ }
58
+ }
59
+ }
60
+ // Check for secrets management in deps
61
+ const pkg = await tk.readJSON("package.json");
62
+ if (pkg) {
63
+ const allDeps = Object.keys({
64
+ ...pkg.dependencies,
65
+ ...pkg.devDependencies,
66
+ });
67
+ result.hasSecrets = allDeps.some((d) => SECRET_DEPS.includes(d));
68
+ if (allDeps.includes("dotenv")) {
69
+ result.configPattern = result.configPattern ?? "dotenv";
70
+ }
71
+ }
72
+ return result;
73
+ }
@@ -0,0 +1,3 @@
1
+ import type { EventResult } from "./types.js";
2
+ import type { StaticToolkit } from "./utils.js";
3
+ export declare function detectEvents(tk: StaticToolkit): Promise<EventResult>;
@@ -0,0 +1,70 @@
1
+ // Static Analysis — Event Detector
2
+ // Detects event-driven architecture patterns from deps and code grep
3
+ const EDA_DEPS = [
4
+ { dep: "kafkajs", technology: "Kafka" },
5
+ { dep: "@confluentinc/kafka-javascript", technology: "Kafka" },
6
+ { dep: "amqplib", technology: "RabbitMQ" },
7
+ { dep: "bullmq", technology: "Redis Streams (BullMQ)" },
8
+ { dep: "bull", technology: "Redis Streams (Bull)" },
9
+ { dep: "bee-queue", technology: "Redis Streams (Bee-Queue)" },
10
+ { dep: "socket.io", technology: "WebSocket (Socket.IO)" },
11
+ { dep: "ws", technology: "WebSocket" },
12
+ { dep: "@google-cloud/pubsub", technology: "Google Pub/Sub" },
13
+ { dep: "@aws-sdk/client-sqs", technology: "AWS SQS" },
14
+ { dep: "@aws-sdk/client-sns", technology: "AWS SNS" },
15
+ { dep: "@aws-sdk/client-eventbridge", technology: "AWS EventBridge" },
16
+ { dep: "nats", technology: "NATS" },
17
+ { dep: "mqtt", technology: "MQTT" },
18
+ { dep: "@azure/service-bus", technology: "Azure Service Bus" },
19
+ { dep: "@azure/event-hubs", technology: "Azure Event Hubs" },
20
+ { dep: "sse-channel", technology: "Server-Sent Events" },
21
+ { dep: "better-sse", technology: "Server-Sent Events" },
22
+ ];
23
+ export async function detectEvents(tk) {
24
+ const result = {
25
+ hasEDA: false,
26
+ patterns: [],
27
+ events: [],
28
+ };
29
+ // Check package.json deps
30
+ const pkg = await tk.readJSON("package.json");
31
+ if (pkg) {
32
+ const allDeps = Object.keys({
33
+ ...pkg.dependencies,
34
+ ...pkg.devDependencies,
35
+ });
36
+ for (const edaDep of EDA_DEPS) {
37
+ if (allDeps.includes(edaDep.dep)) {
38
+ result.hasEDA = true;
39
+ result.patterns.push({
40
+ technology: edaDep.technology,
41
+ dependency: edaDep.dep,
42
+ });
43
+ }
44
+ }
45
+ }
46
+ // Grep for event patterns (filtered to exclude vendored code)
47
+ const [publishResults, subscribeResults, emitResults] = await Promise.all([
48
+ tk.grepFiles("\\.publish\\(|\\.send\\(|\\.produce\\("),
49
+ tk.grepFiles("\\.subscribe\\(|\\.consume\\(|\\.on\\("),
50
+ tk.grepFiles("\\.emit\\(|\\.dispatch\\(|\\.trigger\\("),
51
+ ]);
52
+ // Exclude vendored/generated dirs from all results
53
+ const excludeDirs = /\/(node_modules|venv|\.venv|__pycache__|dist|build|\.git|vendor|target)\//;
54
+ const filterVendored = (results) => results.filter((r) => !excludeDirs.test("/" + r.file));
55
+ // Filter subscribe results to only EDA-relevant ones (skip generic .on() calls)
56
+ const filteredSubscribe = filterVendored(subscribeResults).filter((r) => /\.(subscribe|consume)\(/.test(r.content));
57
+ for (const r of filterVendored(publishResults).slice(0, 20)) {
58
+ result.events.push({ type: "publish", file: r.file, pattern: r.content.slice(0, 100) });
59
+ }
60
+ for (const r of filteredSubscribe.slice(0, 20)) {
61
+ result.events.push({ type: "subscribe", file: r.file, pattern: r.content.slice(0, 100) });
62
+ }
63
+ for (const r of filterVendored(emitResults).slice(0, 20)) {
64
+ result.events.push({ type: "emit", file: r.file, pattern: r.content.slice(0, 100) });
65
+ }
66
+ if (result.events.length > 0) {
67
+ result.hasEDA = true;
68
+ }
69
+ return result;
70
+ }
@@ -0,0 +1,3 @@
1
+ import type { FileTreeResult } from "./types.js";
2
+ import type { StaticToolkit } from "./utils.js";
3
+ export declare function collectFileTree(tk: StaticToolkit): Promise<FileTreeResult>;
@@ -0,0 +1,51 @@
1
+ // Static Analysis — File Tree Collector
2
+ // Collects depth-limited directory tree for LLM context
3
+ const SKIP_DIRS = new Set([
4
+ "node_modules", "dist", "build", ".git", "venv", "__pycache__",
5
+ "target", "coverage", ".next", ".nuxt", ".output", ".cache",
6
+ ".turbo", ".nx", "vendor", ".venv", "env",
7
+ ]);
8
+ const MAX_ENTRIES = 2000;
9
+ const MAX_DEPTH = 3;
10
+ export async function collectFileTree(tk) {
11
+ let totalFiles = 0;
12
+ let totalDirs = 0;
13
+ let entryCount = 0;
14
+ async function walk(dirPath, depth) {
15
+ if (depth > MAX_DEPTH || entryCount >= MAX_ENTRIES)
16
+ return [];
17
+ const entries = await tk.listDir(dirPath);
18
+ const result = [];
19
+ // Sort: directories first, then files
20
+ const sorted = entries.sort((a, b) => {
21
+ if (a.type !== b.type)
22
+ return a.type === "directory" ? -1 : 1;
23
+ return a.name.localeCompare(b.name);
24
+ });
25
+ for (const entry of sorted) {
26
+ if (entryCount >= MAX_ENTRIES)
27
+ break;
28
+ // Skip hidden dirs and vendored dirs
29
+ if (entry.type === "directory") {
30
+ if (entry.name.startsWith(".") && entry.name !== ".github")
31
+ continue;
32
+ if (SKIP_DIRS.has(entry.name))
33
+ continue;
34
+ }
35
+ const entryPath = dirPath === "." ? entry.name : `${dirPath}/${entry.name}`;
36
+ entryCount++;
37
+ if (entry.type === "directory") {
38
+ totalDirs++;
39
+ const children = await walk(entryPath, depth + 1);
40
+ result.push({ path: entryPath, type: "directory", children });
41
+ }
42
+ else {
43
+ totalFiles++;
44
+ result.push({ path: entryPath, type: "file" });
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+ const tree = await walk(".", 0);
50
+ return { tree, totalFiles, totalDirs };
51
+ }
@@ -0,0 +1,19 @@
1
+ import type { StaticAnalysisResult, StaticContext } from "./types.js";
2
+ export type { StaticAnalysisResult, StaticContext } from "./types.js";
3
+ export { validateAnalysis } from "./validator.js";
4
+ /**
5
+ * Run all static analysis scanners.
6
+ *
7
+ * Pipeline:
8
+ * 1. Parallel: structure, docs, infra, events, envs
9
+ * 2. Sequential: components (needs structure), connections (needs components+infra+events)
10
+ * 3. Validation + auto-repair
11
+ * 4. Gap detection — identify what the LLM should resolve
12
+ */
13
+ export declare function runStaticAnalysis(projectRoot: string, onProgress?: (msg: string) => void): Promise<StaticAnalysisResult>;
14
+ /**
15
+ * Collect raw static context from all 7 scanners.
16
+ * This runs ONLY fact-collectors (no component-detector, connection-mapper, or validator).
17
+ * Output is consumed by the pipeline LLM agents.
18
+ */
19
+ export declare function runStaticContextCollection(projectRoot: string, onProgress?: (msg: string) => void): Promise<StaticContext>;