archbyte 0.3.5 → 0.4.1

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 (45) hide show
  1. package/README.md +42 -0
  2. package/bin/archbyte.js +26 -25
  3. package/dist/agents/pipeline/merger.d.ts +2 -2
  4. package/dist/agents/pipeline/merger.js +165 -35
  5. package/dist/agents/pipeline/types.d.ts +29 -1
  6. package/dist/agents/pipeline/types.js +0 -1
  7. package/dist/agents/providers/claude-sdk.d.ts +7 -0
  8. package/dist/agents/providers/claude-sdk.js +83 -0
  9. package/dist/agents/providers/router.d.ts +5 -0
  10. package/dist/agents/providers/router.js +23 -1
  11. package/dist/agents/runtime/types.d.ts +6 -2
  12. package/dist/agents/runtime/types.js +6 -1
  13. package/dist/agents/static/component-detector.js +35 -3
  14. package/dist/agents/static/connection-mapper.d.ts +1 -1
  15. package/dist/agents/static/connection-mapper.js +74 -1
  16. package/dist/agents/static/index.js +5 -2
  17. package/dist/agents/static/types.d.ts +26 -0
  18. package/dist/cli/analyze.js +65 -18
  19. package/dist/cli/arch-diff.d.ts +38 -0
  20. package/dist/cli/arch-diff.js +61 -0
  21. package/dist/cli/auth.d.ts +8 -2
  22. package/dist/cli/auth.js +241 -31
  23. package/dist/cli/config.js +31 -5
  24. package/dist/cli/export.js +64 -2
  25. package/dist/cli/patrol.d.ts +5 -3
  26. package/dist/cli/patrol.js +417 -65
  27. package/dist/cli/setup.js +76 -8
  28. package/dist/cli/shared.d.ts +11 -0
  29. package/dist/cli/shared.js +61 -0
  30. package/dist/cli/ui.d.ts +9 -0
  31. package/dist/cli/ui.js +59 -5
  32. package/dist/cli/validate.d.ts +0 -1
  33. package/dist/cli/validate.js +0 -16
  34. package/dist/server/src/index.js +593 -19
  35. package/package.json +4 -1
  36. package/templates/archbyte.yaml +8 -0
  37. package/ui/dist/assets/index-DDCNauh7.css +1 -0
  38. package/ui/dist/assets/index-DO4t5Xu1.js +72 -0
  39. package/ui/dist/index.html +2 -2
  40. package/dist/cli/mcp-server.d.ts +0 -1
  41. package/dist/cli/mcp-server.js +0 -443
  42. package/dist/cli/mcp.d.ts +0 -1
  43. package/dist/cli/mcp.js +0 -98
  44. package/ui/dist/assets/index-0_XpUUZQ.css +0 -1
  45. package/ui/dist/assets/index-BTo0zV5E.js +0 -70
package/dist/cli/setup.js CHANGED
@@ -15,6 +15,12 @@ const PROVIDERS = [
15
15
  { name: "google", label: "Google", hint: "Gemini 2.5 Pro / Flash" },
16
16
  ];
17
17
  const PROVIDER_MODELS = {
18
+ "claude-sdk": [
19
+ { id: "", label: "Default (recommended)", hint: "Sonnet for all agents" },
20
+ { id: "opus", label: "Claude Opus 4.6", hint: "Most capable" },
21
+ { id: "sonnet", label: "Claude Sonnet 4.5", hint: "Fast, great quality" },
22
+ { id: "haiku", label: "Claude Haiku 4.5", hint: "Fastest, cheapest" },
23
+ ],
18
24
  anthropic: [
19
25
  { id: "", label: "Default (recommended)", hint: "Opus for all agents" },
20
26
  { id: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "Most capable" },
@@ -205,16 +211,10 @@ export async function handleSetup() {
205
211
  console.log();
206
212
  console.log(chalk.bold.cyan("ArchByte Setup"));
207
213
  console.log(chalk.gray("Configure your model provider and API key.\n"));
208
- // Detect AI coding tools — suggest MCP instead of BYOK
214
+ // Detect AI coding tools
209
215
  const hasClaude = isInPath("claude");
210
216
  const codexDir = path.join(CONFIG_DIR, "../.codex");
211
217
  const hasCodex = fs.existsSync(codexDir);
212
- if (hasClaude || hasCodex) {
213
- const tools = [hasClaude && "Claude Code", hasCodex && "Codex CLI"].filter(Boolean).join(" and ");
214
- console.log(chalk.cyan(` Detected ${tools} on this machine.`));
215
- console.log(chalk.white(` After setup, run `) + chalk.bold.cyan(`archbyte mcp install`) + chalk.white(` to use ArchByte from your AI tool.`));
216
- console.log();
217
- }
218
218
  const config = loadConfig();
219
219
  const profiles = getProfiles(config);
220
220
  // Migrate legacy flat config → profiles
@@ -247,6 +247,75 @@ export async function handleSetup() {
247
247
  }
248
248
  console.log();
249
249
  }
250
+ // Detect AI coding tools and offer zero-config options
251
+ if (hasClaude || hasCodex) {
252
+ const tools = [hasClaude && "Claude Code", hasCodex && "Codex CLI"].filter(Boolean).join(" and ");
253
+ console.log(chalk.cyan(` Detected ${tools} on this machine.\n`));
254
+ // Build options based on what's detected
255
+ const toolOptions = [];
256
+ if (hasClaude) {
257
+ toolOptions.push({
258
+ label: `Claude Code (SDK) ${chalk.gray("zero config — uses your Claude Code subscription")}`,
259
+ value: "claude-sdk",
260
+ });
261
+ }
262
+ if (hasCodex) {
263
+ toolOptions.push({
264
+ label: `Codex CLI ${chalk.gray("zero config — uses your Codex subscription")}`,
265
+ value: "codex",
266
+ });
267
+ }
268
+ toolOptions.push({
269
+ label: `Bring your own API key ${chalk.gray("Anthropic, OpenAI, or Google")}`,
270
+ value: "byok",
271
+ });
272
+ const toolIdx = await select("How do you want to run ArchByte?", toolOptions.map((o) => o.label));
273
+ const choice = toolOptions[toolIdx].value;
274
+ if (choice === "claude-sdk") {
275
+ config.provider = "claude-sdk";
276
+ // Model selection
277
+ const models = PROVIDER_MODELS["claude-sdk"];
278
+ const modelIdx = await select("\n Choose a model:", models.map((m) => `${m.label} ${chalk.gray(m.hint)}`));
279
+ const chosenModel = models[modelIdx];
280
+ if (chosenModel.id) {
281
+ config.model = chosenModel.id;
282
+ console.log(chalk.green(` ✓ Model: ${chosenModel.label}`));
283
+ }
284
+ else {
285
+ delete config.model;
286
+ console.log(chalk.green(` ✓ Model: Sonnet (default)`));
287
+ }
288
+ config.profiles = profiles;
289
+ delete config.apiKey;
290
+ saveConfig(config);
291
+ const dim = chalk.gray;
292
+ const sep = dim(" ───");
293
+ console.log();
294
+ console.log(chalk.bold.green(" ✓ Setup complete — using Claude Code (SDK)"));
295
+ console.log();
296
+ console.log(sep);
297
+ console.log();
298
+ console.log(dim(" No API key needed. ArchByte uses your Claude Code subscription."));
299
+ console.log(dim(" All model calls go through Claude Code on this machine."));
300
+ console.log();
301
+ console.log(sep);
302
+ console.log();
303
+ console.log(" " + chalk.bold("Next steps"));
304
+ console.log();
305
+ console.log(" " + chalk.cyan("archbyte run") + " Analyze your codebase");
306
+ console.log(" " + chalk.cyan("archbyte status") + " Check account and usage");
307
+ console.log(" " + chalk.cyan("archbyte --help") + " See all commands");
308
+ console.log();
309
+ return;
310
+ }
311
+ if (choice === "codex") {
312
+ // TODO: Add Codex SDK provider when available
313
+ console.log(chalk.yellow("\n Codex SDK provider coming soon. Setting up with API key for now.\n"));
314
+ }
315
+ // User chose BYOK — continue to normal provider selection below
316
+ if (choice === "byok")
317
+ console.log();
318
+ }
250
319
  // Step 1: Choose provider
251
320
  const idx = await select("Choose your model provider:", PROVIDERS.map((p) => {
252
321
  const active = config.provider === p.name ? chalk.green(" (active)") : "";
@@ -523,7 +592,6 @@ export async function handleSetup() {
523
592
  console.log();
524
593
  console.log(" " + chalk.cyan("archbyte run") + " Analyze your codebase");
525
594
  if (hasClaude || hasCodex) {
526
- console.log(" " + chalk.cyan("archbyte mcp install") + " Use from your AI tool");
527
595
  }
528
596
  console.log(" " + chalk.cyan("archbyte status") + " Check account and usage");
529
597
  console.log(" " + chalk.cyan("archbyte --help") + " See all commands");
@@ -64,5 +64,16 @@ export declare function parseRulesFromYaml(content: string): RuleConfig;
64
64
  * level: error
65
65
  */
66
66
  export declare function parseCustomRulesFromYaml(content: string): CustomRule[];
67
+ /**
68
+ * Parse the patrol.ignore list from archbyte.yaml.
69
+ * Returns user-defined glob patterns for watch mode to ignore.
70
+ *
71
+ * patrol:
72
+ * ignore:
73
+ * - "docs/"
74
+ * - "*.md"
75
+ * - "build/"
76
+ */
77
+ export declare function loadPatrolIgnore(configPath?: string): string[];
67
78
  export declare function getRuleLevel(config: RuleConfig, rule: keyof RuleConfig, defaultLevel: RuleLevel): RuleLevel;
68
79
  export declare function getThreshold(config: RuleConfig, rule: "max-connections", defaultVal: number): number;
@@ -259,6 +259,67 @@ export function parseCustomRulesFromYaml(content) {
259
259
  flushItem();
260
260
  return rules;
261
261
  }
262
+ /**
263
+ * Parse the patrol.ignore list from archbyte.yaml.
264
+ * Returns user-defined glob patterns for watch mode to ignore.
265
+ *
266
+ * patrol:
267
+ * ignore:
268
+ * - "docs/"
269
+ * - "*.md"
270
+ * - "build/"
271
+ */
272
+ export function loadPatrolIgnore(configPath) {
273
+ const rootDir = process.cwd();
274
+ const yamlPath = configPath
275
+ ? path.resolve(rootDir, configPath)
276
+ : path.join(rootDir, ".archbyte", "archbyte.yaml");
277
+ if (!fs.existsSync(yamlPath))
278
+ return [];
279
+ try {
280
+ const lines = fs.readFileSync(yamlPath, "utf-8").split("\n");
281
+ const patterns = [];
282
+ let inPatrol = false;
283
+ let inIgnore = false;
284
+ for (const line of lines) {
285
+ const trimmed = line.trimEnd();
286
+ if (/^patrol:\s*$/.test(trimmed)) {
287
+ inPatrol = true;
288
+ continue;
289
+ }
290
+ // Another top-level section ends patrol
291
+ if (inPatrol && /^\S/.test(trimmed) && !trimmed.startsWith("#")) {
292
+ inPatrol = false;
293
+ inIgnore = false;
294
+ continue;
295
+ }
296
+ if (!inPatrol)
297
+ continue;
298
+ if (trimmed === "" || trimmed.trim().startsWith("#"))
299
+ continue;
300
+ if (/^ {2}ignore:\s*$/.test(trimmed)) {
301
+ inIgnore = true;
302
+ continue;
303
+ }
304
+ // Another patrol sub-key ends ignore
305
+ if (inIgnore && /^ {2}\S/.test(trimmed) && !/^ {2}ignore:/.test(trimmed)) {
306
+ inIgnore = false;
307
+ continue;
308
+ }
309
+ if (!inIgnore)
310
+ continue;
311
+ // List item: " - pattern"
312
+ const itemMatch = trimmed.match(/^ {4}-\s+"?([^"]+)"?\s*$/);
313
+ if (itemMatch) {
314
+ patterns.push(itemMatch[1].trim());
315
+ }
316
+ }
317
+ return patterns;
318
+ }
319
+ catch {
320
+ return [];
321
+ }
322
+ }
262
323
  export function getRuleLevel(config, rule, defaultLevel) {
263
324
  const entry = config[rule];
264
325
  if (!entry)
package/dist/cli/ui.d.ts CHANGED
@@ -30,4 +30,13 @@ export declare function progressBar(totalSteps: number): ProgressBar;
30
30
  * (arrow keys, etc.) to prevent accidental confirmation.
31
31
  */
32
32
  export declare function confirm(prompt: string): Promise<boolean>;
33
+ /**
34
+ * Text input prompt. Returns the entered string.
35
+ * Non-TTY fallback: returns empty string.
36
+ *
37
+ * @param mask - If true, replaces each character with * (for passwords).
38
+ */
39
+ export declare function textInput(prompt: string, opts?: {
40
+ mask?: boolean;
41
+ }): Promise<string>;
33
42
  export {};
package/dist/cli/ui.js CHANGED
@@ -111,13 +111,12 @@ export function select(prompt, options) {
111
111
  cleanup();
112
112
  resolve(selected);
113
113
  }
114
- else if (data === "\x03") {
115
- // Ctrl+C only — clean exit
114
+ else if (data === "\x03" || data === "q") {
115
+ // Ctrl+C or q — clean exit
116
116
  cleanup();
117
117
  process.stdout.write("\n");
118
118
  process.exit(0);
119
119
  }
120
- // All other keys (including q, arrows, etc.) are ignored
121
120
  };
122
121
  function cleanup() {
123
122
  stdin.removeListener("data", onData);
@@ -202,8 +201,8 @@ export function confirm(prompt) {
202
201
  process.stdout.write("n\n");
203
202
  resolve(false);
204
203
  }
205
- else if (data === "\x03") {
206
- // Ctrl+C only
204
+ else if (data === "\x03" || data === "q") {
205
+ // Ctrl+C or q — clean exit
207
206
  process.stdout.write("\n");
208
207
  process.exit(0);
209
208
  }
@@ -216,3 +215,58 @@ export function confirm(prompt) {
216
215
  stdin.on("data", onData);
217
216
  });
218
217
  }
218
+ /**
219
+ * Text input prompt. Returns the entered string.
220
+ * Non-TTY fallback: returns empty string.
221
+ *
222
+ * @param mask - If true, replaces each character with * (for passwords).
223
+ */
224
+ export function textInput(prompt, opts) {
225
+ if (!process.stdout.isTTY) {
226
+ console.log(` ${prompt}: `);
227
+ return Promise.resolve("");
228
+ }
229
+ return new Promise((resolve) => {
230
+ process.stdout.write(` ${prompt}: `);
231
+ const stdin = process.stdin;
232
+ const wasRaw = stdin.isRaw;
233
+ stdin.setRawMode(true);
234
+ stdin.resume();
235
+ stdin.setEncoding("utf8");
236
+ let value = "";
237
+ const onData = (data) => {
238
+ if (data === "\r" || data === "\n") {
239
+ // Enter — submit
240
+ stdin.removeListener("data", onData);
241
+ stdin.setRawMode(wasRaw ?? false);
242
+ stdin.pause();
243
+ process.stdout.write("\n");
244
+ resolve(value);
245
+ }
246
+ else if (data === "\x03") {
247
+ // Ctrl+C
248
+ stdin.removeListener("data", onData);
249
+ stdin.setRawMode(wasRaw ?? false);
250
+ stdin.pause();
251
+ process.stdout.write("\n");
252
+ process.exit(0);
253
+ }
254
+ else if (data === "\x7f" || data === "\b") {
255
+ // Backspace
256
+ if (value.length > 0) {
257
+ value = value.slice(0, -1);
258
+ process.stdout.write("\b \b");
259
+ }
260
+ }
261
+ else if (data.startsWith("\x1b")) {
262
+ // Ignore escape sequences
263
+ }
264
+ else if (data >= " ") {
265
+ // Printable character
266
+ value += data;
267
+ process.stdout.write(opts?.mask ? "*" : data);
268
+ }
269
+ };
270
+ stdin.on("data", onData);
271
+ });
272
+ }
@@ -4,7 +4,6 @@ interface ValidateOptions {
4
4
  diagram?: string;
5
5
  config?: string;
6
6
  ci?: boolean;
7
- watch?: boolean;
8
7
  }
9
8
  export interface Violation {
10
9
  rule: string;
@@ -202,22 +202,6 @@ export async function handleValidate(options) {
202
202
  }
203
203
  // Human-readable output
204
204
  printValidationReport(result);
205
- // Watch mode: re-validate on file changes
206
- if (options.watch) {
207
- const chokidar = await import("chokidar");
208
- const diagramPath = resolveArchitecturePath(options);
209
- console.log(chalk.gray(` Watching ${diagramPath} for changes...`));
210
- console.log(chalk.gray(" Press Ctrl+C to stop."));
211
- const watcher = chokidar.watch(diagramPath, { ignoreInitial: true });
212
- watcher.on("change", () => {
213
- console.clear();
214
- const watchResult = runValidation(options);
215
- printValidationReport(watchResult);
216
- console.log(chalk.gray(` Watching ${diagramPath} for changes...`));
217
- console.log(chalk.gray(" Press Ctrl+C to stop."));
218
- });
219
- return;
220
- }
221
205
  if (result.errors > 0) {
222
206
  process.exit(1);
223
207
  }