archbyte 0.2.8 → 0.3.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.
package/dist/cli/setup.js CHANGED
@@ -11,21 +11,20 @@ const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = path.dirname(__filename);
12
12
  const PROVIDERS = [
13
13
  { name: "anthropic", label: "Anthropic", hint: "Claude Sonnet / Opus" },
14
- { name: "openai", label: "OpenAI", hint: "Codex / GPT-5.2" },
14
+ { name: "openai", label: "OpenAI", hint: "GPT-5.2 / o4-mini" },
15
15
  { name: "google", label: "Google", hint: "Gemini Flash / Pro" },
16
16
  ];
17
17
  const PROVIDER_MODELS = {
18
18
  anthropic: [
19
- { id: "", label: "Default (recommended)", hint: "Haiku for fast agents, Sonnet for connections" },
19
+ { id: "", label: "Default (recommended)", hint: "Opus for all agents" },
20
20
  { id: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "Fastest, cheapest" },
21
21
  { id: "claude-sonnet-4-5-20250929", label: "Claude Sonnet 4.5", hint: "Balanced, great quality" },
22
22
  { id: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "Most capable" },
23
23
  ],
24
24
  openai: [
25
- { id: "", label: "Default (recommended)", hint: "Codex for fast agents, GPT-5.2 for connections" },
26
- { id: "gpt-5.2-codex", label: "GPT-5.2 Codex", hint: "Fast, coding-optimized" },
27
- { id: "gpt-5.2", label: "GPT-5.2", hint: "General purpose, thinking model" },
28
- { id: "o3", label: "o3", hint: "Reasoning model" },
25
+ { id: "", label: "Default (recommended)", hint: "GPT-5.2 for all agents" },
26
+ { id: "gpt-5.2", label: "GPT-5.2", hint: "Latest flagship model" },
27
+ { id: "o4-mini", label: "o4-mini", hint: "Fast reasoning model" },
29
28
  ],
30
29
  google: [
31
30
  { id: "", label: "Default (recommended)", hint: "Flash for fast agents, Pro for connections" },
@@ -93,10 +92,47 @@ function askHidden(prompt) {
93
92
  stdin.on("data", onData);
94
93
  });
95
94
  }
96
- async function validateProviderSilent(providerName, apiKey) {
95
+ function askText(prompt) {
96
+ return new Promise((resolve) => {
97
+ process.stdout.write(prompt);
98
+ const stdin = process.stdin;
99
+ const wasRaw = stdin.isRaw;
100
+ stdin.setRawMode(true);
101
+ stdin.resume();
102
+ stdin.setEncoding("utf8");
103
+ let input = "";
104
+ const onData = (data) => {
105
+ for (const ch of data) {
106
+ if (ch === "\n" || ch === "\r" || ch === "\u0004") {
107
+ stdin.setRawMode(wasRaw ?? false);
108
+ stdin.pause();
109
+ stdin.removeListener("data", onData);
110
+ process.stdout.write("\n");
111
+ resolve(input);
112
+ return;
113
+ }
114
+ else if (ch === "\u0003") {
115
+ process.stdout.write("\n");
116
+ process.exit(0);
117
+ }
118
+ else if (ch === "\u007F" || ch === "\b") {
119
+ if (input.length > 0) {
120
+ input = input.slice(0, -1);
121
+ process.stdout.write("\b \b");
122
+ }
123
+ }
124
+ else {
125
+ input += ch;
126
+ process.stdout.write(ch);
127
+ }
128
+ }
129
+ };
130
+ stdin.on("data", onData);
131
+ });
132
+ }
133
+ async function validateProviderSilent(providerName, apiKey, model) {
97
134
  try {
98
135
  const provider = createProvider({ provider: providerName, apiKey });
99
- const model = resolveModel(providerName, "fast");
100
136
  await provider.chat({
101
137
  model,
102
138
  system: "Reply with 'ok'.",
@@ -105,7 +141,11 @@ async function validateProviderSilent(providerName, apiKey) {
105
141
  });
106
142
  return true;
107
143
  }
108
- catch {
144
+ catch (err) {
145
+ if (process.env.ARCHBYTE_DEBUG) {
146
+ const msg = err instanceof Error ? err.message : String(err);
147
+ console.log(chalk.red(`\n Validation error: ${msg}`));
148
+ }
109
149
  return false;
110
150
  }
111
151
  }
@@ -163,7 +203,8 @@ export async function handleSetup() {
163
203
  for (const p of configured) {
164
204
  const active = p === config.provider ? chalk.green(" (active)") : "";
165
205
  const model = profiles[p].model ? chalk.gray(` model: ${profiles[p].model}`) : "";
166
- console.log(chalk.gray(` ${p}: ${maskKey(profiles[p].apiKey)}${model}${active}`));
206
+ const email = profiles[p].email ? chalk.gray(` (${profiles[p].email})`) : "";
207
+ console.log(chalk.gray(` ${p}: ${maskKey(profiles[p].apiKey)}${email}${model}${active}`));
167
208
  }
168
209
  console.log();
169
210
  }
@@ -213,6 +254,30 @@ export async function handleSetup() {
213
254
  if (!profiles[provider])
214
255
  profiles[provider] = { apiKey: "" };
215
256
  profiles[provider].apiKey = apiKey;
257
+ // Step 2b: Email for this provider account (optional)
258
+ const existingEmail = profiles[provider].email;
259
+ if (existingEmail) {
260
+ console.log(chalk.gray(`\n Account email: ${existingEmail}`));
261
+ const emailIdx = await select(" Update email?", [
262
+ `Keep existing ${chalk.gray("(" + existingEmail + ")")}`,
263
+ "Enter new email",
264
+ "Skip",
265
+ ]);
266
+ if (emailIdx === 1) {
267
+ const newEmail = await askText(chalk.bold(" Email: "));
268
+ if (newEmail) {
269
+ profiles[provider].email = newEmail;
270
+ console.log(chalk.green(` ✓ Email: ${newEmail}`));
271
+ }
272
+ }
273
+ }
274
+ else {
275
+ const email = await askText(chalk.bold(` ${selected.label} account email ${chalk.gray("(optional, Enter to skip)")}: `));
276
+ if (email) {
277
+ profiles[provider].email = email;
278
+ console.log(chalk.green(` ✓ Email: ${email}`));
279
+ }
280
+ }
216
281
  // Step 3: Model selection
217
282
  const models = PROVIDER_MODELS[provider];
218
283
  if (models) {
@@ -227,14 +292,24 @@ export async function handleSetup() {
227
292
  console.log(chalk.green(` ✓ Model: ${chosen.label}`));
228
293
  }
229
294
  else {
230
- delete profiles[provider].model;
231
- console.log(chalk.green(` ✓ Model: Default (auto per agent tier)`));
295
+ // Save the actual default model so config is explicit
296
+ const defaultModels = {
297
+ anthropic: "claude-opus-4-6",
298
+ openai: "gpt-5.2",
299
+ google: "gemini-2.5-pro",
300
+ ollama: "qwen2.5-coder",
301
+ };
302
+ const defaultModel = defaultModels[provider] ?? resolveModel(provider, "standard");
303
+ profiles[provider].model = defaultModel;
304
+ console.log(chalk.green(` ✓ Model: ${defaultModel} (default)`));
232
305
  }
233
306
  }
234
307
  config.profiles = profiles;
308
+ // Validate with the model the user actually chose
309
+ const validationModel = profiles[provider].model ?? resolveModel(provider, "fast");
235
310
  // Validate provider with spinner
236
- const s = spinner("Validating credentials");
237
- let isValid = await validateProviderSilent(provider, apiKey);
311
+ const s = spinner(`Validating credentials with ${validationModel}`);
312
+ let isValid = await validateProviderSilent(provider, apiKey, validationModel);
238
313
  s.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
239
314
  // Retry loop on failure
240
315
  if (!isValid) {
@@ -248,8 +323,8 @@ export async function handleSetup() {
248
323
  profiles[provider].apiKey = newKey;
249
324
  console.log(chalk.green(` ✓ API key: ${maskKey(newKey)}`));
250
325
  }
251
- const s2 = spinner("Validating credentials");
252
- isValid = await validateProviderSilent(provider, profiles[provider].apiKey);
326
+ const s2 = spinner(`Validating credentials with ${validationModel}`);
327
+ isValid = await validateProviderSilent(provider, profiles[provider].apiKey, validationModel);
253
328
  s2.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
254
329
  }
255
330
  if (!isValid) {
@@ -262,13 +337,11 @@ export async function handleSetup() {
262
337
  delete config.ollamaBaseUrl;
263
338
  // Save config
264
339
  saveConfig(config);
265
- console.log(chalk.gray(`\n Config saved to ${CONFIG_PATH}`));
266
- console.log(chalk.gray(" Your API key is stored locally on this machine and never sent to ArchByte."));
267
- console.log(chalk.gray(" All model calls go directly from your machine to your provider."));
268
340
  // Generate archbyte.yaml in .archbyte/ if it doesn't exist
269
341
  const projectDir = process.cwd();
270
342
  const archbyteDir = path.join(projectDir, ".archbyte");
271
343
  const yamlPath = path.join(archbyteDir, "archbyte.yaml");
344
+ let yamlCreated = false;
272
345
  if (!fs.existsSync(yamlPath)) {
273
346
  if (!fs.existsSync(archbyteDir)) {
274
347
  fs.mkdirSync(archbyteDir, { recursive: true });
@@ -288,21 +361,39 @@ export async function handleSetup() {
288
361
  let template = fs.readFileSync(templatePath, "utf-8");
289
362
  template = template.replace("name: my-project", `name: ${projectName}`);
290
363
  fs.writeFileSync(yamlPath, template, "utf-8");
291
- console.log(chalk.green(` Created .archbyte/archbyte.yaml`));
292
- }
293
- else {
294
- console.log(chalk.gray(` .archbyte/archbyte.yaml already exists`));
364
+ yamlCreated = true;
295
365
  }
296
366
  // Generate README.md in .archbyte/
297
367
  writeArchbyteReadme(archbyteDir);
368
+ // ─── Summary ───
369
+ const dim = chalk.gray;
370
+ const sep = dim(" ───");
371
+ console.log();
372
+ console.log(chalk.bold.green(" ✓ Setup complete"));
373
+ console.log();
374
+ console.log(sep);
375
+ console.log();
376
+ console.log(dim(" ~/.archbyte/") + " API keys, login, config " + dim("(global)"));
377
+ console.log(dim(" .archbyte/") + " Analysis, diagrams " + dim("(per project)"));
298
378
  console.log();
299
- console.log(chalk.green(" Setup complete!"));
379
+ console.log(dim(" Your keys never leave this machine."));
380
+ console.log(dim(" All model calls go directly to your provider."));
300
381
  console.log();
301
- console.log(chalk.bold(" Next: ") + chalk.cyan("archbyte run") + chalk.bold(" to analyze your codebase."));
302
- console.log(chalk.gray(" Run from your project root, or use ") + chalk.cyan("archbyte run -d /path/to/project"));
382
+ console.log(sep);
383
+ console.log();
384
+ if (yamlCreated) {
385
+ console.log(chalk.green(" + ") + "Created " + chalk.cyan(".archbyte/archbyte.yaml"));
386
+ }
387
+ else {
388
+ console.log(dim(" .archbyte/archbyte.yaml already exists"));
389
+ }
390
+ console.log();
391
+ console.log(sep);
392
+ console.log();
393
+ console.log(" " + chalk.bold("Next"));
394
+ console.log(" " + chalk.cyan("archbyte run") + " Analyze your codebase");
303
395
  if (hasClaude || hasCodex) {
304
- console.log();
305
- console.log(chalk.bold(" MCP: ") + chalk.cyan("archbyte mcp install") + chalk.bold(" to use ArchByte from your AI tool."));
396
+ console.log(" " + chalk.cyan("archbyte mcp install") + " Use from your AI tool");
306
397
  }
307
398
  console.log();
308
399
  }
@@ -71,6 +71,8 @@ export interface ScanMetadata {
71
71
  };
72
72
  validation?: unknown;
73
73
  gaps?: unknown;
74
+ incrementalMode?: boolean;
75
+ skippedAgents?: string[];
74
76
  }
75
77
  export declare function loadSpec(rootDir: string): ArchbyteSpec | null;
76
78
  export declare function writeSpec(rootDir: string, spec: ArchbyteSpec): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archbyte",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "ArchByte - See what agents build. As they build it.",
5
5
  "type": "module",
6
6
  "bin": {