archbyte 0.2.7 → 0.2.9

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/README.md CHANGED
@@ -14,7 +14,7 @@ archbyte login
14
14
  # Configure your AI provider (BYOK)
15
15
  archbyte init
16
16
 
17
- # Or, if you use Claude Code / Codex skip BYOK entirely:
17
+ # Using Claude Code / Codex? Install the MCP server too:
18
18
  archbyte mcp install
19
19
  ```
20
20
 
@@ -22,7 +22,7 @@ Your API keys stay on your machine. ArchByte never stores or transmits your prov
22
22
 
23
23
  ### Setup with Claude Code
24
24
 
25
- **Option A: MCP (recommended)** — use ArchByte tools directly in Claude Code, no API key needed:
25
+ **Option A: MCP** — use ArchByte tools directly in Claude Code. You still need to sign in and configure a provider first:
26
26
 
27
27
  ```bash
28
28
  archbyte mcp install
package/dist/cli/setup.js CHANGED
@@ -11,7 +11,7 @@ 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 = {
@@ -22,10 +22,9 @@ const PROVIDER_MODELS = {
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
  }
@@ -132,26 +172,7 @@ export async function handleSetup() {
132
172
  if (hasClaude || hasCodex) {
133
173
  const tools = [hasClaude && "Claude Code", hasCodex && "Codex CLI"].filter(Boolean).join(" and ");
134
174
  console.log(chalk.cyan(` Detected ${tools} on this machine.`));
135
- console.log(chalk.white(` You can use ArchByte directly through MCP. No API key needed.`));
136
- console.log(chalk.white(` Your AI tool already provides the model, so you skip the BYOK step.`));
137
- console.log();
138
- console.log(chalk.white(` Run: `) + chalk.bold.cyan(`archbyte mcp install`));
139
- console.log();
140
- const continueIdx = await select("Continue with BYOK setup anyway?", [
141
- `Skip ${chalk.gray("(use MCP instead, recommended)")}`,
142
- `Continue ${chalk.gray("(set up your own API key)")}`,
143
- ]);
144
- if (continueIdx === 0) {
145
- console.log();
146
- console.log(chalk.gray(" Run `archbyte mcp install` to configure MCP for your AI tool."));
147
- console.log();
148
- console.log(chalk.gray(" Then open your AI tool and try:"));
149
- console.log(chalk.cyan(' "Analyze the architecture of this project"'));
150
- console.log(chalk.cyan(' "Export the architecture as a Mermaid diagram"'));
151
- console.log(chalk.cyan(' "Show me the architecture stats"'));
152
- console.log();
153
- return;
154
- }
175
+ console.log(chalk.white(` After setup, run `) + chalk.bold.cyan(`archbyte mcp install`) + chalk.white(` to use ArchByte from your AI tool.`));
155
176
  console.log();
156
177
  }
157
178
  const config = loadConfig();
@@ -182,7 +203,8 @@ export async function handleSetup() {
182
203
  for (const p of configured) {
183
204
  const active = p === config.provider ? chalk.green(" (active)") : "";
184
205
  const model = profiles[p].model ? chalk.gray(` model: ${profiles[p].model}`) : "";
185
- 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}`));
186
208
  }
187
209
  console.log();
188
210
  }
@@ -232,6 +254,30 @@ export async function handleSetup() {
232
254
  if (!profiles[provider])
233
255
  profiles[provider] = { apiKey: "" };
234
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
+ }
235
281
  // Step 3: Model selection
236
282
  const models = PROVIDER_MODELS[provider];
237
283
  if (models) {
@@ -251,9 +297,11 @@ export async function handleSetup() {
251
297
  }
252
298
  }
253
299
  config.profiles = profiles;
300
+ // Validate with the model the user actually chose
301
+ const validationModel = profiles[provider].model ?? resolveModel(provider, "fast");
254
302
  // Validate provider with spinner
255
- const s = spinner("Validating credentials");
256
- let isValid = await validateProviderSilent(provider, apiKey);
303
+ const s = spinner(`Validating credentials with ${validationModel}`);
304
+ let isValid = await validateProviderSilent(provider, apiKey, validationModel);
257
305
  s.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
258
306
  // Retry loop on failure
259
307
  if (!isValid) {
@@ -267,8 +315,8 @@ export async function handleSetup() {
267
315
  profiles[provider].apiKey = newKey;
268
316
  console.log(chalk.green(` ✓ API key: ${maskKey(newKey)}`));
269
317
  }
270
- const s2 = spinner("Validating credentials");
271
- isValid = await validateProviderSilent(provider, profiles[provider].apiKey);
318
+ const s2 = spinner(`Validating credentials with ${validationModel}`);
319
+ isValid = await validateProviderSilent(provider, profiles[provider].apiKey, validationModel);
272
320
  s2.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
273
321
  }
274
322
  if (!isValid) {
@@ -319,6 +367,10 @@ export async function handleSetup() {
319
367
  console.log();
320
368
  console.log(chalk.bold(" Next: ") + chalk.cyan("archbyte run") + chalk.bold(" to analyze your codebase."));
321
369
  console.log(chalk.gray(" Run from your project root, or use ") + chalk.cyan("archbyte run -d /path/to/project"));
370
+ if (hasClaude || hasCodex) {
371
+ console.log();
372
+ console.log(chalk.bold(" MCP: ") + chalk.cyan("archbyte mcp install") + chalk.bold(" to use ArchByte from your AI tool."));
373
+ }
322
374
  console.log();
323
375
  }
324
376
  function writeArchbyteReadme(archbyteDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archbyte",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "ArchByte - See what agents build. As they build it.",
5
5
  "type": "module",
6
6
  "bin": {