archbyte 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/archbyte.js +7 -2
- package/dist/agents/pipeline/agents/service-describer.js +4 -4
- package/dist/agents/pipeline/merger.js +26 -34
- package/dist/agents/static/component-detector.js +71 -107
- package/dist/agents/static/connection-mapper.js +24 -25
- package/dist/agents/static/deep-drill.d.ts +72 -0
- package/dist/agents/static/deep-drill.js +388 -0
- package/dist/agents/static/doc-parser.js +73 -48
- package/dist/agents/static/env-detector.js +3 -6
- package/dist/agents/static/event-detector.js +20 -26
- package/dist/agents/static/infra-analyzer.js +15 -1
- package/dist/agents/static/structure-scanner.js +56 -57
- package/dist/agents/static/taxonomy.d.ts +19 -0
- package/dist/agents/static/taxonomy.js +147 -0
- package/dist/agents/tools/local-fs.js +5 -2
- package/dist/cli/analyze.js +15 -5
- package/dist/cli/run.js +1 -1
- package/dist/server/src/index.js +226 -72
- package/package.json +1 -1
- package/ui/dist/assets/index-BQouokNH.css +1 -0
- package/ui/dist/assets/index-CWGPRsWP.js +72 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-BXsZyipz.js +0 -72
- package/ui/dist/assets/index-ow1c3Nxp.css +0 -1
|
@@ -150,7 +150,7 @@ async function detectCI(tk, result) {
|
|
|
150
150
|
}
|
|
151
151
|
async function detectCloud(tk, result) {
|
|
152
152
|
// Check for IaC and cloud config in parallel
|
|
153
|
-
const [terraformFiles,
|
|
153
|
+
const [terraformFiles, wranglerTomlRoot, vercelJson, netlifyToml, awsCdk, samTemplate, serverlessYml,] = await Promise.all([
|
|
154
154
|
tk.globFiles("**/*.tf"),
|
|
155
155
|
tk.readFileSafe("wrangler.toml"),
|
|
156
156
|
tk.readJSON("vercel.json"),
|
|
@@ -159,6 +159,20 @@ async function detectCloud(tk, result) {
|
|
|
159
159
|
tk.readYAML("template.yaml"), // AWS SAM
|
|
160
160
|
tk.readYAML("serverless.yml"),
|
|
161
161
|
]);
|
|
162
|
+
// Also check subdirectories for wrangler.toml (e.g. cloud/, workers/, api/)
|
|
163
|
+
let wranglerToml = wranglerTomlRoot;
|
|
164
|
+
if (!wranglerToml) {
|
|
165
|
+
const rootEntries = await tk.listDir(".");
|
|
166
|
+
for (const entry of rootEntries) {
|
|
167
|
+
if (entry.type === "directory") {
|
|
168
|
+
const sub = await tk.readFileSafe(`${entry.name}/wrangler.toml`);
|
|
169
|
+
if (sub) {
|
|
170
|
+
wranglerToml = sub;
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
162
176
|
if (terraformFiles.length > 0) {
|
|
163
177
|
result.cloud.iac = "Terraform";
|
|
164
178
|
// Try to detect provider from tf files
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Static Analysis — Structure Scanner
|
|
2
2
|
// Detects project language, framework, monorepo, package manager, entry points
|
|
3
|
+
import { categorizeDep } from "./taxonomy.js";
|
|
3
4
|
export async function scanStructure(tk) {
|
|
4
5
|
const result = {
|
|
5
6
|
projectName: "",
|
|
@@ -56,84 +57,82 @@ export async function scanStructure(tk) {
|
|
|
56
57
|
if (result.language === "unknown")
|
|
57
58
|
result.language = "Python";
|
|
58
59
|
}
|
|
59
|
-
//
|
|
60
|
+
// Collect all dep names from root + common subdirectory package.json files
|
|
61
|
+
const allDepNames = [];
|
|
60
62
|
if (pkg) {
|
|
61
|
-
|
|
63
|
+
allDepNames.push(...Object.keys({
|
|
62
64
|
...pkg.dependencies,
|
|
63
65
|
...pkg.devDependencies,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else if (depNames.includes("svelte"))
|
|
84
|
-
result.framework = "Svelte";
|
|
85
|
-
else if (depNames.includes("angular"))
|
|
86
|
-
result.framework = "Angular";
|
|
87
|
-
// Test framework
|
|
88
|
-
if (depNames.includes("vitest"))
|
|
89
|
-
result.testFramework = "Vitest";
|
|
90
|
-
else if (depNames.includes("jest"))
|
|
91
|
-
result.testFramework = "Jest";
|
|
92
|
-
else if (depNames.includes("mocha"))
|
|
93
|
-
result.testFramework = "Mocha";
|
|
94
|
-
// Build system
|
|
95
|
-
if (depNames.includes("vite"))
|
|
96
|
-
result.buildSystem = "Vite";
|
|
97
|
-
else if (depNames.includes("webpack"))
|
|
98
|
-
result.buildSystem = "Webpack";
|
|
99
|
-
else if (depNames.includes("esbuild"))
|
|
100
|
-
result.buildSystem = "esbuild";
|
|
101
|
-
else if (depNames.includes("rollup"))
|
|
102
|
-
result.buildSystem = "Rollup";
|
|
103
|
-
// Monorepo detection
|
|
104
|
-
if (pkg.workspaces) {
|
|
105
|
-
result.isMonorepo = true;
|
|
106
|
-
result.monorepoTool = "npm-workspaces";
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
const UI_DIRS = ["ui", "client", "frontend", "web", "app", "packages"];
|
|
69
|
+
const subPkgs = await Promise.all(UI_DIRS.map((d) => tk.readJSON(`${d}/package.json`)));
|
|
70
|
+
for (const subPkg of subPkgs) {
|
|
71
|
+
if (!subPkg)
|
|
72
|
+
continue;
|
|
73
|
+
allDepNames.push(...Object.keys({
|
|
74
|
+
...subPkg.dependencies,
|
|
75
|
+
...subPkg.devDependencies,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
// Framework: first dep matching meta-framework, ui-framework, or http-framework
|
|
79
|
+
const frameworkCategories = new Set(["meta-framework", "ui-framework", "http-framework"]);
|
|
80
|
+
for (const dep of allDepNames) {
|
|
81
|
+
const cat = categorizeDep(dep);
|
|
82
|
+
if (cat && frameworkCategories.has(cat.category)) {
|
|
83
|
+
result.framework = cat.displayName;
|
|
84
|
+
break;
|
|
107
85
|
}
|
|
108
86
|
}
|
|
109
|
-
//
|
|
87
|
+
// Test framework: first dep matching test-framework
|
|
88
|
+
for (const dep of allDepNames) {
|
|
89
|
+
const cat = categorizeDep(dep);
|
|
90
|
+
if (cat?.category === "test-framework") {
|
|
91
|
+
result.testFramework = cat.displayName;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Build system: first dep matching bundler
|
|
96
|
+
for (const dep of allDepNames) {
|
|
97
|
+
const cat = categorizeDep(dep);
|
|
98
|
+
if (cat?.category === "bundler") {
|
|
99
|
+
result.buildSystem = cat.displayName;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Monorepo detection
|
|
104
|
+
if (pkg?.workspaces) {
|
|
105
|
+
result.isMonorepo = true;
|
|
106
|
+
result.monorepoTool = "npm-workspaces";
|
|
107
|
+
}
|
|
108
|
+
// Python frameworks (text-based — no package.json)
|
|
110
109
|
if (pyProject) {
|
|
111
110
|
if (pyProject.includes("django"))
|
|
112
|
-
result.framework = "Django";
|
|
111
|
+
result.framework = result.framework ?? "Django";
|
|
113
112
|
else if (pyProject.includes("fastapi"))
|
|
114
|
-
result.framework = "FastAPI";
|
|
113
|
+
result.framework = result.framework ?? "FastAPI";
|
|
115
114
|
else if (pyProject.includes("flask"))
|
|
116
|
-
result.framework = "Flask";
|
|
115
|
+
result.framework = result.framework ?? "Flask";
|
|
117
116
|
}
|
|
118
117
|
// Rust frameworks
|
|
119
118
|
if (cargoToml) {
|
|
120
119
|
if (cargoToml.includes("axum"))
|
|
121
|
-
result.framework = "Axum";
|
|
120
|
+
result.framework = result.framework ?? "Axum";
|
|
122
121
|
else if (cargoToml.includes("actix"))
|
|
123
|
-
result.framework = "Actix";
|
|
122
|
+
result.framework = result.framework ?? "Actix";
|
|
124
123
|
else if (cargoToml.includes("rocket"))
|
|
125
|
-
result.framework = "Rocket";
|
|
126
|
-
result.buildSystem = "Cargo";
|
|
124
|
+
result.framework = result.framework ?? "Rocket";
|
|
125
|
+
result.buildSystem = result.buildSystem ?? "Cargo";
|
|
127
126
|
}
|
|
128
127
|
// Go frameworks
|
|
129
128
|
if (goMod) {
|
|
130
129
|
if (goMod.includes("gin-gonic"))
|
|
131
|
-
result.framework = "Gin";
|
|
130
|
+
result.framework = result.framework ?? "Gin";
|
|
132
131
|
else if (goMod.includes("labstack/echo"))
|
|
133
|
-
result.framework = "Echo";
|
|
132
|
+
result.framework = result.framework ?? "Echo";
|
|
134
133
|
else if (goMod.includes("go-fiber"))
|
|
135
|
-
result.framework = "Fiber";
|
|
136
|
-
result.buildSystem = "Go";
|
|
134
|
+
result.framework = result.framework ?? "Fiber";
|
|
135
|
+
result.buildSystem = result.buildSystem ?? "Go";
|
|
137
136
|
}
|
|
138
137
|
// Monorepo tools (check files in parallel)
|
|
139
138
|
const [nxJson, turboJson, lernaJson, pnpmWorkspace] = await Promise.all([
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface PackageCategory {
|
|
2
|
+
pattern: RegExp;
|
|
3
|
+
category: string;
|
|
4
|
+
role: string;
|
|
5
|
+
displayName?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const PACKAGE_TAXONOMY: PackageCategory[];
|
|
8
|
+
/** Role -> component type mapping (replaces DIR_TYPE_HINTS and detectTypeFromDeps) */
|
|
9
|
+
export declare const ROLE_TO_TYPE: Record<string, string>;
|
|
10
|
+
/** Manifest files that indicate a directory is an independent module */
|
|
11
|
+
export declare const MANIFEST_FILES: string[];
|
|
12
|
+
/** Categorize a single dependency name */
|
|
13
|
+
export declare function categorizeDep(name: string): (PackageCategory & {
|
|
14
|
+
displayName: string;
|
|
15
|
+
}) | null;
|
|
16
|
+
/** Categorize all deps from a manifest — returns only recognized deps */
|
|
17
|
+
export declare function categorizeAllDeps(depNames: string[]): Map<string, PackageCategory & {
|
|
18
|
+
displayName: string;
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Static Analysis — Package Taxonomy
|
|
2
|
+
// Single source of truth for mapping package names to architectural categories.
|
|
3
|
+
// All scanners import from here instead of maintaining their own hardcoded lists.
|
|
4
|
+
export const PACKAGE_TAXONOMY = [
|
|
5
|
+
// Database clients
|
|
6
|
+
{ pattern: /^(pg|mysql2?|better-sqlite3|@libsql\/client|mongodb|mongoose)$/i, category: "database-client", role: "data", displayName: "PostgreSQL" },
|
|
7
|
+
{ pattern: /^(@prisma\/client|prisma|drizzle-orm|typeorm|sequelize|knex|mikro-orm)$/i, category: "orm", role: "data" },
|
|
8
|
+
// Cache
|
|
9
|
+
{ pattern: /^(redis|ioredis|@upstash\/redis|memcached|keyv)$/i, category: "cache-client", role: "data", displayName: "Redis" },
|
|
10
|
+
// Message queues
|
|
11
|
+
{ pattern: /^(kafkajs|amqplib|bullmq|bull|bee-queue|@aws-sdk\/client-sqs|@aws-sdk\/client-sns|@aws-sdk\/client-eventbridge|nats|mqtt|@google-cloud\/pubsub|@azure\/service-bus|@azure\/event-hubs|@confluentinc\/kafka-javascript)$/i, category: "queue-client", role: "messaging" },
|
|
12
|
+
// HTTP frameworks
|
|
13
|
+
{ pattern: /^(express|fastify|@nestjs\/core|hono|koa|@hapi\/hapi)$/i, category: "http-framework", role: "api" },
|
|
14
|
+
// Frontend frameworks
|
|
15
|
+
{ pattern: /^(react|react-dom|vue|svelte|@angular\/core|solid-js|preact)$/i, category: "ui-framework", role: "frontend" },
|
|
16
|
+
// Meta-frameworks
|
|
17
|
+
{ pattern: /^(next|nuxt|@remix-run\/node|gatsby|astro)$/i, category: "meta-framework", role: "frontend" },
|
|
18
|
+
// Cloud SDKs
|
|
19
|
+
{ pattern: /^@aws-sdk\//i, category: "cloud-sdk", role: "external", displayName: "AWS" },
|
|
20
|
+
{ pattern: /^@google-cloud\//i, category: "cloud-sdk", role: "external", displayName: "GCP" },
|
|
21
|
+
{ pattern: /^@azure\//i, category: "cloud-sdk", role: "external", displayName: "Azure" },
|
|
22
|
+
// AI/LLM SDKs
|
|
23
|
+
{ pattern: /^(@anthropic-ai\/sdk|openai|@google\/genai|@langchain\/.*)$/i, category: "ai-sdk", role: "external" },
|
|
24
|
+
// Payment
|
|
25
|
+
{ pattern: /^(stripe|@stripe)/i, category: "payment-sdk", role: "external", displayName: "Stripe" },
|
|
26
|
+
// Real-time
|
|
27
|
+
{ pattern: /^(socket\.io|ws|@socket\.io)/i, category: "realtime", role: "messaging" },
|
|
28
|
+
// SSE libraries
|
|
29
|
+
{ pattern: /^(sse-channel|better-sse)$/i, category: "sse", role: "messaging", displayName: "Server-Sent Events" },
|
|
30
|
+
// CLI frameworks
|
|
31
|
+
{ pattern: /^(commander|yargs|@oclif\/core|clipanion|cac)$/i, category: "cli-framework", role: "cli" },
|
|
32
|
+
// Build/bundlers
|
|
33
|
+
{ pattern: /^(vite|webpack|esbuild|rollup|parcel|tsup|turbopack)$/i, category: "bundler", role: "build" },
|
|
34
|
+
// Test frameworks
|
|
35
|
+
{ pattern: /^(vitest|jest|mocha|@playwright\/test|cypress)$/i, category: "test-framework", role: "test" },
|
|
36
|
+
// Secrets management
|
|
37
|
+
{ pattern: /^(dotenv|@aws-sdk\/client-secrets-manager|node-vault|vault|@google-cloud\/secret-manager|@azure\/keyvault-secrets|infisical-sdk)$/i, category: "secrets", role: "infra" },
|
|
38
|
+
// GraphQL
|
|
39
|
+
{ pattern: /^(graphql|@apollo\/server|@apollo\/client)$/i, category: "graphql", role: "api", displayName: "GraphQL" },
|
|
40
|
+
// CSS frameworks (architectural signal for UI)
|
|
41
|
+
{ pattern: /^(tailwindcss|@xyflow\/react)$/i, category: "ui-library", role: "frontend" },
|
|
42
|
+
];
|
|
43
|
+
// Display name overrides for specific packages (when pattern-level displayName is too broad)
|
|
44
|
+
const DISPLAY_NAME_OVERRIDES = {
|
|
45
|
+
"pg": "PostgreSQL",
|
|
46
|
+
"mysql2": "MySQL",
|
|
47
|
+
"mysql": "MySQL",
|
|
48
|
+
"mongodb": "MongoDB",
|
|
49
|
+
"mongoose": "MongoDB",
|
|
50
|
+
"redis": "Redis",
|
|
51
|
+
"ioredis": "Redis",
|
|
52
|
+
"@upstash/redis": "Redis",
|
|
53
|
+
"kafkajs": "Kafka",
|
|
54
|
+
"@confluentinc/kafka-javascript": "Kafka",
|
|
55
|
+
"amqplib": "RabbitMQ",
|
|
56
|
+
"bullmq": "BullMQ",
|
|
57
|
+
"bull": "Bull",
|
|
58
|
+
"bee-queue": "Bee-Queue",
|
|
59
|
+
"nats": "NATS",
|
|
60
|
+
"mqtt": "MQTT",
|
|
61
|
+
"@google-cloud/pubsub": "Google Pub/Sub",
|
|
62
|
+
"@aws-sdk/client-sqs": "AWS SQS",
|
|
63
|
+
"@aws-sdk/client-sns": "AWS SNS",
|
|
64
|
+
"@aws-sdk/client-eventbridge": "AWS EventBridge",
|
|
65
|
+
"@azure/service-bus": "Azure Service Bus",
|
|
66
|
+
"@azure/event-hubs": "Azure Event Hubs",
|
|
67
|
+
"socket.io": "Socket.IO",
|
|
68
|
+
"ws": "WebSocket",
|
|
69
|
+
"next": "Next.js",
|
|
70
|
+
"nuxt": "Nuxt",
|
|
71
|
+
"react": "React",
|
|
72
|
+
"react-dom": "React",
|
|
73
|
+
"vue": "Vue",
|
|
74
|
+
"svelte": "Svelte",
|
|
75
|
+
"@angular/core": "Angular",
|
|
76
|
+
"solid-js": "Solid",
|
|
77
|
+
"preact": "Preact",
|
|
78
|
+
"express": "Express",
|
|
79
|
+
"fastify": "Fastify",
|
|
80
|
+
"@nestjs/core": "NestJS",
|
|
81
|
+
"hono": "Hono",
|
|
82
|
+
"koa": "Koa",
|
|
83
|
+
"@prisma/client": "Prisma",
|
|
84
|
+
"prisma": "Prisma",
|
|
85
|
+
"drizzle-orm": "Drizzle",
|
|
86
|
+
"typeorm": "TypeORM",
|
|
87
|
+
"sequelize": "Sequelize",
|
|
88
|
+
"knex": "Knex",
|
|
89
|
+
"openai": "OpenAI",
|
|
90
|
+
"@anthropic-ai/sdk": "Anthropic",
|
|
91
|
+
"@google/genai": "Google AI",
|
|
92
|
+
"vite": "Vite",
|
|
93
|
+
"webpack": "Webpack",
|
|
94
|
+
"esbuild": "esbuild",
|
|
95
|
+
"rollup": "Rollup",
|
|
96
|
+
"vitest": "Vitest",
|
|
97
|
+
"jest": "Jest",
|
|
98
|
+
"mocha": "Mocha",
|
|
99
|
+
"@playwright/test": "Playwright",
|
|
100
|
+
"cypress": "Cypress",
|
|
101
|
+
"commander": "Commander.js",
|
|
102
|
+
"yargs": "Yargs",
|
|
103
|
+
"tailwindcss": "Tailwind CSS",
|
|
104
|
+
"@xyflow/react": "React Flow",
|
|
105
|
+
"graphql": "GraphQL",
|
|
106
|
+
"@apollo/server": "Apollo GraphQL",
|
|
107
|
+
"typescript": "TypeScript",
|
|
108
|
+
};
|
|
109
|
+
/** Role -> component type mapping (replaces DIR_TYPE_HINTS and detectTypeFromDeps) */
|
|
110
|
+
export const ROLE_TO_TYPE = {
|
|
111
|
+
frontend: "frontend",
|
|
112
|
+
api: "api",
|
|
113
|
+
data: "database",
|
|
114
|
+
messaging: "worker",
|
|
115
|
+
external: "service",
|
|
116
|
+
cli: "service",
|
|
117
|
+
build: "service",
|
|
118
|
+
test: "service",
|
|
119
|
+
infra: "service",
|
|
120
|
+
};
|
|
121
|
+
/** Manifest files that indicate a directory is an independent module */
|
|
122
|
+
export const MANIFEST_FILES = [
|
|
123
|
+
"package.json", "Cargo.toml", "go.mod", "pyproject.toml",
|
|
124
|
+
"requirements.txt", "setup.py", "pubspec.yaml", "build.gradle",
|
|
125
|
+
"pom.xml", "Gemfile", "Makefile", "Dockerfile", "wrangler.toml",
|
|
126
|
+
"tsconfig.json",
|
|
127
|
+
];
|
|
128
|
+
/** Categorize a single dependency name */
|
|
129
|
+
export function categorizeDep(name) {
|
|
130
|
+
const match = PACKAGE_TAXONOMY.find(t => t.pattern.test(name));
|
|
131
|
+
if (!match)
|
|
132
|
+
return null;
|
|
133
|
+
return {
|
|
134
|
+
...match,
|
|
135
|
+
displayName: DISPLAY_NAME_OVERRIDES[name] ?? match.displayName ?? name,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Categorize all deps from a manifest — returns only recognized deps */
|
|
139
|
+
export function categorizeAllDeps(depNames) {
|
|
140
|
+
const result = new Map();
|
|
141
|
+
for (const name of depNames) {
|
|
142
|
+
const cat = categorizeDep(name);
|
|
143
|
+
if (cat)
|
|
144
|
+
result.set(name, cat);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
2
|
import { resolve, relative, join } from "node:path";
|
|
3
3
|
import { EXCLUDED_DIRS } from "../static/excluded-dirs.js";
|
|
4
|
+
/** Dot-prefixed directories that should NOT be skipped during walks (CI/CD configs) */
|
|
5
|
+
const ALLOWED_DOT_DIRS = new Set([".github", ".circleci", ".gitlab"]);
|
|
4
6
|
export class LocalFSBackend {
|
|
5
7
|
root;
|
|
6
8
|
constructor(projectRoot) {
|
|
@@ -83,9 +85,10 @@ export class LocalFSBackend {
|
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
for (const entry of entries) {
|
|
86
|
-
if (
|
|
88
|
+
if (EXCLUDED_DIRS.has(entry.name))
|
|
89
|
+
continue;
|
|
90
|
+
if (entry.name.startsWith(".") && !ALLOWED_DOT_DIRS.has(entry.name))
|
|
87
91
|
continue;
|
|
88
|
-
}
|
|
89
92
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
90
93
|
if (entry.isDirectory()) {
|
|
91
94
|
await this.walk(base, relPath, regex, matches);
|
package/dist/cli/analyze.js
CHANGED
|
@@ -50,10 +50,11 @@ export async function handleAnalyze(options) {
|
|
|
50
50
|
progress.update(1, "Building analysis...");
|
|
51
51
|
const freshAnalysis = buildAnalysisFromStatic(result, rootDir);
|
|
52
52
|
const duration = Date.now() - startTime;
|
|
53
|
-
// Merge into existing
|
|
53
|
+
// Merge into existing data if it was produced by an agentic run,
|
|
54
54
|
// preserving LLM-generated components/connections while refreshing
|
|
55
55
|
// static data (environments, metadata, project info).
|
|
56
56
|
const existingAnalysis = loadExistingAnalysis(rootDir);
|
|
57
|
+
const existingSpec = loadSpec(rootDir);
|
|
57
58
|
const wasAgentic = existingAnalysis && existingAnalysis.metadata?.mode !== "static";
|
|
58
59
|
const analysis = wasAgentic ? mergeStaticIntoExisting(existingAnalysis, freshAnalysis) : freshAnalysis;
|
|
59
60
|
// Stamp scan metadata on analysis.json (backward compat)
|
|
@@ -62,10 +63,19 @@ export async function handleAnalyze(options) {
|
|
|
62
63
|
ameta.mode = wasAgentic ? "static-refresh" : "static";
|
|
63
64
|
writeAnalysis(rootDir, analysis);
|
|
64
65
|
// Dual-write: archbyte.yaml + metadata.json
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
// When prior data came from an agentic run, only refresh static fields
|
|
67
|
+
// (project info, environments) — never overwrite LLM components/connections.
|
|
68
|
+
if (wasAgentic && existingSpec) {
|
|
69
|
+
const freshSpec = staticResultToSpec(result, rootDir, existingSpec.rules);
|
|
70
|
+
existingSpec.project = freshSpec.project;
|
|
71
|
+
existingSpec.environments = freshSpec.environments;
|
|
72
|
+
writeSpec(rootDir, existingSpec);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const spec = staticResultToSpec(result, rootDir, existingSpec?.rules);
|
|
76
|
+
writeSpec(rootDir, spec);
|
|
77
|
+
}
|
|
78
|
+
writeScanMetadata(rootDir, duration, wasAgentic ? "static-refresh" : "static");
|
|
69
79
|
progress.update(2, "Generating diagram...");
|
|
70
80
|
await autoGenerate(rootDir, options);
|
|
71
81
|
progress.done("Analysis complete");
|
package/dist/cli/run.js
CHANGED
|
@@ -42,7 +42,7 @@ export async function handleRun(options) {
|
|
|
42
42
|
console.log();
|
|
43
43
|
console.log(sep);
|
|
44
44
|
console.log();
|
|
45
|
-
console.log(dim(" Docs ") + chalk.cyan("https://archbyte.heartbyte.io/setup
|
|
45
|
+
console.log(dim(" Docs ") + chalk.cyan("https://archbyte.heartbyte.io/setup"));
|
|
46
46
|
console.log(dim(" Website ") + chalk.cyan("https://archbyte.heartbyte.io"));
|
|
47
47
|
console.log();
|
|
48
48
|
console.log(sep);
|