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,193 @@
1
+ import { resolveModel } from "./types.js";
2
+ import { AGENT_TOOLS } from "../tools/tool-definitions.js";
3
+ /**
4
+ * Agent Orchestrator — runs the multi-agent analysis pipeline.
5
+ *
6
+ * Phase 1 (parallel): structure-scanner, doc-parser, infra-analyzer,
7
+ * component-detector, event-detector, env-detector
8
+ * Phase 2 (sequential): connection-mapper (needs Phase 1 results)
9
+ * Phase 3 (sequential): validator (needs Phase 2 results)
10
+ */
11
+ export class Orchestrator {
12
+ agents = [];
13
+ provider;
14
+ tools;
15
+ projectRoot;
16
+ config;
17
+ license;
18
+ constructor(opts) {
19
+ this.provider = opts.provider;
20
+ this.tools = opts.tools;
21
+ this.projectRoot = opts.projectRoot;
22
+ this.config = opts.config;
23
+ this.license = opts.license;
24
+ }
25
+ register(agent) {
26
+ this.agents.push(agent);
27
+ }
28
+ registerAll(agents) {
29
+ for (const agent of agents) {
30
+ this.register(agent);
31
+ }
32
+ }
33
+ async run(onProgress) {
34
+ const start = Date.now();
35
+ const results = [];
36
+ const errors = [];
37
+ // Filter agents by license tier
38
+ const allowed = this.agents.filter((a) => this.isAllowed(a));
39
+ const blocked = this.agents.filter((a) => !this.isAllowed(a));
40
+ if (blocked.length > 0) {
41
+ onProgress?.(`Skipping ${blocked.length} premium agent(s) — upgrade to Pro to unlock`);
42
+ }
43
+ // Split into phases
44
+ const parallel = allowed.filter((a) => a.phase === "parallel");
45
+ const sequential = allowed.filter((a) => a.phase === "sequential");
46
+ // Phase 1: Run parallel agents
47
+ onProgress?.(`Running ${parallel.length} agents in parallel...`);
48
+ const parallelResults = await Promise.allSettled(parallel.map((agent) => this.runAgent(agent, {}, onProgress)));
49
+ const resultMap = {};
50
+ for (let i = 0; i < parallel.length; i++) {
51
+ const result = parallelResults[i];
52
+ if (result.status === "fulfilled") {
53
+ results.push(result.value);
54
+ resultMap[parallel[i].id] = result.value;
55
+ }
56
+ else {
57
+ const errorMsg = result.reason instanceof Error
58
+ ? result.reason.message
59
+ : String(result.reason);
60
+ errors.push({ agentId: parallel[i].id, error: errorMsg });
61
+ onProgress?.(`Agent ${parallel[i].name} failed: ${errorMsg}`);
62
+ }
63
+ }
64
+ // Phase 2+3: Run sequential agents with prior results
65
+ for (const agent of sequential) {
66
+ onProgress?.(`Running ${agent.name}...`);
67
+ try {
68
+ const result = await this.runAgent(agent, resultMap, onProgress);
69
+ results.push(result);
70
+ resultMap[agent.id] = result;
71
+ }
72
+ catch (err) {
73
+ const errorMsg = err instanceof Error ? err.message : String(err);
74
+ errors.push({ agentId: agent.id, error: errorMsg });
75
+ onProgress?.(`Agent ${agent.name} failed: ${errorMsg}`);
76
+ }
77
+ }
78
+ return {
79
+ agents: results,
80
+ totalDuration: Date.now() - start,
81
+ success: errors.length === 0,
82
+ errors,
83
+ };
84
+ }
85
+ isAllowed(agent) {
86
+ if (agent.tier === "free")
87
+ return true;
88
+ if (agent.tier === "premium" && this.license.tier === "premium")
89
+ return true;
90
+ return false;
91
+ }
92
+ async runAgent(agent, priorResults, onProgress) {
93
+ const model = resolveModel(this.config.provider, agent.modelTier, this.config.modelOverrides);
94
+ // Create a model-routed provider wrapper
95
+ const routedProvider = {
96
+ name: this.provider.name,
97
+ chat: (params) => this.provider.chat({ ...params, model: params.model ?? model }),
98
+ stream: (params) => this.provider.stream({ ...params, model: params.model ?? model }),
99
+ };
100
+ const context = {
101
+ llm: routedProvider,
102
+ tools: this.tools,
103
+ projectRoot: this.projectRoot,
104
+ priorResults: Object.keys(priorResults).length > 0 ? priorResults : undefined,
105
+ onProgress,
106
+ };
107
+ onProgress?.(`Starting ${agent.name}...`);
108
+ const start = Date.now();
109
+ const result = await agent.run(context);
110
+ result.duration = Date.now() - start;
111
+ onProgress?.(`${agent.name} completed in ${result.duration}ms`);
112
+ return result;
113
+ }
114
+ }
115
+ /**
116
+ * Execute a tool_use loop for an agent.
117
+ * This is the core pattern: send prompt → get tool calls → execute tools → send results → repeat.
118
+ */
119
+ export async function executeToolLoop(context, systemPrompt, userMessage, maxIterations = 20) {
120
+ const messages = [{ role: "user", content: userMessage }];
121
+ for (let i = 0; i < maxIterations; i++) {
122
+ const response = await context.llm.chat({
123
+ system: systemPrompt,
124
+ messages,
125
+ tools: AGENT_TOOLS,
126
+ maxTokens: 8192,
127
+ });
128
+ // Collect text output
129
+ const textParts = response.content
130
+ .filter((b) => b.type === "text")
131
+ .map((b) => b.text ?? "");
132
+ // Check for tool calls
133
+ const toolCalls = response.content.filter((b) => b.type === "tool_use");
134
+ if (toolCalls.length === 0 || response.stopReason !== "tool_use") {
135
+ // No more tool calls — return final text
136
+ return textParts.join("\n");
137
+ }
138
+ // Add assistant response to messages
139
+ messages.push({ role: "assistant", content: response.content });
140
+ // Execute each tool call and add results
141
+ const toolResults = [];
142
+ for (const tc of toolCalls) {
143
+ const result = await executeTool(context.tools, tc.name, tc.input);
144
+ context.onProgress?.(`Tool ${tc.name}(${summarizeInput(tc.input)})`);
145
+ toolResults.push({
146
+ type: "tool_result",
147
+ tool_use_id: tc.id,
148
+ content: result,
149
+ });
150
+ }
151
+ messages.push({ role: "user", content: toolResults });
152
+ }
153
+ throw new Error("Agent exceeded maximum tool iterations");
154
+ }
155
+ async function executeTool(tools, name, input) {
156
+ try {
157
+ switch (name) {
158
+ case "read_file": {
159
+ const content = await tools.readFile(input.path);
160
+ // Truncate very large files
161
+ if (content.length > 50000) {
162
+ return content.slice(0, 50000) + "\n\n[... truncated, file too large]";
163
+ }
164
+ return content;
165
+ }
166
+ case "glob": {
167
+ const files = await tools.glob(input.pattern);
168
+ return files.join("\n") || "No files found";
169
+ }
170
+ case "grep": {
171
+ const results = await tools.grep(input.pattern, input.path);
172
+ return (results.map((r) => `${r.file}:${r.line}: ${r.content}`).join("\n") ||
173
+ "No matches found");
174
+ }
175
+ case "list_dir": {
176
+ const entries = await tools.listDir(input.path);
177
+ return entries
178
+ .map((e) => `${e.type === "directory" ? "d" : "f"} ${e.name}${e.size ? ` (${e.size}B)` : ""}`)
179
+ .join("\n") || "Empty directory";
180
+ }
181
+ default:
182
+ return `Unknown tool: ${name}`;
183
+ }
184
+ }
185
+ catch (err) {
186
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
187
+ }
188
+ }
189
+ function summarizeInput(input) {
190
+ const val = input.path ?? input.pattern ?? "";
191
+ const s = String(val);
192
+ return s.length > 40 ? s.slice(0, 40) + "..." : s;
193
+ }
@@ -0,0 +1,23 @@
1
+ import type { ArchByteAgent, AgentTier } from "./types.js";
2
+ /**
3
+ * Agent Registry — discovers and manages available agents.
4
+ * Handles tier gating (free vs pro vs enterprise).
5
+ */
6
+ export declare class AgentRegistry {
7
+ private agents;
8
+ register(agent: ArchByteAgent): void;
9
+ get(id: string): ArchByteAgent | undefined;
10
+ getAll(): ArchByteAgent[];
11
+ getByTier(tier: AgentTier): ArchByteAgent[];
12
+ /**
13
+ * Get agents that the given license tier can access.
14
+ * free → only free agents
15
+ * premium → all agents (free + premium)
16
+ */
17
+ getAccessible(licenseTier: AgentTier): ArchByteAgent[];
18
+ getCoreAgents(): ArchByteAgent[];
19
+ getPremiumAgents(): ArchByteAgent[];
20
+ getParallelAgents(licenseTier: AgentTier): ArchByteAgent[];
21
+ getSequentialAgents(licenseTier: AgentTier): ArchByteAgent[];
22
+ }
23
+ export declare function getRegistry(): AgentRegistry;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Agent Registry — discovers and manages available agents.
3
+ * Handles tier gating (free vs pro vs enterprise).
4
+ */
5
+ export class AgentRegistry {
6
+ agents = new Map();
7
+ register(agent) {
8
+ if (this.agents.has(agent.id)) {
9
+ throw new Error(`Agent already registered: ${agent.id}`);
10
+ }
11
+ this.agents.set(agent.id, agent);
12
+ }
13
+ get(id) {
14
+ return this.agents.get(id);
15
+ }
16
+ getAll() {
17
+ return Array.from(this.agents.values());
18
+ }
19
+ getByTier(tier) {
20
+ return this.getAll().filter((a) => a.tier === tier);
21
+ }
22
+ /**
23
+ * Get agents that the given license tier can access.
24
+ * free → only free agents
25
+ * premium → all agents (free + premium)
26
+ */
27
+ getAccessible(licenseTier) {
28
+ return this.getAll().filter((a) => {
29
+ if (a.tier === "free")
30
+ return true;
31
+ if (a.tier === "premium" && licenseTier === "premium")
32
+ return true;
33
+ return false;
34
+ });
35
+ }
36
+ getCoreAgents() {
37
+ return this.getByTier("free");
38
+ }
39
+ getPremiumAgents() {
40
+ return this.getByTier("premium");
41
+ }
42
+ getParallelAgents(licenseTier) {
43
+ return this.getAccessible(licenseTier).filter((a) => a.phase === "parallel");
44
+ }
45
+ getSequentialAgents(licenseTier) {
46
+ return this.getAccessible(licenseTier).filter((a) => a.phase === "sequential");
47
+ }
48
+ }
49
+ // Singleton registry
50
+ let globalRegistry = null;
51
+ export function getRegistry() {
52
+ if (!globalRegistry) {
53
+ globalRegistry = new AgentRegistry();
54
+ }
55
+ return globalRegistry;
56
+ }
@@ -0,0 +1,117 @@
1
+ export interface Message {
2
+ role: "user" | "assistant";
3
+ content: string | ContentBlock[];
4
+ }
5
+ export interface ContentBlock {
6
+ type: "text" | "tool_use" | "tool_result";
7
+ text?: string;
8
+ id?: string;
9
+ name?: string;
10
+ input?: Record<string, unknown>;
11
+ tool_use_id?: string;
12
+ content?: string;
13
+ is_error?: boolean;
14
+ }
15
+ export interface ToolDefinition {
16
+ name: string;
17
+ description: string;
18
+ input_schema: Record<string, unknown>;
19
+ }
20
+ export interface ChatParams {
21
+ model?: string;
22
+ system: string;
23
+ messages: Message[];
24
+ tools?: ToolDefinition[];
25
+ maxTokens?: number;
26
+ }
27
+ export interface LLMResponse {
28
+ content: ContentBlock[];
29
+ stopReason: "end_turn" | "tool_use" | "max_tokens" | "stop_sequence";
30
+ usage: {
31
+ inputTokens: number;
32
+ outputTokens: number;
33
+ };
34
+ }
35
+ export interface LLMChunk {
36
+ type: "text" | "tool_use_start" | "tool_use_input" | "done";
37
+ text?: string;
38
+ toolName?: string;
39
+ toolId?: string;
40
+ input?: string;
41
+ }
42
+ export type ModelTier = "fast" | "standard" | "advanced";
43
+ export interface LLMProvider {
44
+ name: string;
45
+ chat(params: ChatParams): Promise<LLMResponse>;
46
+ stream(params: ChatParams): AsyncIterable<LLMChunk>;
47
+ }
48
+ export interface GrepResult {
49
+ file: string;
50
+ line: number;
51
+ content: string;
52
+ }
53
+ export interface DirEntry {
54
+ name: string;
55
+ type: "file" | "directory";
56
+ size?: number;
57
+ }
58
+ export interface ToolBackend {
59
+ readFile(path: string): Promise<string>;
60
+ glob(pattern: string, cwd?: string): Promise<string[]>;
61
+ grep(pattern: string, path?: string): Promise<GrepResult[]>;
62
+ listDir(path: string): Promise<DirEntry[]>;
63
+ }
64
+ export type AgentTier = "free" | "premium";
65
+ export type AgentPhase = "parallel" | "sequential";
66
+ export type ToolName = "read_file" | "glob" | "grep" | "list_dir";
67
+ export interface AgentContext {
68
+ llm: LLMProvider;
69
+ tools: ToolBackend;
70
+ projectRoot: string;
71
+ priorResults?: Record<string, AgentResult>;
72
+ onProgress?: (msg: string) => void;
73
+ }
74
+ export interface AgentResult {
75
+ agentId: string;
76
+ data: unknown;
77
+ confidence: number;
78
+ duration: number;
79
+ }
80
+ export interface ArchByteAgent {
81
+ id: string;
82
+ name: string;
83
+ description: string;
84
+ tier: AgentTier;
85
+ phase: AgentPhase;
86
+ modelTier: ModelTier;
87
+ systemPrompt: string;
88
+ requiredTools: ToolName[];
89
+ run(context: AgentContext): Promise<AgentResult>;
90
+ }
91
+ export interface License {
92
+ tier: AgentTier | "none";
93
+ userId: string;
94
+ email: string;
95
+ scansUsed: number;
96
+ scansAllowed: number;
97
+ expiresAt: string;
98
+ isValid: boolean;
99
+ }
100
+ export type ProviderName = "anthropic" | "openai" | "google" | "ollama";
101
+ export interface ArchByteConfig {
102
+ provider: ProviderName;
103
+ apiKey: string;
104
+ modelOverrides?: Partial<Record<ModelTier, string>>;
105
+ ollamaBaseUrl?: string;
106
+ }
107
+ export interface PipelineResult {
108
+ agents: AgentResult[];
109
+ totalDuration: number;
110
+ success: boolean;
111
+ errors: Array<{
112
+ agentId: string;
113
+ error: string;
114
+ }>;
115
+ }
116
+ export declare const MODEL_MAP: Record<ProviderName, Record<ModelTier, string>>;
117
+ export declare function resolveModel(provider: ProviderName, tier: ModelTier, overrides?: Partial<Record<ModelTier, string>>): string;
@@ -0,0 +1,29 @@
1
+ // ArchByte Agent Framework — Core Types
2
+ // === Model Routing ===
3
+ export const MODEL_MAP = {
4
+ anthropic: {
5
+ fast: "claude-haiku-4-5-20251001",
6
+ standard: "claude-sonnet-4-5-20250929",
7
+ advanced: "claude-opus-4-6",
8
+ },
9
+ openai: {
10
+ fast: "gpt-4o-mini",
11
+ standard: "gpt-4o",
12
+ advanced: "o3",
13
+ },
14
+ google: {
15
+ fast: "gemini-2.0-flash",
16
+ standard: "gemini-2.5-pro",
17
+ advanced: "gemini-2.5-pro",
18
+ },
19
+ ollama: {
20
+ fast: "llama3.2",
21
+ standard: "llama3.3",
22
+ advanced: "llama3.3",
23
+ },
24
+ };
25
+ export function resolveModel(provider, tier, overrides) {
26
+ if (overrides?.[tier])
27
+ return overrides[tier];
28
+ return MODEL_MAP[provider][tier];
29
+ }
@@ -0,0 +1,3 @@
1
+ import type { CodeSampleResult } from "./types.js";
2
+ import type { StaticToolkit } from "./utils.js";
3
+ export declare function collectCodeSamples(tk: StaticToolkit): Promise<CodeSampleResult>;
@@ -0,0 +1,153 @@
1
+ // Static Analysis — Code Sampler
2
+ // Collects key file excerpts, import graph, and config files for LLM context
3
+ const MAX_EXCERPT_LINES = 80;
4
+ const MAX_IMPORT_GREP = 500;
5
+ const MAX_ROUTE_GREP = 100;
6
+ /** Config file names to look for at the project root and in workspace dirs */
7
+ const CONFIG_FILES = [
8
+ "package.json",
9
+ "docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml",
10
+ "Cargo.toml", "go.mod", "pyproject.toml", "requirements.txt",
11
+ "Makefile", "Dockerfile",
12
+ "tsconfig.json", "vite.config.ts", "vite.config.js",
13
+ "next.config.js", "next.config.ts", "next.config.mjs",
14
+ "nest-cli.json", "angular.json",
15
+ ];
16
+ /** Common entry point filenames */
17
+ const ENTRY_POINTS = [
18
+ "src/index.ts", "src/index.js", "src/main.ts", "src/main.js",
19
+ "src/app.ts", "src/app.js", "src/server.ts", "src/server.js",
20
+ "index.ts", "index.js", "main.ts", "main.js",
21
+ "app.ts", "app.js", "server.ts", "server.js",
22
+ "main.py", "app.py", "manage.py",
23
+ "main.go", "cmd/main.go", "cmd/server/main.go",
24
+ "src/main.rs", "src/lib.rs",
25
+ ];
26
+ export async function collectCodeSamples(tk) {
27
+ const samples = [];
28
+ const configFiles = [];
29
+ const importMap = {};
30
+ // 1. Find workspace directories (dirs with a build config)
31
+ const rootEntries = await tk.listDir(".");
32
+ const topDirs = rootEntries
33
+ .filter((e) => e.type === "directory" && !e.name.startsWith(".") && !isVendored(e.name))
34
+ .map((e) => e.name);
35
+ // 2. Collect config files at root level
36
+ await collectConfigs(tk, ".", configFiles);
37
+ // 3. Collect config files in top-level directories
38
+ const configPromises = topDirs.map((dir) => collectConfigs(tk, dir, configFiles));
39
+ await Promise.all(configPromises);
40
+ // Also check for sub-workspace dirs (e.g., apps/web, packages/core)
41
+ const subDirChecks = ["apps", "packages", "services", "libs", "modules", "crates"];
42
+ for (const parent of subDirChecks) {
43
+ if (!topDirs.includes(parent))
44
+ continue;
45
+ const subEntries = await tk.listDir(parent);
46
+ const subDirs = subEntries
47
+ .filter((e) => e.type === "directory" && !e.name.startsWith("."))
48
+ .map((e) => `${parent}/${e.name}`);
49
+ await Promise.all(subDirs.map((dir) => collectConfigs(tk, dir, configFiles)));
50
+ }
51
+ // 4. Collect entry point excerpts for dirs that have configs
52
+ const dirsWithConfigs = new Set();
53
+ for (const cf of configFiles) {
54
+ const dir = cf.path.includes("/") ? cf.path.substring(0, cf.path.lastIndexOf("/")) : ".";
55
+ dirsWithConfigs.add(dir);
56
+ }
57
+ for (const dir of dirsWithConfigs) {
58
+ await collectEntryPoint(tk, dir, samples);
59
+ }
60
+ // 5. Build import map from grep
61
+ try {
62
+ const importResults = await tk.grepFiles("import\\s.*from\\s|require\\(");
63
+ for (const r of importResults.slice(0, MAX_IMPORT_GREP)) {
64
+ if (isVendoredPath(r.file))
65
+ continue;
66
+ const imported = extractImportPath(r.content);
67
+ if (imported) {
68
+ if (!importMap[r.file])
69
+ importMap[r.file] = [];
70
+ importMap[r.file].push(imported);
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ // grep failed, skip import map
76
+ }
77
+ // 6. Collect route file samples
78
+ try {
79
+ const routeResults = await tk.grepFiles("app\\.(get|post|put|delete|patch|use)\\(|router\\.|@(Get|Post|Put|Delete|Patch)\\(|@app\\.route");
80
+ const routeFiles = new Set();
81
+ for (const r of routeResults.slice(0, MAX_ROUTE_GREP)) {
82
+ if (isVendoredPath(r.file))
83
+ continue;
84
+ routeFiles.add(r.file);
85
+ }
86
+ // Read first 80 lines of each unique route file (up to 10)
87
+ let routeCount = 0;
88
+ for (const filePath of routeFiles) {
89
+ if (routeCount >= 10)
90
+ break;
91
+ if (samples.some((s) => s.path === filePath))
92
+ continue;
93
+ const content = await tk.readFileSafe(filePath);
94
+ if (!content)
95
+ continue;
96
+ const excerpt = content.split("\n").slice(0, MAX_EXCERPT_LINES).join("\n");
97
+ samples.push({ path: filePath, excerpt, category: "route-file" });
98
+ routeCount++;
99
+ }
100
+ }
101
+ catch {
102
+ // grep failed, skip route files
103
+ }
104
+ return { samples, importMap, configFiles };
105
+ }
106
+ async function collectConfigs(tk, dir, configFiles) {
107
+ for (const name of CONFIG_FILES) {
108
+ const filePath = dir === "." ? name : `${dir}/${name}`;
109
+ const content = await tk.readFileSafe(filePath);
110
+ if (content) {
111
+ // Cap config content at 5000 chars to avoid blowing context
112
+ configFiles.push({
113
+ path: filePath,
114
+ content: content.length > 5000 ? content.slice(0, 5000) + "\n... (truncated)" : content,
115
+ });
116
+ }
117
+ }
118
+ }
119
+ async function collectEntryPoint(tk, dir, samples) {
120
+ for (const ep of ENTRY_POINTS) {
121
+ const filePath = dir === "." ? ep : `${dir}/${ep}`;
122
+ if (samples.some((s) => s.path === filePath))
123
+ continue;
124
+ const content = await tk.readFileSafe(filePath);
125
+ if (content) {
126
+ const excerpt = content.split("\n").slice(0, MAX_EXCERPT_LINES).join("\n");
127
+ samples.push({ path: filePath, excerpt, category: "entry-point" });
128
+ return; // one entry point per dir is enough
129
+ }
130
+ }
131
+ }
132
+ function extractImportPath(line) {
133
+ // ES import: import ... from "path"
134
+ const esMatch = line.match(/from\s+["']([^"']+)["']/);
135
+ if (esMatch)
136
+ return esMatch[1];
137
+ // CommonJS: require("path")
138
+ const cjsMatch = line.match(/require\(\s*["']([^"']+)["']\s*\)/);
139
+ if (cjsMatch)
140
+ return cjsMatch[1];
141
+ return null;
142
+ }
143
+ const VENDORED = new Set([
144
+ "node_modules", "dist", "build", ".git", "venv", "__pycache__",
145
+ "target", "coverage", "vendor", ".next", ".nuxt",
146
+ ]);
147
+ function isVendored(name) {
148
+ return VENDORED.has(name);
149
+ }
150
+ function isVendoredPath(filePath) {
151
+ const parts = filePath.split("/");
152
+ return parts.some((p) => VENDORED.has(p));
153
+ }
@@ -0,0 +1,3 @@
1
+ import type { ComponentResult, StructureResult } from "./types.js";
2
+ import type { StaticToolkit } from "./utils.js";
3
+ export declare function detectComponents(tk: StaticToolkit, structure: StructureResult): Promise<ComponentResult>;