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,195 @@
1
+ // Static Analysis — Structure Scanner
2
+ // Detects project language, framework, monorepo, package manager, entry points
3
+ export async function scanStructure(tk) {
4
+ const result = {
5
+ projectName: "",
6
+ language: "unknown",
7
+ languages: [],
8
+ framework: null,
9
+ packageManager: null,
10
+ isMonorepo: false,
11
+ monorepoTool: null,
12
+ entryPoints: [],
13
+ buildSystem: null,
14
+ testFramework: null,
15
+ directories: {},
16
+ };
17
+ // Read config files in parallel
18
+ const [pkg, cargoToml, goMod, pyProject, tsconfig] = await Promise.all([
19
+ tk.readJSON("package.json"),
20
+ tk.readFileSafe("Cargo.toml"),
21
+ tk.readFileSafe("go.mod"),
22
+ tk.readFileSafe("pyproject.toml"),
23
+ tk.readJSON("tsconfig.json"),
24
+ ]);
25
+ // Project name
26
+ if (pkg?.name) {
27
+ result.projectName = pkg.name;
28
+ }
29
+ else if (goMod) {
30
+ const match = goMod.match(/^module\s+(.+)/m);
31
+ if (match)
32
+ result.projectName = match[1].split("/").pop() ?? match[1];
33
+ }
34
+ // Language detection
35
+ if (tsconfig) {
36
+ result.language = "TypeScript";
37
+ result.languages.push("TypeScript");
38
+ }
39
+ if (pkg && !tsconfig) {
40
+ result.languages.push("JavaScript");
41
+ if (result.language === "unknown")
42
+ result.language = "JavaScript";
43
+ }
44
+ if (cargoToml) {
45
+ result.languages.push("Rust");
46
+ if (result.language === "unknown")
47
+ result.language = "Rust";
48
+ }
49
+ if (goMod) {
50
+ result.languages.push("Go");
51
+ if (result.language === "unknown")
52
+ result.language = "Go";
53
+ }
54
+ if (pyProject) {
55
+ result.languages.push("Python");
56
+ if (result.language === "unknown")
57
+ result.language = "Python";
58
+ }
59
+ // Framework detection from deps
60
+ if (pkg) {
61
+ const allDeps = {
62
+ ...pkg.dependencies,
63
+ ...pkg.devDependencies,
64
+ };
65
+ const depNames = Object.keys(allDeps);
66
+ // JS/TS frameworks
67
+ if (depNames.includes("next"))
68
+ result.framework = "Next.js";
69
+ else if (depNames.includes("nuxt"))
70
+ result.framework = "Nuxt";
71
+ else if (depNames.includes("@nestjs/core"))
72
+ result.framework = "NestJS";
73
+ else if (depNames.includes("fastify"))
74
+ result.framework = "Fastify";
75
+ else if (depNames.includes("express"))
76
+ result.framework = "Express";
77
+ else if (depNames.includes("hono"))
78
+ result.framework = "Hono";
79
+ else if (depNames.includes("react"))
80
+ result.framework = "React";
81
+ else if (depNames.includes("vue"))
82
+ result.framework = "Vue";
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";
107
+ }
108
+ }
109
+ // Python frameworks
110
+ if (pyProject) {
111
+ if (pyProject.includes("django"))
112
+ result.framework = "Django";
113
+ else if (pyProject.includes("fastapi"))
114
+ result.framework = "FastAPI";
115
+ else if (pyProject.includes("flask"))
116
+ result.framework = "Flask";
117
+ }
118
+ // Rust frameworks
119
+ if (cargoToml) {
120
+ if (cargoToml.includes("axum"))
121
+ result.framework = "Axum";
122
+ else if (cargoToml.includes("actix"))
123
+ result.framework = "Actix";
124
+ else if (cargoToml.includes("rocket"))
125
+ result.framework = "Rocket";
126
+ result.buildSystem = "Cargo";
127
+ }
128
+ // Go frameworks
129
+ if (goMod) {
130
+ if (goMod.includes("gin-gonic"))
131
+ result.framework = "Gin";
132
+ else if (goMod.includes("labstack/echo"))
133
+ result.framework = "Echo";
134
+ else if (goMod.includes("go-fiber"))
135
+ result.framework = "Fiber";
136
+ result.buildSystem = "Go";
137
+ }
138
+ // Monorepo tools (check files in parallel)
139
+ const [nxJson, turboJson, lernaJson, pnpmWorkspace] = await Promise.all([
140
+ tk.readJSON("nx.json"),
141
+ tk.readJSON("turbo.json"),
142
+ tk.readJSON("lerna.json"),
143
+ tk.readFileSafe("pnpm-workspace.yaml"),
144
+ ]);
145
+ if (nxJson) {
146
+ result.isMonorepo = true;
147
+ result.monorepoTool = "nx";
148
+ }
149
+ if (turboJson) {
150
+ result.isMonorepo = true;
151
+ result.monorepoTool = "turborepo";
152
+ }
153
+ if (lernaJson) {
154
+ result.isMonorepo = true;
155
+ result.monorepoTool = "lerna";
156
+ }
157
+ if (pnpmWorkspace) {
158
+ result.isMonorepo = true;
159
+ result.monorepoTool = "pnpm";
160
+ }
161
+ // Package manager detection
162
+ const [hasPackageLock, hasYarnLock, hasPnpmLock, hasBunLock] = await Promise.all([
163
+ tk.readFileSafe("package-lock.json").then((c) => !!c),
164
+ tk.readFileSafe("yarn.lock").then((c) => !!c),
165
+ tk.readFileSafe("pnpm-lock.yaml").then((c) => !!c),
166
+ tk.readFileSafe("bun.lockb").then((c) => !!c),
167
+ ]);
168
+ if (hasPnpmLock)
169
+ result.packageManager = "pnpm";
170
+ else if (hasYarnLock)
171
+ result.packageManager = "yarn";
172
+ else if (hasBunLock)
173
+ result.packageManager = "bun";
174
+ else if (hasPackageLock)
175
+ result.packageManager = "npm";
176
+ // Entry points
177
+ if (pkg?.main)
178
+ result.entryPoints.push(pkg.main);
179
+ if (pkg?.bin) {
180
+ const bin = pkg.bin;
181
+ if (typeof bin === "string") {
182
+ result.entryPoints.push(bin);
183
+ }
184
+ else if (typeof bin === "object") {
185
+ result.entryPoints.push(...Object.values(bin));
186
+ }
187
+ }
188
+ // Directory structure
189
+ const rootEntries = await tk.listDir(".");
190
+ const dirs = rootEntries.filter((e) => e.type === "directory").map((e) => e.name);
191
+ for (const d of dirs) {
192
+ result.directories[d] = true;
193
+ }
194
+ return result;
195
+ }
@@ -0,0 +1,165 @@
1
+ export interface StructureResult {
2
+ projectName: string;
3
+ language: string;
4
+ languages: string[];
5
+ framework: string | null;
6
+ packageManager: string | null;
7
+ isMonorepo: boolean;
8
+ monorepoTool: string | null;
9
+ entryPoints: string[];
10
+ buildSystem: string | null;
11
+ testFramework: string | null;
12
+ directories: Record<string, boolean>;
13
+ }
14
+ export interface DocResult {
15
+ projectDescription: string;
16
+ architectureNotes: string[];
17
+ apiEndpoints: Array<{
18
+ method: string;
19
+ path: string;
20
+ description: string;
21
+ }>;
22
+ externalDependencies: string[];
23
+ }
24
+ export interface ComponentResult {
25
+ components: StaticComponent[];
26
+ }
27
+ export interface StaticComponent {
28
+ id: string;
29
+ name: string;
30
+ type: string;
31
+ layer: string;
32
+ path: string;
33
+ description: string;
34
+ technologies: string[];
35
+ }
36
+ export interface InfraResult {
37
+ docker: {
38
+ services: Array<{
39
+ name: string;
40
+ image?: string;
41
+ buildContext?: string;
42
+ ports?: string[];
43
+ dependsOn?: string[];
44
+ environment?: Record<string, string>;
45
+ }>;
46
+ composeFile: boolean;
47
+ composeFilePath?: string;
48
+ };
49
+ kubernetes: {
50
+ resources: Array<{
51
+ kind: string;
52
+ name: string;
53
+ namespace?: string;
54
+ }>;
55
+ };
56
+ cloud: {
57
+ provider: string | null;
58
+ services: string[];
59
+ iac: string | null;
60
+ };
61
+ ci: {
62
+ platform: string | null;
63
+ pipelines: string[];
64
+ };
65
+ }
66
+ export interface EventResult {
67
+ hasEDA: boolean;
68
+ patterns: Array<{
69
+ technology: string;
70
+ dependency: string;
71
+ }>;
72
+ events: Array<{
73
+ type: string;
74
+ file: string;
75
+ pattern: string;
76
+ }>;
77
+ }
78
+ export interface EnvResult {
79
+ environments: Array<{
80
+ name: string;
81
+ variables: string[];
82
+ }>;
83
+ configPattern: string | null;
84
+ hasSecrets: boolean;
85
+ }
86
+ export interface StaticConnection {
87
+ from: string;
88
+ to: string;
89
+ type: string;
90
+ description: string;
91
+ confidence: number;
92
+ async: boolean;
93
+ }
94
+ export interface ConnectionResult {
95
+ connections: StaticConnection[];
96
+ flows: Array<{
97
+ name: string;
98
+ description: string;
99
+ steps: string[];
100
+ }>;
101
+ }
102
+ export interface ValidationResult {
103
+ valid: boolean;
104
+ repairs: string[];
105
+ errors: string[];
106
+ }
107
+ /**
108
+ * A gap identified by static analysis that the LLM should try to resolve.
109
+ * Each gap is a generic question/uncertainty — not project-specific.
110
+ */
111
+ export interface AnalysisGap {
112
+ /** Category of the gap */
113
+ category: "unresolved_docker_service" | "isolated_component" | "weak_description" | "missing_database" | "unmodeled_external_service" | "unmapped_api_endpoints" | "event_pattern_no_connection" | "ambiguous_component_type" | "unknown_primary_language";
114
+ /** Human-readable description of the gap */
115
+ description: string;
116
+ /** IDs of related components (if any) */
117
+ relatedComponentIds?: string[];
118
+ /** Raw context data the LLM can use to resolve this gap */
119
+ context?: Record<string, unknown>;
120
+ }
121
+ export interface StaticAnalysisResult {
122
+ structure: StructureResult;
123
+ docs: DocResult;
124
+ components: ComponentResult;
125
+ infra: InfraResult;
126
+ events: EventResult;
127
+ envs: EnvResult;
128
+ connections: ConnectionResult;
129
+ validation: ValidationResult;
130
+ /** Gaps the static analysis couldn't resolve — passed to LLM for resolution */
131
+ gaps: AnalysisGap[];
132
+ }
133
+ export interface TreeEntry {
134
+ path: string;
135
+ type: "file" | "directory";
136
+ children?: TreeEntry[];
137
+ }
138
+ export interface FileTreeResult {
139
+ tree: TreeEntry[];
140
+ totalFiles: number;
141
+ totalDirs: number;
142
+ }
143
+ export interface FileSample {
144
+ path: string;
145
+ excerpt: string;
146
+ category: "entry-point" | "config" | "route-file" | "model-file";
147
+ }
148
+ export interface ConfigSample {
149
+ path: string;
150
+ content: string;
151
+ }
152
+ export interface CodeSampleResult {
153
+ samples: FileSample[];
154
+ importMap: Record<string, string[]>;
155
+ configFiles: ConfigSample[];
156
+ }
157
+ export interface StaticContext {
158
+ structure: StructureResult;
159
+ docs: DocResult;
160
+ infra: InfraResult;
161
+ events: EventResult;
162
+ envs: EnvResult;
163
+ fileTree: FileTreeResult;
164
+ codeSamples: CodeSampleResult;
165
+ }
@@ -0,0 +1,2 @@
1
+ // Static Analysis — Result Interfaces
2
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { GrepResult, DirEntry } from "../runtime/types.js";
2
+ /**
3
+ * Wraps LocalFSBackend with safe-read helpers for static scanners.
4
+ */
5
+ export declare class StaticToolkit {
6
+ private fs;
7
+ constructor(projectRoot: string);
8
+ readFileSafe(path: string): Promise<string | null>;
9
+ globFiles(pattern: string, cwd?: string): Promise<string[]>;
10
+ grepFiles(pattern: string, searchPath?: string): Promise<GrepResult[]>;
11
+ listDir(dirPath: string): Promise<DirEntry[]>;
12
+ readJSON(path: string): Promise<Record<string, unknown> | null>;
13
+ readYAML(path: string): Promise<unknown>;
14
+ readYAMLAll(path: string): Promise<unknown[]>;
15
+ }
16
+ export declare function slugify(name: string | undefined): string | undefined;
17
+ export declare function mapComponentType(type: string | undefined): string;
18
+ /**
19
+ * Assign a layer based on component type.
20
+ */
21
+ export declare function assignLayer(type: string): string;
@@ -0,0 +1,146 @@
1
+ // Static Analysis — Shared Utilities
2
+ import yaml from "js-yaml";
3
+ import { LocalFSBackend } from "../tools/local-fs.js";
4
+ /**
5
+ * Wraps LocalFSBackend with safe-read helpers for static scanners.
6
+ */
7
+ export class StaticToolkit {
8
+ fs;
9
+ constructor(projectRoot) {
10
+ this.fs = new LocalFSBackend(projectRoot);
11
+ }
12
+ async readFileSafe(path) {
13
+ try {
14
+ return await this.fs.readFile(path);
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ async globFiles(pattern, cwd) {
21
+ try {
22
+ // Expand brace patterns like *.{yml,yaml} → [*.yml, *.yaml]
23
+ const patterns = expandBraces(pattern);
24
+ if (patterns.length === 1) {
25
+ return await this.fs.glob(patterns[0], cwd);
26
+ }
27
+ const resultSets = await Promise.all(patterns.map((p) => this.fs.glob(p, cwd).catch(() => [])));
28
+ // Deduplicate and sort
29
+ return [...new Set(resultSets.flat())].sort();
30
+ }
31
+ catch {
32
+ return [];
33
+ }
34
+ }
35
+ async grepFiles(pattern, searchPath) {
36
+ try {
37
+ // Work around LocalFSBackend.grep glob bug with cwd:
38
+ // grep from root and filter by path prefix
39
+ const results = await this.fs.grep(pattern);
40
+ if (!searchPath)
41
+ return results;
42
+ const prefix = searchPath.endsWith("/") ? searchPath : `${searchPath}/`;
43
+ return results.filter((r) => r.file.startsWith(prefix));
44
+ }
45
+ catch {
46
+ return [];
47
+ }
48
+ }
49
+ async listDir(dirPath) {
50
+ try {
51
+ return await this.fs.listDir(dirPath);
52
+ }
53
+ catch {
54
+ return [];
55
+ }
56
+ }
57
+ async readJSON(path) {
58
+ const content = await this.readFileSafe(path);
59
+ if (!content)
60
+ return null;
61
+ try {
62
+ return JSON.parse(content);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ }
68
+ async readYAML(path) {
69
+ const content = await this.readFileSafe(path);
70
+ if (!content)
71
+ return null;
72
+ try {
73
+ return yaml.load(content);
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ async readYAMLAll(path) {
80
+ const content = await this.readFileSafe(path);
81
+ if (!content)
82
+ return [];
83
+ try {
84
+ return yaml.loadAll(content);
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ }
90
+ }
91
+ export function slugify(name) {
92
+ if (!name)
93
+ return undefined;
94
+ return name
95
+ .toLowerCase()
96
+ .replace(/[^a-z0-9]+/g, "-")
97
+ .replace(/^-|-$/g, "");
98
+ }
99
+ export function mapComponentType(type) {
100
+ if (!type)
101
+ return "service";
102
+ const t = type.toLowerCase();
103
+ if (t.includes("frontend") || t.includes("ui") || t.includes("web"))
104
+ return "frontend";
105
+ if (t.includes("api") || t.includes("gateway"))
106
+ return "api";
107
+ if (t.includes("database") || t.includes("db"))
108
+ return "database";
109
+ if (t.includes("worker") || t.includes("job") || t.includes("queue"))
110
+ return "worker";
111
+ if (t.includes("library") || t.includes("lib") || t.includes("package"))
112
+ return "library";
113
+ if (t.includes("cli") || t.includes("command"))
114
+ return "service";
115
+ if (t.includes("cache"))
116
+ return "cache";
117
+ return "service";
118
+ }
119
+ /**
120
+ * Assign a layer based on component type.
121
+ */
122
+ export function assignLayer(type) {
123
+ switch (type) {
124
+ case "frontend":
125
+ case "cli":
126
+ case "gateway":
127
+ return "presentation";
128
+ case "database":
129
+ return "data";
130
+ case "external":
131
+ return "external";
132
+ default:
133
+ return "application";
134
+ }
135
+ }
136
+ /**
137
+ * Expand simple brace patterns: "*.{a,b}" → ["*.a", "*.b"]
138
+ * Handles one level of braces only (sufficient for our glob patterns).
139
+ */
140
+ function expandBraces(pattern) {
141
+ const match = pattern.match(/^(.*)\{([^}]+)\}(.*)$/);
142
+ if (!match)
143
+ return [pattern];
144
+ const [, prefix, alternatives, suffix] = match;
145
+ return alternatives.split(",").map((alt) => `${prefix}${alt}${suffix}`);
146
+ }
@@ -0,0 +1,2 @@
1
+ import type { StaticAnalysisResult, ValidationResult } from "./types.js";
2
+ export declare function validateAnalysis(analysis: StaticAnalysisResult): ValidationResult;
@@ -0,0 +1,75 @@
1
+ // Static Analysis — Validator
2
+ // Referential integrity checks, dedup, auto-repair
3
+ export function validateAnalysis(analysis) {
4
+ const result = {
5
+ valid: true,
6
+ repairs: [],
7
+ errors: [],
8
+ };
9
+ const componentIds = new Set(analysis.components.components.map((c) => c.id));
10
+ // Check: at least 1 component
11
+ if (componentIds.size === 0) {
12
+ result.errors.push("No components detected");
13
+ result.valid = false;
14
+ }
15
+ // Check: no duplicate component IDs
16
+ const idCounts = new Map();
17
+ for (const c of analysis.components.components) {
18
+ idCounts.set(c.id, (idCounts.get(c.id) ?? 0) + 1);
19
+ }
20
+ for (const [id, count] of idCounts) {
21
+ if (count > 1) {
22
+ result.repairs.push(`Deduplicated component ID: ${id} (appeared ${count}x)`);
23
+ }
24
+ }
25
+ // Dedup components
26
+ const seenIds = new Set();
27
+ analysis.components.components = analysis.components.components.filter((c) => {
28
+ if (seenIds.has(c.id))
29
+ return false;
30
+ seenIds.add(c.id);
31
+ return true;
32
+ });
33
+ // Check: every connection from/to references existing component ID
34
+ const validConnections = [];
35
+ for (const conn of analysis.connections.connections) {
36
+ if (!componentIds.has(conn.from)) {
37
+ result.repairs.push(`Removed connection: unknown source "${conn.from}"`);
38
+ continue;
39
+ }
40
+ if (!componentIds.has(conn.to)) {
41
+ result.repairs.push(`Removed connection: unknown target "${conn.to}"`);
42
+ continue;
43
+ }
44
+ if (conn.from === conn.to) {
45
+ result.repairs.push(`Removed self-connection: ${conn.from}`);
46
+ continue;
47
+ }
48
+ validConnections.push(conn);
49
+ }
50
+ // Dedup connections
51
+ const connSeen = new Set();
52
+ analysis.connections.connections = validConnections.filter((c) => {
53
+ const key = `${c.from}::${c.to}::${c.type}`;
54
+ if (connSeen.has(key)) {
55
+ result.repairs.push(`Deduplicated connection: ${key}`);
56
+ return false;
57
+ }
58
+ connSeen.add(key);
59
+ return true;
60
+ });
61
+ // Assign stable slugified IDs to any components missing them
62
+ for (const comp of analysis.components.components) {
63
+ if (!comp.id) {
64
+ comp.id = comp.name
65
+ .toLowerCase()
66
+ .replace(/[^a-z0-9]+/g, "-")
67
+ .replace(/^-|-$/g, "");
68
+ result.repairs.push(`Assigned ID to component: ${comp.name} → ${comp.id}`);
69
+ }
70
+ }
71
+ if (result.repairs.length > 0 || result.errors.length > 0) {
72
+ result.valid = result.errors.length === 0;
73
+ }
74
+ return result;
75
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Claude Code Tool Backend
3
+ *
4
+ * When archbyte runs inside Claude Code (via /archbyte-analyze skill),
5
+ * this backend bridges to Claude Code's native tools (Glob, Grep, Read)
6
+ * instead of using the local filesystem directly.
7
+ *
8
+ * This gives the best analysis quality — Claude's tools are optimized
9
+ * for code understanding and handle large repos efficiently.
10
+ */
11
+ import type { ToolBackend, GrepResult, DirEntry } from "../runtime/types.js";
12
+ /**
13
+ * ClaudeCodeBackend — uses the same local FS operations as LocalFSBackend
14
+ * but is designed to be instantiated when running inside Claude Code.
15
+ *
16
+ * In practice, when archbyte is invoked via a Claude Code skill,
17
+ * Claude Code's Task agents run the archbyte agent pipeline.
18
+ * The agents use tool_use calls which are executed by the orchestrator
19
+ * against this backend — which reads the local filesystem that
20
+ * Claude Code also has access to.
21
+ *
22
+ * The key difference from LocalFSBackend:
23
+ * - Optimized for the files Claude Code has already discovered
24
+ * - Can accept a pre-scanned file list to avoid redundant discovery
25
+ * - Respects .gitignore and Claude Code's file filtering
26
+ */
27
+ export declare class ClaudeCodeBackend implements ToolBackend {
28
+ private root;
29
+ private preScannedFiles?;
30
+ constructor(projectRoot: string, preScannedFiles?: string[]);
31
+ readFile(filePath: string): Promise<string>;
32
+ glob(pattern: string): Promise<string[]>;
33
+ grep(pattern: string, searchPath?: string): Promise<GrepResult[]>;
34
+ listDir(dirPath: string): Promise<DirEntry[]>;
35
+ private resolvePath;
36
+ private walk;
37
+ private globToRegex;
38
+ }