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,586 @@
1
+ import * as path from "path";
2
+ import * as fs from "fs";
3
+ import chalk from "chalk";
4
+ import { resolveConfig } from "./config.js";
5
+ import { recordUsage } from "./license-gate.js";
6
+ export async function handleAnalyze(options) {
7
+ const rootDir = process.cwd();
8
+ const isStaticOnly = options.static || options.skipLlm;
9
+ console.log();
10
+ console.log(chalk.bold.cyan("ArchByte Analyzer"));
11
+ if (isStaticOnly) {
12
+ console.log(chalk.gray("Static-only mode: deterministic analysis, no model"));
13
+ }
14
+ else {
15
+ console.log(chalk.gray("Static context + model pipeline"));
16
+ }
17
+ console.log();
18
+ // ─── Dry-run mode ───
19
+ if (options.dryRun) {
20
+ const config = resolveConfig();
21
+ const provider = options.provider ?? config?.provider ?? "none";
22
+ const mode = isStaticOnly ? "static (no LLM)" : "pipeline (static + LLM)";
23
+ const diagramPath = path.join(rootDir, ".archbyte", "architecture.json");
24
+ const diagramExists = fs.existsSync(diagramPath);
25
+ const outputPath = options.output ?? ".archbyte/analysis.json";
26
+ console.log(chalk.bold("ArchByte Analyze — Dry Run"));
27
+ console.log(chalk.gray(` Project: ${path.basename(rootDir)}`));
28
+ console.log(chalk.gray(` Mode: ${mode}`));
29
+ console.log(chalk.gray(` Provider: ${provider}`));
30
+ console.log(chalk.gray(` Diagram: ${diagramExists ? "exists" : "not found"}`));
31
+ console.log(chalk.gray(` Output: ${outputPath}`));
32
+ console.log();
33
+ console.log(chalk.yellow("No changes made. Remove --dry-run to execute."));
34
+ console.log();
35
+ return;
36
+ }
37
+ // ─── Premium-only mode (unchanged — uses Orchestrator) ───
38
+ if (options.premiumOnly) {
39
+ await runLegacyOrchestrator(rootDir, options, true);
40
+ return;
41
+ }
42
+ // ─── Static-only mode (--static / --skip-llm) ───
43
+ if (isStaticOnly) {
44
+ const startTime = Date.now();
45
+ console.log(chalk.bold("Running static analysis (no model)..."));
46
+ console.log();
47
+ const { runStaticAnalysis } = await import("../agents/static/index.js");
48
+ const result = await runStaticAnalysis(rootDir, (msg) => {
49
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
50
+ console.log(chalk.gray(` [${elapsed}s] ${msg}`));
51
+ });
52
+ const analysis = buildAnalysisFromStatic(result, rootDir);
53
+ const duration = Date.now() - startTime;
54
+ // Stamp scan metadata
55
+ const meta = analysis.metadata;
56
+ meta.durationMs = duration;
57
+ meta.mode = "static";
58
+ writeAnalysis(rootDir, analysis);
59
+ await autoGenerate(rootDir, options);
60
+ printSummary(analysis, duration, "static");
61
+ return;
62
+ }
63
+ // ─── Default mode: static context → LLM pipeline ───
64
+ // 1. Resolve LLM config
65
+ let config = resolveConfig();
66
+ if (options.provider) {
67
+ config = {
68
+ provider: options.provider,
69
+ apiKey: options.apiKey ?? config?.apiKey ?? "",
70
+ ollamaBaseUrl: config?.ollamaBaseUrl,
71
+ };
72
+ }
73
+ if (options.apiKey && config) {
74
+ config.apiKey = options.apiKey;
75
+ }
76
+ if (!config) {
77
+ console.error(chalk.red("No model provider configured."));
78
+ console.error();
79
+ console.error(chalk.bold("Set up with:"));
80
+ console.error(chalk.gray(" archbyte config set provider anthropic"));
81
+ console.error(chalk.gray(" archbyte config set api-key sk-ant-..."));
82
+ console.error();
83
+ console.error(chalk.bold("Or use environment variables:"));
84
+ console.error(chalk.gray(" export ARCHBYTE_PROVIDER=anthropic"));
85
+ console.error(chalk.gray(" export ARCHBYTE_API_KEY=sk-ant-..."));
86
+ console.error();
87
+ console.error(chalk.bold("Or run without a model:"));
88
+ console.error(chalk.gray(" archbyte analyze --static"));
89
+ console.error();
90
+ console.error(chalk.bold("Supported providers:"));
91
+ console.error(chalk.gray(" anthropic, openai, google, ollama"));
92
+ process.exit(1);
93
+ }
94
+ console.log(chalk.gray(`Provider: ${chalk.white(config.provider)}`));
95
+ console.log(chalk.gray(`Project: ${chalk.white(path.basename(rootDir))}`));
96
+ console.log();
97
+ // 2. Create provider
98
+ const { createProvider } = await import("../agents/providers/router.js");
99
+ let provider;
100
+ try {
101
+ provider = createProvider(config);
102
+ }
103
+ catch (err) {
104
+ console.error(chalk.red(`Failed to create ${config.provider} provider: ${err instanceof Error ? err.message : err}`));
105
+ process.exit(1);
106
+ }
107
+ // 3. Run static context collection → LLM pipeline
108
+ const startTime = Date.now();
109
+ console.log(chalk.bold("Phase 1: Collecting static context..."));
110
+ const { runStaticContextCollection } = await import("../agents/static/index.js");
111
+ const ctx = await runStaticContextCollection(rootDir, (msg) => {
112
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
113
+ console.log(chalk.gray(` [${elapsed}s] ${msg}`));
114
+ });
115
+ // Save static context for debugging / re-runs
116
+ const ctxPath = path.join(rootDir, ".archbyte", "static-context.json");
117
+ if (!fs.existsSync(path.dirname(ctxPath))) {
118
+ fs.mkdirSync(path.dirname(ctxPath), { recursive: true });
119
+ }
120
+ fs.writeFileSync(ctxPath, JSON.stringify(ctx, null, 2), "utf-8");
121
+ console.log(chalk.gray(` Saved static context to: .archbyte/static-context.json`));
122
+ console.log();
123
+ console.log(chalk.bold("Phase 2: Running model pipeline (3 parallel + 2 sequential)..."));
124
+ const { runPipeline } = await import("../agents/pipeline/index.js");
125
+ let result;
126
+ try {
127
+ result = await runPipeline(ctx, provider, config, (msg) => {
128
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
129
+ console.log(chalk.gray(` [${elapsed}s] ${msg}`));
130
+ });
131
+ }
132
+ catch (err) {
133
+ const errorMsg = err instanceof Error ? err.message : String(err);
134
+ writeAnalysisStatus(rootDir, "error", errorMsg);
135
+ console.error(chalk.yellow(`\n Pipeline failed: ${errorMsg}`));
136
+ console.log(chalk.bold.cyan("\n Falling back to static-only analysis..."));
137
+ try {
138
+ const { runStaticAnalysis } = await import("../agents/static/index.js");
139
+ const staticResult = await runStaticAnalysis(rootDir, (msg) => {
140
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
141
+ console.log(chalk.gray(` [${elapsed}s] ${msg}`));
142
+ });
143
+ const analysis = buildAnalysisFromStatic(staticResult, rootDir);
144
+ const duration = Date.now() - startTime;
145
+ const meta = analysis.metadata;
146
+ meta.durationMs = duration;
147
+ meta.mode = "static-fallback";
148
+ meta.pipelineError = errorMsg;
149
+ writeAnalysis(rootDir, analysis);
150
+ writeAnalysisStatus(rootDir, "success");
151
+ await autoGenerate(rootDir, options);
152
+ printSummary(analysis, duration, "static");
153
+ }
154
+ catch (fallbackErr) {
155
+ console.error(chalk.red(` Static fallback also failed: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}`));
156
+ process.exit(1);
157
+ }
158
+ return;
159
+ }
160
+ console.log();
161
+ // 4. Convert to analysis.json format and write
162
+ const analysis = buildAnalysisFromStatic(result, rootDir);
163
+ const duration = Date.now() - startTime;
164
+ // Stamp scan metadata
165
+ const meta = analysis.metadata;
166
+ meta.durationMs = duration;
167
+ meta.filesScanned = ctx.fileTree.totalFiles;
168
+ meta.mode = "pipeline";
169
+ if (result.tokenUsage) {
170
+ meta.tokenUsage = { input: result.tokenUsage.input, output: result.tokenUsage.output };
171
+ }
172
+ writeAnalysis(rootDir, analysis);
173
+ writeAnalysisStatus(rootDir, "success");
174
+ await autoGenerate(rootDir, options);
175
+ // Record usage (best-effort, non-blocking)
176
+ recordUsage({
177
+ projectName: path.basename(rootDir),
178
+ agentCount: 5,
179
+ durationMs: duration,
180
+ });
181
+ printSummary(analysis, duration, "pipeline");
182
+ }
183
+ // ─── Legacy orchestrator (--legacy and --premium-only) ───
184
+ async function runLegacyOrchestrator(rootDir, options, premiumOnly) {
185
+ // Resolve LLM config
186
+ let config = resolveConfig();
187
+ if (options.provider) {
188
+ config = {
189
+ provider: options.provider,
190
+ apiKey: options.apiKey ?? config?.apiKey ?? "",
191
+ ollamaBaseUrl: config?.ollamaBaseUrl,
192
+ };
193
+ }
194
+ if (options.apiKey && config) {
195
+ config.apiKey = options.apiKey;
196
+ }
197
+ if (!config) {
198
+ console.error(chalk.red("No model provider configured."));
199
+ console.error(chalk.gray(" archbyte config set provider anthropic"));
200
+ console.error(chalk.gray(" archbyte config set api-key sk-ant-..."));
201
+ process.exit(1);
202
+ }
203
+ console.log(chalk.gray(`Provider: ${chalk.white(config.provider)}`));
204
+ console.log(chalk.gray(`Project: ${chalk.white(path.basename(rootDir))}`));
205
+ console.log();
206
+ const { createProvider } = await import("../agents/providers/router.js");
207
+ const { Orchestrator } = await import("../agents/runtime/orchestrator.js");
208
+ const { LocalFSBackend } = await import("../agents/tools/local-fs.js");
209
+ const { CORE_AGENTS, loadPremiumAgents } = await import("../agents/index.js");
210
+ let provider;
211
+ try {
212
+ provider = createProvider(config);
213
+ }
214
+ catch (err) {
215
+ console.error(chalk.red(`Failed to create ${config.provider} provider: ${err instanceof Error ? err.message : err}`));
216
+ process.exit(1);
217
+ }
218
+ const tools = new LocalFSBackend(rootDir);
219
+ // Build license — use server-verified tier cache, not unverified JWT
220
+ const { loadCredentials, getVerifiedTier } = await import("./auth.js");
221
+ const creds = loadCredentials();
222
+ const verifiedTier = getVerifiedTier();
223
+ const license = {
224
+ tier: verifiedTier,
225
+ userId: "local",
226
+ email: creds?.email ?? "local",
227
+ scansUsed: 0,
228
+ scansAllowed: -1, // unlimited for all tiers
229
+ expiresAt: creds?.expiresAt ?? new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
230
+ isValid: true,
231
+ };
232
+ const orchestrator = new Orchestrator({ provider, tools, projectRoot: rootDir, config, license });
233
+ // Premium agents loaded dynamically (not shipped in npm package)
234
+ const PREMIUM_AGENTS = await loadPremiumAgents();
235
+ const ALL_AGENTS = [...CORE_AGENTS, ...PREMIUM_AGENTS];
236
+ if (premiumOnly) {
237
+ if (PREMIUM_AGENTS.length === 0) {
238
+ console.error(chalk.red("Premium agents are not available in this installation."));
239
+ process.exit(1);
240
+ }
241
+ orchestrator.registerAll(PREMIUM_AGENTS);
242
+ }
243
+ else {
244
+ orchestrator.registerAll(ALL_AGENTS);
245
+ }
246
+ const activeAgents = premiumOnly
247
+ ? PREMIUM_AGENTS
248
+ : license.tier === "premium" && PREMIUM_AGENTS.length > 0
249
+ ? ALL_AGENTS
250
+ : CORE_AGENTS;
251
+ const startTime = Date.now();
252
+ console.log(chalk.bold("Running legacy analysis pipeline..."));
253
+ console.log(chalk.gray(`Agents: ${activeAgents.length} (${activeAgents.filter(a => a.phase === "parallel").length} parallel, ${activeAgents.filter(a => a.phase === "sequential").length} sequential)`));
254
+ if (license.tier === "premium") {
255
+ console.log(chalk.magenta(` Pro agents: ${PREMIUM_AGENTS.length}`));
256
+ }
257
+ console.log();
258
+ const result = await orchestrator.run((msg) => {
259
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
260
+ console.log(chalk.gray(` [${elapsed}s] ${msg}`));
261
+ });
262
+ console.log();
263
+ if (result.errors.length > 0) {
264
+ console.log(chalk.yellow(`Completed with ${result.errors.length} error(s):`));
265
+ for (const err of result.errors) {
266
+ console.log(chalk.red(` ${err.agentId}: ${err.error}`));
267
+ }
268
+ console.log();
269
+ }
270
+ const archbyteDir = path.join(rootDir, ".archbyte");
271
+ if (!fs.existsSync(archbyteDir)) {
272
+ fs.mkdirSync(archbyteDir, { recursive: true });
273
+ }
274
+ if (premiumOnly) {
275
+ const premiumResults = result.agents.map((a) => ({
276
+ agentId: a.agentId,
277
+ confidence: a.confidence,
278
+ timestamp: new Date().toISOString(),
279
+ data: a.data,
280
+ }));
281
+ const premiumPath = path.join(archbyteDir, "premium-results.json");
282
+ fs.writeFileSync(premiumPath, JSON.stringify(premiumResults, null, 2), "utf-8");
283
+ console.log(chalk.green(`Saved premium results to: ${path.relative(rootDir, premiumPath)}`));
284
+ }
285
+ else {
286
+ const analysis = buildAnalysis(result.agents, rootDir);
287
+ writeAnalysis(rootDir, analysis);
288
+ await autoGenerate(rootDir, options);
289
+ }
290
+ // Telemetry
291
+ const telemetry = {
292
+ timestamp: new Date().toISOString(),
293
+ provider: config.provider,
294
+ totalDuration: result.totalDuration,
295
+ agents: result.agents.map((a) => ({
296
+ id: a.agentId,
297
+ duration: a.duration,
298
+ confidence: a.confidence,
299
+ })),
300
+ errors: result.errors,
301
+ };
302
+ const telemetryPath = path.join(archbyteDir, "telemetry.json");
303
+ fs.writeFileSync(telemetryPath, JSON.stringify(telemetry, null, 2), "utf-8");
304
+ recordUsage({
305
+ projectName: path.basename(rootDir),
306
+ agentCount: result.agents.length,
307
+ durationMs: result.totalDuration,
308
+ });
309
+ // Summary
310
+ console.log();
311
+ console.log(chalk.bold.green(premiumOnly ? "Pro analysis complete!" : "Legacy analysis complete!"));
312
+ console.log(chalk.gray(` Duration: ${(result.totalDuration / 1000).toFixed(1)}s`));
313
+ console.log(chalk.gray(` Agents: ${result.agents.length}/${activeAgents.length} succeeded`));
314
+ if (!premiumOnly) {
315
+ const analysisPath = path.join(rootDir, ".archbyte", "analysis.json");
316
+ try {
317
+ const analysis = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
318
+ console.log(chalk.gray(` Components: ${(analysis.components ?? []).length}`));
319
+ console.log(chalk.gray(` Connections: ${(analysis.connections ?? []).length}`));
320
+ }
321
+ catch { /* ignore */ }
322
+ }
323
+ console.log();
324
+ if (premiumOnly) {
325
+ console.log(chalk.bold("Results saved to .archbyte/premium-results.json"));
326
+ }
327
+ else {
328
+ console.log(chalk.bold("Next steps:"));
329
+ console.log(chalk.gray(` ${chalk.cyan("archbyte serve")} -- view the architecture diagram`));
330
+ console.log(chalk.gray(` ${chalk.cyan("archbyte validate")} -- check architecture fitness rules`));
331
+ console.log(chalk.gray(` ${chalk.cyan("archbyte export")} -- export to mermaid, plantuml, etc.`));
332
+ }
333
+ console.log();
334
+ }
335
+ // ─── Shared helpers ───
336
+ function writeAnalysisStatus(rootDir, status, error) {
337
+ const archbyteDir = path.join(rootDir, ".archbyte");
338
+ if (!fs.existsSync(archbyteDir)) {
339
+ fs.mkdirSync(archbyteDir, { recursive: true });
340
+ }
341
+ const statusPath = path.join(archbyteDir, "analysis-status.json");
342
+ fs.writeFileSync(statusPath, JSON.stringify({
343
+ status,
344
+ ...(error ? { error } : {}),
345
+ timestamp: new Date().toISOString(),
346
+ }, null, 2), "utf-8");
347
+ }
348
+ function writeAnalysis(rootDir, analysis) {
349
+ const archbyteDir = path.join(rootDir, ".archbyte");
350
+ if (!fs.existsSync(archbyteDir)) {
351
+ fs.mkdirSync(archbyteDir, { recursive: true });
352
+ }
353
+ const analysisPath = path.join(archbyteDir, "analysis.json");
354
+ fs.writeFileSync(analysisPath, JSON.stringify(analysis, null, 2), "utf-8");
355
+ console.log(chalk.green(`Saved analysis to: ${path.relative(rootDir, analysisPath)}`));
356
+ }
357
+ async function autoGenerate(rootDir, options) {
358
+ console.log();
359
+ console.log(chalk.gray("Generating architecture diagram..."));
360
+ const analysisPath = path.join(rootDir, ".archbyte", "analysis.json");
361
+ try {
362
+ const { handleGenerate } = await import("./generate.js");
363
+ await handleGenerate({ input: analysisPath, verbose: options.verbose });
364
+ }
365
+ catch (err) {
366
+ console.error(chalk.yellow(`Warning: diagram generation failed: ${err instanceof Error ? err.message : err}`));
367
+ console.error(chalk.gray("You can run 'archbyte generate' manually."));
368
+ }
369
+ }
370
+ function printSummary(analysis, durationMs, mode) {
371
+ const components = analysis.components ?? [];
372
+ const connections = analysis.connections ?? [];
373
+ console.log();
374
+ console.log(chalk.bold.green("Analysis complete!"));
375
+ console.log(chalk.gray(` Mode: ${mode === "pipeline" ? "static + agents" : "static"}`));
376
+ console.log(chalk.gray(` Duration: ${(durationMs / 1000).toFixed(1)}s`));
377
+ console.log(chalk.gray(` Components: ${components.length}`));
378
+ console.log(chalk.gray(` Connections: ${connections.length}`));
379
+ console.log();
380
+ console.log(chalk.bold("Next steps:"));
381
+ console.log(chalk.gray(` ${chalk.cyan("archbyte serve")} -- view the architecture diagram`));
382
+ console.log(chalk.gray(` ${chalk.cyan("archbyte validate")} -- check architecture fitness rules`));
383
+ console.log(chalk.gray(` ${chalk.cyan("archbyte export")} -- export to mermaid, plantuml, etc.`));
384
+ console.log();
385
+ }
386
+ // ─── Analysis converters ───
387
+ /**
388
+ * Convert a StaticAnalysisResult (from pipeline or static-only) into the
389
+ * flat analysis.json format expected by generate.ts.
390
+ */
391
+ export function buildAnalysisFromStatic(result, projectRoot) {
392
+ const allComponents = result.components.components;
393
+ // Partition components by role
394
+ const appComponents = allComponents
395
+ .filter((c) => c.layer !== "external" && c.type !== "database" && c.layer !== "data")
396
+ .map((c) => ({
397
+ id: c.id,
398
+ name: c.name,
399
+ type: mapComponentType(c.type),
400
+ path: c.path,
401
+ techStack: c.technologies,
402
+ description: c.description,
403
+ layer: c.layer,
404
+ }));
405
+ const databases = allComponents
406
+ .filter((c) => c.type === "database" || c.layer === "data")
407
+ .map((c) => ({
408
+ id: c.id,
409
+ name: c.name,
410
+ type: c.technologies[0] ?? "database",
411
+ }));
412
+ const externalServices = allComponents
413
+ .filter((c) => c.layer === "external")
414
+ .map((c) => ({
415
+ id: c.id,
416
+ name: c.name,
417
+ type: c.technologies[0] ?? "api",
418
+ }));
419
+ // Connections
420
+ const mappedConnections = result.connections.connections.map((c) => ({
421
+ from: c.from,
422
+ to: c.to,
423
+ type: c.type,
424
+ description: c.description,
425
+ async: c.async,
426
+ }));
427
+ // Environments
428
+ const environments = result.envs.environments.map((e) => ({
429
+ name: e.name,
430
+ label: e.name.charAt(0).toUpperCase() + e.name.slice(1),
431
+ color: e.name === "production" ? "#ef4444" : e.name === "staging" ? "#f59e0b" : "#22c55e",
432
+ }));
433
+ // Flows — only keep steps that are valid "componentId → componentId" pairs
434
+ const allComponentIds = new Set(allComponents.map((c) => c.id));
435
+ const flows = result.connections.flows.map((f, i) => ({
436
+ id: `flow-${i}`,
437
+ name: f.name,
438
+ description: f.description,
439
+ steps: f.steps
440
+ .map((step) => {
441
+ const parts = step.split("\u2192").map((s) => s.trim());
442
+ const from = parts[0] ?? "";
443
+ const to = parts[1] ?? "";
444
+ return { from, to, label: step };
445
+ })
446
+ .filter((s) => s.from && s.to && allComponentIds.has(s.from) && allComponentIds.has(s.to)),
447
+ })).filter((f) => f.steps.length > 0);
448
+ return {
449
+ project: {
450
+ name: result.structure.projectName ?? path.basename(projectRoot),
451
+ description: result.docs.projectDescription ?? "",
452
+ primaryLanguage: result.structure.language,
453
+ isMonorepo: result.structure.isMonorepo,
454
+ },
455
+ components: appComponents,
456
+ databases,
457
+ externalServices,
458
+ connections: mappedConnections,
459
+ environments,
460
+ flows,
461
+ metadata: {
462
+ analyzedAt: new Date().toISOString(),
463
+ validation: result.validation,
464
+ gaps: result.gaps,
465
+ },
466
+ };
467
+ }
468
+ /**
469
+ * Aggregate agent results into the analysis.json format expected by the generator.
470
+ * Used by legacy mode (--legacy flag) only.
471
+ */
472
+ export function buildAnalysis(agents, projectRoot) {
473
+ const getAgentData = (id) => {
474
+ const agent = agents.find((a) => a.agentId === id);
475
+ if (!agent?.data || typeof agent.data !== "object")
476
+ return {};
477
+ return agent.data;
478
+ };
479
+ const structure = getAgentData("structure-scanner");
480
+ const components = getAgentData("component-detector");
481
+ const connections = getAgentData("connection-mapper");
482
+ const docs = getAgentData("doc-parser");
483
+ const envs = getAgentData("env-detector");
484
+ const validation = getAgentData("validator");
485
+ const componentList = components.components ?? [];
486
+ const mappedComponents = componentList.map((c, i) => ({
487
+ id: slugify(c.name) ?? `component-${i}`,
488
+ name: c.name ?? `Component ${i}`,
489
+ type: mapComponentType(c.type),
490
+ path: c.path ?? "",
491
+ techStack: c.technologies ?? [],
492
+ description: c.description ?? "",
493
+ layer: c.layer ?? "application",
494
+ }));
495
+ const connectionList = connections.connections ?? [];
496
+ const mappedConnections = connectionList.map((c) => ({
497
+ from: slugify(c.source) ?? "",
498
+ to: slugify(c.target) ?? "",
499
+ type: c.type ?? "import",
500
+ description: c.label ?? "",
501
+ async: c.style === "dashed",
502
+ }));
503
+ const databases = mappedComponents
504
+ .filter((c) => c.type === "database" || c.layer === "data")
505
+ .map((c) => ({
506
+ id: c.id,
507
+ name: c.name,
508
+ type: "postgresql",
509
+ }));
510
+ const externalServices = mappedComponents
511
+ .filter((c) => c.layer === "external")
512
+ .map((c) => ({
513
+ id: c.id,
514
+ name: c.name,
515
+ type: "api",
516
+ }));
517
+ const appComponents = mappedComponents.filter((c) => c.layer !== "external" && c.type !== "database");
518
+ const envList = envs.environments ?? [];
519
+ const environments = envList.map((e) => ({
520
+ name: e.name ?? "default",
521
+ label: (e.name ?? "default").charAt(0).toUpperCase() + (e.name ?? "default").slice(1),
522
+ color: e.name === "production" ? "#ef4444" : e.name === "staging" ? "#f59e0b" : "#22c55e",
523
+ }));
524
+ const flowList = connections.flows ?? [];
525
+ const flows = flowList.map((f, i) => ({
526
+ id: `flow-${i}`,
527
+ name: f.name ?? `Flow ${i}`,
528
+ description: f.description ?? "",
529
+ steps: (f.steps ?? []).map((step) => {
530
+ const parts = step.split("\u2192").map((s) => s.trim());
531
+ return {
532
+ from: slugify(parts[0]) ?? "",
533
+ to: slugify(parts[1]) ?? "",
534
+ label: step,
535
+ };
536
+ }),
537
+ }));
538
+ return {
539
+ project: {
540
+ name: structure.projectName ?? path.basename(projectRoot),
541
+ description: docs.projectDescription ?? "",
542
+ primaryLanguage: structure.language,
543
+ isMonorepo: structure.isMonorepo ?? false,
544
+ },
545
+ components: appComponents,
546
+ databases,
547
+ externalServices,
548
+ connections: mappedConnections,
549
+ environments,
550
+ flows,
551
+ metadata: {
552
+ analyzedAt: new Date().toISOString(),
553
+ agentResults: agents.map((a) => ({
554
+ id: a.agentId,
555
+ confidence: a.confidence,
556
+ })),
557
+ validation,
558
+ },
559
+ };
560
+ }
561
+ function slugify(name) {
562
+ if (!name)
563
+ return undefined;
564
+ return name
565
+ .toLowerCase()
566
+ .replace(/[^a-z0-9]+/g, "-")
567
+ .replace(/^-|-$/g, "");
568
+ }
569
+ function mapComponentType(type) {
570
+ if (!type)
571
+ return "service";
572
+ const t = type.toLowerCase();
573
+ if (t.includes("frontend") || t.includes("ui") || t.includes("web"))
574
+ return "frontend";
575
+ if (t.includes("api") || t.includes("gateway"))
576
+ return "api";
577
+ if (t.includes("database") || t.includes("db"))
578
+ return "database";
579
+ if (t.includes("worker") || t.includes("job") || t.includes("queue"))
580
+ return "worker";
581
+ if (t.includes("library") || t.includes("lib") || t.includes("package"))
582
+ return "library";
583
+ if (t.includes("cache"))
584
+ return "cache";
585
+ return "service";
586
+ }
@@ -0,0 +1,46 @@
1
+ interface Credentials {
2
+ token: string;
3
+ email: string;
4
+ tier: string;
5
+ expiresAt: string;
6
+ }
7
+ export type OAuthProvider = "github" | "google";
8
+ export declare function handleLogin(provider?: OAuthProvider): Promise<void>;
9
+ export declare function handleLoginWithToken(token: string): Promise<void>;
10
+ export declare function handleLogout(): Promise<void>;
11
+ export declare function handleStatus(): Promise<void>;
12
+ export declare function loadCredentials(): Credentials | null;
13
+ /**
14
+ * Get the tier from the JWT token payload. NEVER falls back to the
15
+ * editable credentials file — that would allow trivial tier bypass
16
+ * by editing ~/.archbyte/credentials.json.
17
+ *
18
+ * WARNING: This reads from an UNVERIFIED JWT payload. The CLI cannot
19
+ * verify HMAC signatures (no shared secret). Use getVerifiedTier()
20
+ * instead, which reads from the server-verified tier cache.
21
+ */
22
+ export declare function getTierFromToken(): "free" | "premium";
23
+ /**
24
+ * Cache the server-verified tier. Called by license-gate after a
25
+ * successful server response. Uses email as the identifier since
26
+ * the license-gate has access to it from credentials.
27
+ */
28
+ export declare function cacheVerifiedTier(tier: "free" | "premium", email: string): void;
29
+ /**
30
+ * Get the tier from the server-verified cache. Returns "free" if cache
31
+ * is missing, stale (> 1 hour), or belongs to a different user.
32
+ */
33
+ export declare function getVerifiedTier(): "free" | "premium";
34
+ /**
35
+ * Check if an offline action is allowed. Returns true if within limits.
36
+ * Increments the counter when allowed.
37
+ */
38
+ export declare function checkOfflineAction(): {
39
+ allowed: boolean;
40
+ reason?: string;
41
+ };
42
+ /**
43
+ * Reset offline action counter. Called after successful server verification.
44
+ */
45
+ export declare function resetOfflineActions(): void;
46
+ export {};