@a3t/rapid 0.1.6 → 0.1.8

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.
@@ -1,1638 +0,0 @@
1
- // src/index.ts
2
- import { Command as Command10 } from "commander";
3
- import { setLogLevel, logger as logger10 } from "@a3t/rapid-core";
4
- import { readFileSync } from "fs";
5
- import { fileURLToPath } from "url";
6
- import { dirname, join as join4 } from "path";
7
-
8
- // src/commands/init.ts
9
- import { Command } from "commander";
10
- import { writeFile, access } from "fs/promises";
11
- import { join } from "path";
12
- import {
13
- getDefaultConfig,
14
- logger,
15
- MCP_SERVER_TEMPLATES,
16
- addMcpServerFromTemplate,
17
- getSecretReferences,
18
- writeMcpConfig,
19
- writeOpenCodeConfig,
20
- RAPID_METHODOLOGY,
21
- MCP_USAGE_GUIDELINES,
22
- GIT_GUIDELINES
23
- } from "@a3t/rapid-core";
24
- import ora from "ora";
25
- var initCommand = new Command("init").description("Initialize RAPID in a project").option("-t, --template <name>", "Template to use", "default").option("--force", "Overwrite existing files", false).option("--agent <name>", "Default agent to configure", "claude").option("--no-devcontainer", "Skip devcontainer creation").option("--mcp <servers>", "MCP servers to enable (comma-separated)", "context7,tavily").option("--no-mcp", "Skip MCP server configuration").action(async (options) => {
26
- const spinner = ora("Initializing RAPID...").start();
27
- try {
28
- const cwd = process.cwd();
29
- const configPath = join(cwd, "rapid.json");
30
- if (!options.force) {
31
- try {
32
- await access(configPath);
33
- spinner.fail("rapid.json already exists. Use --force to overwrite.");
34
- process.exit(1);
35
- } catch {
36
- }
37
- }
38
- const mcpServers = options.mcp === false ? [] : options.mcp.split(",").map((s) => s.trim());
39
- let config = createConfig(options);
40
- if (mcpServers.length > 0) {
41
- spinner.text = "Configuring MCP servers...";
42
- for (const serverName of mcpServers) {
43
- if (MCP_SERVER_TEMPLATES[serverName]) {
44
- config = addMcpServerFromTemplate(config, serverName);
45
- } else {
46
- logger.warn(`Unknown MCP server template: ${serverName}`);
47
- }
48
- }
49
- const secretRefs = getSecretReferences(mcpServers);
50
- if (Object.keys(secretRefs).length > 0) {
51
- config.secrets = {
52
- ...config.secrets,
53
- provider: "1password",
54
- vault: "Development",
55
- items: {
56
- ...config.secrets?.items,
57
- ...secretRefs
58
- }
59
- };
60
- }
61
- }
62
- spinner.text = "Writing rapid.json...";
63
- await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
64
- if (mcpServers.length > 0) {
65
- spinner.text = "Generating MCP configuration files...";
66
- await writeMcpConfig(cwd, config);
67
- await writeOpenCodeConfig(cwd, config);
68
- }
69
- if (config.agents.available.claude) {
70
- spinner.text = "Creating CLAUDE.md...";
71
- const claudeMdPath = join(cwd, "CLAUDE.md");
72
- await writeFile(claudeMdPath, getClaudeMdTemplate(cwd));
73
- }
74
- spinner.text = "Creating AGENTS.md...";
75
- const agentsMdPath = join(cwd, "AGENTS.md");
76
- await writeFile(agentsMdPath, getAgentsMdTemplate(cwd));
77
- spinner.succeed("RAPID initialized successfully!");
78
- logger.blank();
79
- logger.info("Created files:");
80
- console.log(` ${logger.dim("\u2022")} rapid.json`);
81
- if (mcpServers.length > 0) {
82
- console.log(` ${logger.dim("\u2022")} .mcp.json`);
83
- console.log(` ${logger.dim("\u2022")} opencode.json`);
84
- }
85
- console.log(` ${logger.dim("\u2022")} CLAUDE.md`);
86
- console.log(` ${logger.dim("\u2022")} AGENTS.md`);
87
- if (mcpServers.length > 0) {
88
- logger.blank();
89
- logger.info("MCP servers configured:");
90
- for (const serverName of mcpServers) {
91
- const template = MCP_SERVER_TEMPLATES[serverName];
92
- if (template) {
93
- console.log(` ${logger.brand("\u2022")} ${serverName} - ${template.description}`);
94
- }
95
- }
96
- }
97
- logger.blank();
98
- logger.info("Next steps:");
99
- console.log(` ${logger.dim("1.")} Run ${logger.brand("rapid dev")} to start coding`);
100
- console.log(` ${logger.dim("2.")} Edit ${logger.dim("rapid.json")} to customize your setup`);
101
- if (mcpServers.length > 0) {
102
- console.log(
103
- ` ${logger.dim("3.")} Add API keys to ${logger.dim("secrets.items")} in rapid.json`
104
- );
105
- }
106
- logger.blank();
107
- } catch (error) {
108
- spinner.fail("Failed to initialize RAPID");
109
- logger.error(error instanceof Error ? error.message : String(error));
110
- process.exit(1);
111
- }
112
- });
113
- function createConfig(options) {
114
- const defaults = getDefaultConfig();
115
- return {
116
- $schema: "https://getrapid.dev/schema/v1/rapid.json",
117
- version: "1.0",
118
- agents: {
119
- default: options.agent,
120
- available: defaults.agents.available
121
- },
122
- secrets: {
123
- provider: "env"
124
- },
125
- context: {
126
- files: ["README.md", "CLAUDE.md", "AGENTS.md"],
127
- generateAgentFiles: false
128
- // We already created them
129
- }
130
- };
131
- }
132
- function getClaudeMdTemplate(projectPath) {
133
- const projectName = projectPath.split("/").pop() || "project";
134
- return `# Claude Instructions
135
-
136
- ## Project: ${projectName}
137
-
138
- This file contains instructions for Claude Code when working on this project.
139
-
140
- ## Overview
141
-
142
- <!-- Describe your project here -->
143
-
144
- ${RAPID_METHODOLOGY}
145
- ${MCP_USAGE_GUIDELINES}
146
- ${GIT_GUIDELINES}
147
- ## Key Files
148
-
149
- - \`rapid.json\` - RAPID configuration
150
- - \`README.md\` - Project documentation
151
-
152
- ## Commands
153
-
154
- \`\`\`bash
155
- # Start development
156
- rapid dev
157
-
158
- # Check status
159
- rapid status
160
- \`\`\`
161
- `;
162
- }
163
- function getAgentsMdTemplate(projectPath) {
164
- const projectName = projectPath.split("/").pop() || "project";
165
- return `# Agent Instructions
166
-
167
- ## Project: ${projectName}
168
-
169
- This file contains instructions for AI coding agents working on this project.
170
-
171
- ## Overview
172
-
173
- <!-- Describe your project here -->
174
-
175
- ${RAPID_METHODOLOGY}
176
- ${MCP_USAGE_GUIDELINES}
177
- ${GIT_GUIDELINES}
178
- ## Project Structure
179
-
180
- \`\`\`
181
- .
182
- \u251C\u2500\u2500 rapid.json # RAPID configuration
183
- \u251C\u2500\u2500 CLAUDE.md # Claude-specific instructions
184
- \u251C\u2500\u2500 AGENTS.md # Generic agent instructions
185
- \u2514\u2500\u2500 ...
186
- \`\`\`
187
-
188
- ## Getting Started
189
-
190
- 1. Review the project structure
191
- 2. Check \`rapid.json\` for configuration
192
- 3. Follow the RAPID methodology above when making changes
193
- `;
194
- }
195
-
196
- // src/commands/dev.ts
197
- import { Command as Command2 } from "commander";
198
- import { writeFile as writeFile2 } from "fs/promises";
199
- import { isAbsolute, join as join2 } from "path";
200
- import {
201
- loadConfig,
202
- getAgent,
203
- checkAgentAvailable,
204
- logger as logger2,
205
- getContainerStatus,
206
- startContainer,
207
- execInContainer,
208
- hasDevcontainerCli,
209
- loadSecrets,
210
- hasOpCli,
211
- isOpAuthenticated
212
- } from "@a3t/rapid-core";
213
- import ora2 from "ora";
214
- var devCommand = new Command2("dev").description("Launch AI coding session in the dev container").option("-a, --agent <name>", "Agent to use").option("--list", "List available agents without launching").option("--local", "Run locally instead of in container (not recommended)").option("--no-start", "Do not auto-start container if stopped").action(async (options) => {
215
- try {
216
- const spinner = ora2("Loading configuration...").start();
217
- const loaded = await loadConfig();
218
- if (!loaded) {
219
- spinner.fail("No rapid.json found. Run `rapid init` first.");
220
- process.exit(1);
221
- }
222
- const { config, rootDir } = loaded;
223
- spinner.succeed("Configuration loaded");
224
- if (options.list) {
225
- listAgents(config);
226
- return;
227
- }
228
- const agentName = options.agent || config.agents.default;
229
- const agent = getAgent(config, agentName);
230
- if (!agent) {
231
- logger2.error(`Agent "${agentName}" not found in configuration`);
232
- logger2.info("Available agents:");
233
- Object.keys(config.agents.available).forEach((name) => {
234
- const isDefault = name === config.agents.default;
235
- console.log(` ${isDefault ? "* " : " "}${name}${isDefault ? " (default)" : ""}`);
236
- });
237
- process.exit(1);
238
- }
239
- if (options.local) {
240
- logger2.warn("Running locally instead of in container");
241
- logger2.dim("This bypasses the isolated dev environment");
242
- logger2.blank();
243
- await runLocally(agent, agentName, rootDir);
244
- return;
245
- }
246
- const hasDevCli = await hasDevcontainerCli();
247
- if (!hasDevCli) {
248
- logger2.error("devcontainer CLI not found");
249
- logger2.info("Install with: npm install -g @devcontainers/cli");
250
- logger2.blank();
251
- logger2.info("Or use --local to run without container (not recommended)");
252
- process.exit(1);
253
- }
254
- spinner.start("Checking container status...");
255
- const status = await getContainerStatus(rootDir, config);
256
- if (!status.running) {
257
- if (options.start === false) {
258
- spinner.fail("Container not running. Use `rapid start` first.");
259
- process.exit(1);
260
- }
261
- spinner.text = "Starting container...";
262
- spinner.stopAndPersist({ symbol: "\u{1F433}", text: "Starting container..." });
263
- const result = await startContainer(rootDir, config, { quiet: false });
264
- if (!result.success) {
265
- logger2.blank();
266
- logger2.error("Failed to start container");
267
- logger2.error(result.error || "Unknown error");
268
- process.exit(1);
269
- }
270
- logger2.blank();
271
- } else {
272
- spinner.succeed(`Container running (${status.containerName})`);
273
- }
274
- let secrets = {};
275
- if (config.secrets?.provider === "1password" && config.secrets.items) {
276
- spinner.start("Loading secrets from 1Password...");
277
- const hasOp = await hasOpCli();
278
- if (!hasOp) {
279
- spinner.warn("1Password CLI not found - secrets will not be loaded");
280
- logger2.info("Install with: brew install 1password-cli");
281
- } else {
282
- const authenticated = await isOpAuthenticated();
283
- if (!authenticated) {
284
- spinner.warn("1Password not authenticated - secrets will not be loaded");
285
- logger2.info("Run: eval $(op signin)");
286
- } else {
287
- try {
288
- secrets = await loadSecrets(config.secrets);
289
- const count = Object.keys(secrets).length;
290
- spinner.succeed(`Loaded ${count} secret${count !== 1 ? "s" : ""} from 1Password`);
291
- } catch (err) {
292
- spinner.warn("Failed to load secrets from 1Password");
293
- logger2.debug(err instanceof Error ? err.message : String(err));
294
- }
295
- }
296
- }
297
- }
298
- logger2.blank();
299
- logger2.info(`Launching ${logger2.brand(agentName)} in container...`);
300
- logger2.blank();
301
- const agentArgs = [agent.cli, ...agent.args || []];
302
- const mcpEnv = await prepareMcpEnv(rootDir, config.mcp);
303
- const mergedEnv = { ...secrets, ...mcpEnv ?? {} };
304
- await execInContainer(rootDir, agentArgs, config, {
305
- interactive: true,
306
- tty: true,
307
- env: mergedEnv
308
- });
309
- } catch (error) {
310
- logger2.error(error instanceof Error ? error.message : String(error));
311
- process.exit(1);
312
- }
313
- });
314
- async function prepareMcpEnv(rootDir, mcp) {
315
- if (!mcp?.servers || Object.keys(mcp.servers).length === 0) {
316
- return void 0;
317
- }
318
- const configFile = mcp.configFile ?? ".mcp.json";
319
- const configPath = isAbsolute(configFile) ? configFile : join2(rootDir, configFile);
320
- const servers = {};
321
- for (const [name, serverConfig] of Object.entries(mcp.servers)) {
322
- if (!serverConfig || typeof serverConfig !== "object") {
323
- continue;
324
- }
325
- const { enabled, ...rest } = serverConfig;
326
- if (enabled === false) {
327
- continue;
328
- }
329
- servers[name] = rest;
330
- }
331
- if (Object.keys(servers).length === 0) {
332
- return void 0;
333
- }
334
- await writeFile2(configPath, `${JSON.stringify({ servers }, null, 2)}
335
- `, "utf-8");
336
- return {
337
- MCP_CONFIG_FILE: configFile
338
- };
339
- }
340
- async function runLocally(agent, agentName, rootDir) {
341
- const { execa } = await import("execa");
342
- const status = await checkAgentAvailable(agent);
343
- if (!status.available) {
344
- logger2.error(`${agentName} CLI not found locally`);
345
- process.exit(1);
346
- }
347
- logger2.info(`Launching ${logger2.brand(agentName)}...`);
348
- logger2.dim(`Working directory: ${rootDir}`);
349
- logger2.blank();
350
- await execa(agent.cli, agent.args || [], {
351
- cwd: rootDir,
352
- stdio: "inherit"
353
- });
354
- }
355
- function listAgents(config) {
356
- logger2.header("Available Agents");
357
- Object.keys(config.agents.available).forEach((name) => {
358
- const isDefault = name === config.agents.default;
359
- console.log(
360
- ` ${isDefault ? logger2.brand("*") : " "} ${name}${isDefault ? logger2.dim(" (default)") : ""}`
361
- );
362
- });
363
- logger2.blank();
364
- logger2.dim("Use --agent <name> to select a specific agent");
365
- }
366
-
367
- // src/commands/status.ts
368
- import { Command as Command3 } from "commander";
369
- import {
370
- loadConfig as loadConfig2,
371
- checkAllAgents,
372
- logger as logger3,
373
- getContainerStatus as getContainerStatus2,
374
- hasDevcontainerCli as hasDevcontainerCli2,
375
- hasDocker,
376
- loadDevcontainerConfig,
377
- verifySecrets,
378
- hasOpCli as hasOpCli2,
379
- hasVaultCli,
380
- isOpAuthenticated as isOpAuthenticated2,
381
- isVaultAuthenticated,
382
- hasEnvrc,
383
- getProviderInfo,
384
- getAuthStatus
385
- } from "@a3t/rapid-core";
386
- import ora3 from "ora";
387
- var statusCommand = new Command3("status").description("Show environment status").option("--json", "Output as JSON").action(async (options) => {
388
- try {
389
- const spinner = ora3("Checking status...").start();
390
- const loaded = await loadConfig2();
391
- if (!loaded) {
392
- spinner.fail("No rapid.json found");
393
- if (options.json) {
394
- console.log(JSON.stringify({ configured: false }, null, 2));
395
- }
396
- process.exit(1);
397
- }
398
- const { config, filepath, rootDir } = loaded;
399
- spinner.text = "Checking container...";
400
- const containerStatus = await getContainerStatus2(rootDir, config);
401
- const devcontainerConfig = await loadDevcontainerConfig(rootDir, config);
402
- const hasDevCli = await hasDevcontainerCli2();
403
- const dockerRunning = await hasDocker();
404
- spinner.text = "Checking agents...";
405
- const agentStatuses = await checkAllAgents(config);
406
- spinner.text = "Checking secrets...";
407
- const secretsConfig = config.secrets;
408
- const provider = secretsConfig?.provider || "env";
409
- let secretsStatus = null;
410
- if (secretsConfig) {
411
- let cliInstalled = true;
412
- let authenticated = true;
413
- if (provider === "1password") {
414
- cliInstalled = await hasOpCli2();
415
- authenticated = cliInstalled && await isOpAuthenticated2();
416
- } else if (provider === "vault") {
417
- cliInstalled = await hasVaultCli();
418
- authenticated = cliInstalled && await isVaultAuthenticated();
419
- }
420
- const envrcExists = await hasEnvrc(rootDir, secretsConfig);
421
- const verified = await verifySecrets(secretsConfig);
422
- secretsStatus = {
423
- provider,
424
- cliInstalled,
425
- authenticated,
426
- envrcExists,
427
- secretsCount: secretsConfig.items ? Object.keys(secretsConfig.items).length : 0,
428
- allAvailable: verified.allAvailable
429
- };
430
- }
431
- spinner.text = "Checking authentication...";
432
- const authStatus = await getAuthStatus();
433
- spinner.stop();
434
- if (options.json) {
435
- console.log(
436
- JSON.stringify(
437
- {
438
- configured: true,
439
- configPath: filepath,
440
- rootDir,
441
- defaultAgent: config.agents.default,
442
- container: {
443
- configured: !!devcontainerConfig,
444
- running: containerStatus.running,
445
- name: containerStatus.containerName,
446
- devcontainerCli: hasDevCli,
447
- docker: dockerRunning
448
- },
449
- agents: agentStatuses,
450
- secrets: secretsStatus,
451
- auth: {
452
- authenticated: authStatus.authenticated,
453
- sources: authStatus.sources.map((s) => ({
454
- source: s.source,
455
- provider: s.provider,
456
- authType: s.authType,
457
- hasValue: !!s.value
458
- }))
459
- }
460
- },
461
- null,
462
- 2
463
- )
464
- );
465
- return;
466
- }
467
- console.log();
468
- console.log(` ${logger3.brand("RAPID")} Status`);
469
- console.log(` ${logger3.dim("\u2500".repeat(24))}`);
470
- console.log();
471
- console.log(` ${logger3.dim("Config:")} ${filepath}`);
472
- console.log(` ${logger3.dim("Root:")} ${rootDir}`);
473
- console.log();
474
- console.log(` ${logger3.dim("Container:")}`);
475
- if (!devcontainerConfig) {
476
- console.log(` ${logger3.dim("\u25CB")} ${logger3.dim("No devcontainer.json configured")}`);
477
- } else if (!dockerRunning) {
478
- console.log(` ${logger3.dim("\u25CB")} ${logger3.dim("Docker not running")}`);
479
- } else if (!hasDevCli) {
480
- console.log(` ${logger3.dim("\u25CB")} ${logger3.dim("devcontainer CLI not installed")}`);
481
- } else if (containerStatus.running) {
482
- console.log(
483
- ` ${logger3.brand("\u25CF")} Running ${logger3.dim(`(${containerStatus.containerName})`)}`
484
- );
485
- } else if (containerStatus.exists) {
486
- console.log(
487
- ` ${logger3.dim("\u25CB")} Stopped ${logger3.dim(`(${containerStatus.containerName})`)}`
488
- );
489
- } else {
490
- console.log(` ${logger3.dim("\u25CB")} Not started`);
491
- }
492
- console.log();
493
- console.log(
494
- ` ${logger3.dim("Agents:")} ${logger3.dim(`(default: ${config.agents.default})`)}`
495
- );
496
- agentStatuses.forEach((status) => {
497
- const isDefault = status.name === config.agents.default;
498
- const icon = status.available ? logger3.brand("\u2713") : logger3.dim("\u25CB");
499
- const name = isDefault ? logger3.bold(status.name) : status.name;
500
- const version = status.version ? logger3.dim(` (${status.version})`) : "";
501
- console.log(` ${icon} ${name}${version}`);
502
- });
503
- console.log();
504
- if (secretsStatus) {
505
- const providerInfo = getProviderInfo(
506
- secretsStatus.provider
507
- );
508
- console.log(` ${logger3.dim("Secrets:")} ${logger3.dim(`(${providerInfo.name})`)}`);
509
- if (providerInfo.cliRequired) {
510
- const cliIcon = secretsStatus.cliInstalled ? logger3.brand("\u2713") : logger3.dim("\u25CB");
511
- console.log(
512
- ` ${cliIcon} CLI ${secretsStatus.cliInstalled ? "installed" : "not installed"}`
513
- );
514
- if (secretsStatus.cliInstalled) {
515
- const authIcon = secretsStatus.authenticated ? logger3.brand("\u2713") : logger3.dim("\u25CB");
516
- console.log(
517
- ` ${authIcon} ${secretsStatus.authenticated ? "Authenticated" : "Not authenticated"}`
518
- );
519
- }
520
- }
521
- if (secretsStatus.secretsCount > 0) {
522
- const allIcon = secretsStatus.allAvailable ? logger3.brand("\u2713") : logger3.dim("\u25CB");
523
- console.log(
524
- ` ${allIcon} ${secretsStatus.secretsCount} secret${secretsStatus.secretsCount !== 1 ? "s" : ""} ${secretsStatus.allAvailable ? "available" : "configured"}`
525
- );
526
- }
527
- const envrcIcon = secretsStatus.envrcExists ? logger3.brand("\u2713") : logger3.dim("\u25CB");
528
- console.log(
529
- ` ${envrcIcon} .envrc ${secretsStatus.envrcExists ? "exists" : "not generated"}`
530
- );
531
- console.log();
532
- }
533
- console.log(` ${logger3.dim("Auth:")}`);
534
- if (!authStatus.authenticated) {
535
- console.log(` ${logger3.dim("\u25CB")} ${logger3.dim("No authentication detected")}`);
536
- console.log(` ${logger3.dim(" Run `rapid auth` for options")}`);
537
- } else {
538
- for (const cred of authStatus.sources) {
539
- const icon = cred.authType === "oauth" ? logger3.brand("\u25CF") : logger3.dim("\u25CB");
540
- const authType = cred.authType === "oauth" ? "OAuth" : "API Key";
541
- let info = `${cred.source} (${cred.provider}, ${authType})`;
542
- if (cred.accountInfo?.email) {
543
- info += ` - ${cred.accountInfo.email}`;
544
- }
545
- console.log(` ${icon} ${info}`);
546
- }
547
- }
548
- console.log();
549
- if (!containerStatus.running && devcontainerConfig && dockerRunning && hasDevCli) {
550
- logger3.info("Run `rapid start` to start the container");
551
- } else if (containerStatus.running) {
552
- logger3.info("Run `rapid dev` to start coding");
553
- }
554
- console.log();
555
- } catch (error) {
556
- logger3.error(error instanceof Error ? error.message : String(error));
557
- process.exit(1);
558
- }
559
- });
560
-
561
- // src/commands/agent.ts
562
- import { Command as Command4 } from "commander";
563
- import { loadConfig as loadConfig3, checkAllAgents as checkAllAgents2, logger as logger4 } from "@a3t/rapid-core";
564
- var agentCommand = new Command4("agent").description("Manage AI agents");
565
- agentCommand.command("list").description("List available agents").action(async () => {
566
- try {
567
- const loaded = await loadConfig3();
568
- if (!loaded) {
569
- logger4.error("No rapid.json found. Run `rapid init` first.");
570
- process.exit(1);
571
- }
572
- const { config } = loaded;
573
- const statuses = await checkAllAgents2(config);
574
- logger4.header("Available Agents");
575
- statuses.forEach((status) => {
576
- const isDefault = status.name === config.agents.default;
577
- const icon = status.available ? "\u2713" : "\u25CB";
578
- const defaultTag = isDefault ? " (default)" : "";
579
- const versionTag = status.version ? ` - ${status.version}` : "";
580
- if (status.available) {
581
- console.log(
582
- ` ${logger4.brand(icon)} ${status.name}${defaultTag}${logger4.dim(versionTag)}`
583
- );
584
- } else {
585
- console.log(
586
- ` ${logger4.dim(icon)} ${logger4.dim(status.name)}${defaultTag} ${logger4.dim("[not installed]")}`
587
- );
588
- }
589
- });
590
- logger4.blank();
591
- } catch (error) {
592
- logger4.error(error instanceof Error ? error.message : String(error));
593
- process.exit(1);
594
- }
595
- });
596
- agentCommand.command("default [name]").description("Get or set default agent").action(async (name) => {
597
- try {
598
- const loaded = await loadConfig3();
599
- if (!loaded) {
600
- logger4.error("No rapid.json found. Run `rapid init` first.");
601
- process.exit(1);
602
- }
603
- const { config } = loaded;
604
- if (!name) {
605
- console.log(config.agents.default);
606
- return;
607
- }
608
- if (!config.agents.available[name]) {
609
- logger4.error(`Agent "${name}" not found in configuration`);
610
- logger4.info("Available agents:");
611
- Object.keys(config.agents.available).forEach((n) => {
612
- console.log(` - ${n}`);
613
- });
614
- process.exit(1);
615
- }
616
- logger4.warn("Setting default agent requires editing rapid.json");
617
- logger4.info(`Set "agents.default" to "${name}" in your rapid.json`);
618
- } catch (error) {
619
- logger4.error(error instanceof Error ? error.message : String(error));
620
- process.exit(1);
621
- }
622
- });
623
-
624
- // src/commands/start.ts
625
- import { Command as Command5 } from "commander";
626
- import {
627
- loadConfig as loadConfig4,
628
- logger as logger5,
629
- hasDevcontainerCli as hasDevcontainerCli3,
630
- hasDocker as hasDocker2,
631
- loadDevcontainerConfig as loadDevcontainerConfig2,
632
- getContainerStatus as getContainerStatus3,
633
- startContainer as startContainer2
634
- } from "@a3t/rapid-core";
635
- import ora4 from "ora";
636
- var startCommand = new Command5("start").description("Start the development container").option("--rebuild", "Force rebuild the container", false).option("--no-cache", "Build without Docker cache", false).action(async (options) => {
637
- const spinner = ora4("Starting development environment...").start();
638
- try {
639
- spinner.text = "Loading configuration...";
640
- const loaded = await loadConfig4();
641
- if (!loaded) {
642
- spinner.fail("No rapid.json found. Run `rapid init` first.");
643
- process.exit(1);
644
- }
645
- const { config, rootDir } = loaded;
646
- spinner.text = "Checking devcontainer CLI...";
647
- const hasDevCli = await hasDevcontainerCli3();
648
- if (!hasDevCli) {
649
- spinner.fail("devcontainer CLI not found");
650
- logger5.blank();
651
- logger5.info("Install with:");
652
- console.log(" npm install -g @devcontainers/cli");
653
- logger5.blank();
654
- process.exit(1);
655
- }
656
- spinner.text = "Checking Docker...";
657
- const dockerAvailable = await hasDocker2();
658
- if (!dockerAvailable) {
659
- spinner.fail("Docker is not running");
660
- logger5.info("Please start Docker Desktop and try again.");
661
- process.exit(1);
662
- }
663
- spinner.text = "Checking devcontainer configuration...";
664
- const devcontainerConfig = await loadDevcontainerConfig2(rootDir, config);
665
- if (!devcontainerConfig) {
666
- spinner.fail("No devcontainer.json found");
667
- logger5.blank();
668
- logger5.info("Create a .devcontainer/devcontainer.json or run:");
669
- console.log(" rapid init --template <template>");
670
- logger5.blank();
671
- process.exit(1);
672
- }
673
- spinner.text = "Checking container status...";
674
- const status = await getContainerStatus3(rootDir, config);
675
- if (status.running && !options.rebuild) {
676
- spinner.succeed("Container already running");
677
- logger5.info(`Container: ${status.containerName}`);
678
- logger5.blank();
679
- logger5.info("Run `rapid dev` to start coding");
680
- return;
681
- }
682
- spinner.text = options.rebuild ? "Rebuilding container..." : "Starting container...";
683
- spinner.stopAndPersist({ symbol: "\u{1F433}", text: spinner.text });
684
- const result = await startContainer2(rootDir, config, {
685
- rebuild: options.rebuild,
686
- quiet: false
687
- });
688
- if (!result.success) {
689
- logger5.blank();
690
- logger5.error("Failed to start container");
691
- logger5.error(result.error || "Unknown error");
692
- process.exit(1);
693
- }
694
- logger5.blank();
695
- logger5.success("Development environment ready!");
696
- logger5.blank();
697
- logger5.info("Next steps:");
698
- console.log(` ${logger5.dim("\u2022")} Run ${logger5.brand("rapid dev")} to start coding`);
699
- console.log(` ${logger5.dim("\u2022")} Run ${logger5.brand("rapid stop")} when done`);
700
- logger5.blank();
701
- } catch (error) {
702
- spinner.fail("Failed to start environment");
703
- logger5.error(error instanceof Error ? error.message : String(error));
704
- process.exit(1);
705
- }
706
- });
707
-
708
- // src/commands/stop.ts
709
- import { Command as Command6 } from "commander";
710
- import { loadConfig as loadConfig5, logger as logger6, getContainerStatus as getContainerStatus4, stopContainer } from "@a3t/rapid-core";
711
- import ora5 from "ora";
712
- var stopCommand = new Command6("stop").description("Stop the development container").option("--remove", "Remove container after stopping", false).action(async (options) => {
713
- const spinner = ora5("Stopping development environment...").start();
714
- try {
715
- const loaded = await loadConfig5();
716
- if (!loaded) {
717
- spinner.fail("No rapid.json found");
718
- process.exit(1);
719
- }
720
- const { config, rootDir } = loaded;
721
- spinner.text = "Checking container status...";
722
- const status = await getContainerStatus4(rootDir, config);
723
- if (!status.exists) {
724
- spinner.succeed("No container to stop");
725
- return;
726
- }
727
- if (!status.running) {
728
- if (options.remove) {
729
- spinner.text = "Removing container...";
730
- await stopContainer(rootDir, config, { remove: true });
731
- spinner.succeed("Container removed");
732
- } else {
733
- spinner.succeed("Container already stopped");
734
- }
735
- return;
736
- }
737
- spinner.text = "Stopping container...";
738
- const result = await stopContainer(rootDir, config, { remove: options.remove });
739
- if (!result.success) {
740
- spinner.fail("Failed to stop container");
741
- logger6.error(result.error || "Unknown error");
742
- process.exit(1);
743
- }
744
- spinner.succeed(options.remove ? "Container stopped and removed" : "Container stopped");
745
- } catch (error) {
746
- spinner.fail("Failed to stop environment");
747
- logger6.error(error instanceof Error ? error.message : String(error));
748
- process.exit(1);
749
- }
750
- });
751
-
752
- // src/commands/secrets.ts
753
- import { Command as Command7 } from "commander";
754
- import {
755
- loadConfig as loadConfig6,
756
- logger as logger7,
757
- verifySecrets as verifySecrets2,
758
- loadSecrets as loadSecrets2,
759
- hasOpCli as hasOpCli3,
760
- hasVaultCli as hasVaultCli2,
761
- isOpAuthenticated as isOpAuthenticated3,
762
- isVaultAuthenticated as isVaultAuthenticated2,
763
- getOpAuthStatus,
764
- hasOpServiceAccountToken,
765
- generateEnvrc,
766
- writeEnvrc,
767
- hasEnvrc as hasEnvrc2,
768
- getProviderInfo as getProviderInfo2
769
- } from "@a3t/rapid-core";
770
- import ora6 from "ora";
771
- var secretsCommand = new Command7("secrets").description("Manage project secrets");
772
- secretsCommand.command("verify").description("Verify all secrets are accessible").option("--json", "Output as JSON").action(async (options) => {
773
- const spinner = ora6("Verifying secrets...").start();
774
- try {
775
- const loaded = await loadConfig6();
776
- if (!loaded) {
777
- spinner.fail("No rapid.json found");
778
- process.exit(1);
779
- }
780
- const { config } = loaded;
781
- const secretsConfig = config.secrets;
782
- if (!secretsConfig) {
783
- spinner.info("No secrets configured");
784
- return;
785
- }
786
- const provider = secretsConfig.provider || "env";
787
- spinner.text = `Checking ${provider} availability...`;
788
- if (provider === "1password") {
789
- const hasOp = await hasOpCli3();
790
- if (!hasOp) {
791
- spinner.fail("1Password CLI (op) not found");
792
- console.log();
793
- logger7.info("Install with: brew install 1password-cli");
794
- logger7.info("More info: https://developer.1password.com/docs/cli/get-started/");
795
- process.exit(1);
796
- }
797
- const authenticated = await isOpAuthenticated3();
798
- if (!authenticated) {
799
- spinner.fail("1Password CLI not authenticated");
800
- console.log();
801
- logger7.info("Run: eval $(op signin)");
802
- process.exit(1);
803
- }
804
- } else if (provider === "vault") {
805
- const hasVault = await hasVaultCli2();
806
- if (!hasVault) {
807
- spinner.fail("Vault CLI not found");
808
- console.log();
809
- logger7.info("Install from: https://developer.hashicorp.com/vault/docs/install");
810
- process.exit(1);
811
- }
812
- const authenticated = await isVaultAuthenticated2();
813
- if (!authenticated) {
814
- spinner.fail("Vault CLI not authenticated");
815
- console.log();
816
- logger7.info("Run: vault login");
817
- process.exit(1);
818
- }
819
- }
820
- spinner.text = "Verifying secrets...";
821
- const status = await verifySecrets2(secretsConfig);
822
- spinner.stop();
823
- if (options.json) {
824
- console.log(JSON.stringify(status, null, 2));
825
- return;
826
- }
827
- console.log();
828
- console.log(` ${logger7.brand("Secrets")} Verification`);
829
- console.log(` ${logger7.dim("\u2500".repeat(24))}`);
830
- console.log();
831
- console.log(` ${logger7.dim("Provider:")} ${getProviderInfo2(provider).name}`);
832
- console.log(
833
- ` ${logger7.dim("Auth:")} ${status.authenticated ? logger7.brand("\u2713") : logger7.dim("\u25CB")} ${status.authenticated ? "Authenticated" : "Not authenticated"}`
834
- );
835
- console.log();
836
- if (status.secrets.length === 0) {
837
- console.log(` ${logger7.dim("No secrets configured in rapid.json")}`);
838
- console.log();
839
- return;
840
- }
841
- console.log(` ${logger7.dim("Secrets:")}`);
842
- for (const secret of status.secrets) {
843
- const icon = secret.available ? logger7.brand("\u2713") : "\u2717";
844
- const ref = logger7.dim(`(${provider})`);
845
- const error = secret.error ? logger7.dim(` - ${secret.error}`) : "";
846
- console.log(` ${icon} ${secret.name} ${ref}${error}`);
847
- }
848
- console.log();
849
- if (status.allAvailable) {
850
- logger7.info("All secrets verified successfully!");
851
- } else {
852
- logger7.error("Some secrets are not available");
853
- process.exit(1);
854
- }
855
- console.log();
856
- } catch (error) {
857
- spinner.fail("Failed to verify secrets");
858
- logger7.error(error instanceof Error ? error.message : String(error));
859
- process.exit(1);
860
- }
861
- });
862
- secretsCommand.command("list").description("List configured secrets (names only, not values)").option("--json", "Output as JSON").action(async (options) => {
863
- try {
864
- const loaded = await loadConfig6();
865
- if (!loaded) {
866
- logger7.error("No rapid.json found");
867
- process.exit(1);
868
- }
869
- const { config } = loaded;
870
- const secretsConfig = config.secrets;
871
- if (!secretsConfig || !secretsConfig.items || Object.keys(secretsConfig.items).length === 0) {
872
- if (options.json) {
873
- console.log(JSON.stringify({ secrets: [] }, null, 2));
874
- } else {
875
- logger7.info("No secrets configured");
876
- }
877
- return;
878
- }
879
- const provider = secretsConfig.provider || "env";
880
- if (options.json) {
881
- const secrets = Object.entries(secretsConfig.items).map(([name, reference]) => ({
882
- name,
883
- reference,
884
- provider
885
- }));
886
- console.log(JSON.stringify({ provider, secrets }, null, 2));
887
- return;
888
- }
889
- console.log();
890
- console.log(` ${logger7.brand("Configured Secrets")}`);
891
- console.log(` ${logger7.dim("\u2500".repeat(24))}`);
892
- console.log();
893
- console.log(` ${logger7.dim("Provider:")} ${getProviderInfo2(provider).name}`);
894
- console.log();
895
- const maxNameLen = Math.max(...Object.keys(secretsConfig.items).map((n) => n.length));
896
- for (const [name, reference] of Object.entries(secretsConfig.items)) {
897
- const paddedName = name.padEnd(maxNameLen);
898
- console.log(` ${logger7.brand("\u2022")} ${paddedName} ${logger7.dim(reference)}`);
899
- }
900
- console.log();
901
- } catch (error) {
902
- logger7.error(error instanceof Error ? error.message : String(error));
903
- process.exit(1);
904
- }
905
- });
906
- secretsCommand.command("generate").description("Generate .envrc file from rapid.json configuration").option("--force", "Overwrite existing .envrc", false).option("--stdout", "Print to stdout instead of writing file", false).action(async (options) => {
907
- try {
908
- const loaded = await loadConfig6();
909
- if (!loaded) {
910
- logger7.error("No rapid.json found");
911
- process.exit(1);
912
- }
913
- const { config, rootDir } = loaded;
914
- const secretsConfig = config.secrets;
915
- if (!secretsConfig) {
916
- logger7.error("No secrets configuration in rapid.json");
917
- process.exit(1);
918
- }
919
- const content = generateEnvrc(secretsConfig);
920
- if (options.stdout) {
921
- console.log(content);
922
- return;
923
- }
924
- const envrcExists = await hasEnvrc2(rootDir, secretsConfig);
925
- if (envrcExists && !options.force) {
926
- logger7.error(".envrc already exists. Use --force to overwrite.");
927
- process.exit(1);
928
- }
929
- const spinner = ora6("Generating .envrc...").start();
930
- const filepath = await writeEnvrc(rootDir, secretsConfig);
931
- spinner.succeed("Generated .envrc");
932
- console.log();
933
- console.log(` ${logger7.dim("File:")} ${filepath}`);
934
- console.log();
935
- const itemCount = secretsConfig.items ? Object.keys(secretsConfig.items).length : 0;
936
- logger7.info(`Generated .envrc with ${itemCount} secret${itemCount !== 1 ? "s" : ""}`);
937
- logger7.info(`Run ${logger7.brand("direnv allow")} to activate`);
938
- console.log();
939
- } catch (error) {
940
- logger7.error(error instanceof Error ? error.message : String(error));
941
- process.exit(1);
942
- }
943
- });
944
- secretsCommand.command("info").description("Show secrets provider information and authentication status").option("--json", "Output as JSON").action(async (options) => {
945
- const spinner = ora6("Checking provider status...").start();
946
- try {
947
- const loaded = await loadConfig6();
948
- if (!loaded) {
949
- spinner.fail("No rapid.json found");
950
- process.exit(1);
951
- }
952
- const { config, rootDir } = loaded;
953
- const secretsConfig = config.secrets || { provider: "env" };
954
- const provider = secretsConfig.provider || "env";
955
- const info = getProviderInfo2(provider);
956
- let cliInstalled = true;
957
- let authenticated = true;
958
- let opAuthStatus = null;
959
- if (provider === "1password") {
960
- cliInstalled = await hasOpCli3();
961
- if (cliInstalled) {
962
- opAuthStatus = await getOpAuthStatus();
963
- authenticated = opAuthStatus.authenticated;
964
- } else {
965
- authenticated = false;
966
- }
967
- } else if (provider === "vault") {
968
- cliInstalled = await hasVaultCli2();
969
- authenticated = cliInstalled && await isVaultAuthenticated2();
970
- }
971
- const envrcExists = await hasEnvrc2(rootDir, secretsConfig);
972
- const hasServiceToken = hasOpServiceAccountToken();
973
- spinner.stop();
974
- const status = {
975
- provider,
976
- providerName: info.name,
977
- cliRequired: info.cliRequired,
978
- cliInstalled,
979
- authenticated,
980
- authMethod: opAuthStatus?.method,
981
- accountInfo: opAuthStatus?.accountInfo,
982
- hasServiceToken,
983
- envrcExists,
984
- envrcPath: secretsConfig.envrc?.path || ".envrc",
985
- secretsCount: secretsConfig.items ? Object.keys(secretsConfig.items).length : 0
986
- };
987
- if (options.json) {
988
- console.log(JSON.stringify(status, null, 2));
989
- return;
990
- }
991
- console.log();
992
- console.log(` ${logger7.brand("Secrets")} Provider Info`);
993
- console.log(` ${logger7.dim("\u2500".repeat(24))}`);
994
- console.log();
995
- console.log(` ${logger7.dim("Provider:")} ${info.name}`);
996
- if (info.cliRequired) {
997
- const cliIcon = cliInstalled ? logger7.brand("\u2713") : "\u2717";
998
- console.log(
999
- ` ${logger7.dim("CLI:")} ${cliIcon} ${info.cliRequired} ${cliInstalled ? "" : logger7.dim("(not installed)")}`
1000
- );
1001
- if (cliInstalled && provider === "1password" && opAuthStatus) {
1002
- const authIcon = authenticated ? logger7.brand("\u2713") : "\u2717";
1003
- const methodLabel = opAuthStatus.method === "service-account" ? "Service Account" : opAuthStatus.method === "user" ? "User" : "Not authenticated";
1004
- const accountLabel = opAuthStatus.accountInfo ? ` (${opAuthStatus.accountInfo})` : "";
1005
- console.log(` ${logger7.dim("Auth:")} ${authIcon} ${methodLabel}${accountLabel}`);
1006
- if (hasServiceToken) {
1007
- console.log(
1008
- ` ${logger7.dim("Token:")} ${logger7.brand("\u2713")} OP_SERVICE_ACCOUNT_TOKEN set`
1009
- );
1010
- }
1011
- } else if (cliInstalled) {
1012
- const authIcon = authenticated ? logger7.brand("\u2713") : "\u2717";
1013
- console.log(
1014
- ` ${logger7.dim("Auth:")} ${authIcon} ${authenticated ? "Authenticated" : "Not authenticated"}`
1015
- );
1016
- }
1017
- if (info.installUrl && !cliInstalled) {
1018
- console.log();
1019
- console.log(` ${logger7.dim("Install:")} ${info.installUrl}`);
1020
- }
1021
- if (info.authCommand && cliInstalled && !authenticated) {
1022
- console.log();
1023
- console.log(` ${logger7.dim("Authenticate:")} ${info.authCommand}`);
1024
- if (provider === "1password") {
1025
- console.log(
1026
- ` ${logger7.dim("Or set:")} OP_SERVICE_ACCOUNT_TOKEN for non-interactive auth`
1027
- );
1028
- }
1029
- }
1030
- }
1031
- console.log();
1032
- const envrcIcon = envrcExists ? logger7.brand("\u2713") : logger7.dim("\u25CB");
1033
- console.log(
1034
- ` ${logger7.dim(".envrc:")} ${envrcIcon} ${envrcExists ? "Exists" : "Not generated"}`
1035
- );
1036
- console.log(` ${logger7.dim("Secrets:")} ${status.secretsCount} configured`);
1037
- console.log();
1038
- if (!envrcExists && status.secretsCount > 0) {
1039
- logger7.info(`Run ${logger7.brand("rapid secrets generate")} to create .envrc`);
1040
- console.log();
1041
- }
1042
- } catch (error) {
1043
- spinner.fail("Failed to get provider info");
1044
- logger7.error(error instanceof Error ? error.message : String(error));
1045
- process.exit(1);
1046
- }
1047
- });
1048
- secretsCommand.command("run").description("Run a command with secrets loaded into environment").argument("<command...>", "Command to run with secrets").option("--show", "Show which secrets are being loaded (names only)", false).action(async (commandArgs, options) => {
1049
- try {
1050
- const loaded = await loadConfig6();
1051
- if (!loaded) {
1052
- logger7.error("No rapid.json found");
1053
- process.exit(1);
1054
- }
1055
- const { config } = loaded;
1056
- const secretsConfig = config.secrets;
1057
- if (!secretsConfig || !secretsConfig.items) {
1058
- logger7.error("No secrets configured in rapid.json");
1059
- process.exit(1);
1060
- }
1061
- const provider = secretsConfig.provider || "env";
1062
- if (provider === "1password") {
1063
- const hasOp = await hasOpCli3();
1064
- if (!hasOp) {
1065
- logger7.error("1Password CLI (op) not found");
1066
- logger7.info("Install with: brew install 1password-cli");
1067
- process.exit(1);
1068
- }
1069
- const authenticated = await isOpAuthenticated3();
1070
- if (!authenticated) {
1071
- logger7.error("1Password not authenticated");
1072
- logger7.info("Run: eval $(op signin)");
1073
- process.exit(1);
1074
- }
1075
- }
1076
- const secrets = await loadSecrets2(secretsConfig);
1077
- const secretCount = Object.keys(secrets).length;
1078
- if (secretCount === 0) {
1079
- logger7.warn("No secrets were loaded");
1080
- } else if (options.show) {
1081
- logger7.info(`Loaded ${secretCount} secret${secretCount !== 1 ? "s" : ""}:`);
1082
- for (const name of Object.keys(secrets)) {
1083
- console.log(` ${logger7.brand("\u2022")} ${name}`);
1084
- }
1085
- console.log();
1086
- }
1087
- const { execa } = await import("execa");
1088
- const [cmd, ...args] = commandArgs;
1089
- if (!cmd) {
1090
- logger7.error("No command specified");
1091
- process.exit(1);
1092
- }
1093
- await execa(cmd, args, {
1094
- stdio: "inherit",
1095
- env: {
1096
- ...process.env,
1097
- ...secrets
1098
- }
1099
- });
1100
- } catch (error) {
1101
- if (error.exitCode !== void 0) {
1102
- process.exit(error.exitCode);
1103
- }
1104
- logger7.error(error instanceof Error ? error.message : String(error));
1105
- process.exit(1);
1106
- }
1107
- });
1108
-
1109
- // src/commands/auth.ts
1110
- import { Command as Command8 } from "commander";
1111
- import {
1112
- logger as logger8,
1113
- getAuthStatus as getAuthStatus2,
1114
- detectAllCredentials
1115
- } from "@a3t/rapid-core";
1116
- import ora7 from "ora";
1117
- var authCommand = new Command8("auth").description("Show authentication status from external tools").option("--json", "Output as JSON").option("--source <source>", "Filter by source (claude-code, codex, gemini-cli, aider, env)").option("--provider <provider>", "Filter by provider (anthropic, openai, google)").action(async (options) => {
1118
- try {
1119
- const spinner = ora7("Checking authentication...").start();
1120
- const status = await getAuthStatus2();
1121
- spinner.stop();
1122
- if (options.json) {
1123
- let credentials = status.sources;
1124
- if (options.source) {
1125
- credentials = credentials.filter((c) => c.source === options.source);
1126
- }
1127
- if (options.provider) {
1128
- credentials = credentials.filter(
1129
- (c) => c.provider === options.provider
1130
- );
1131
- }
1132
- const sanitized = credentials.map((c) => ({
1133
- source: c.source,
1134
- provider: c.provider,
1135
- authType: c.authType,
1136
- envVar: c.envVar,
1137
- expiresAt: c.expiresAt?.toISOString(),
1138
- accountInfo: c.accountInfo,
1139
- configPath: c.configPath,
1140
- hasValue: !!c.value
1141
- }));
1142
- console.log(
1143
- JSON.stringify(
1144
- {
1145
- authenticated: sanitized.length > 0,
1146
- sources: sanitized,
1147
- warnings: status.warnings
1148
- },
1149
- null,
1150
- 2
1151
- )
1152
- );
1153
- return;
1154
- }
1155
- console.log();
1156
- console.log(` ${logger8.brand("RAPID")} Authentication`);
1157
- console.log(` ${logger8.dim("\u2500".repeat(24))}`);
1158
- console.log();
1159
- if (!status.authenticated) {
1160
- console.log(` ${logger8.dim("No authentication detected")}`);
1161
- console.log();
1162
- console.log(" To authenticate, use one of these methods:");
1163
- console.log();
1164
- console.log(` ${logger8.brand("Claude Pro/Max:")}`);
1165
- console.log(" Run `claude` and sign in with your Anthropic account");
1166
- console.log();
1167
- console.log(` ${logger8.brand("OpenAI (ChatGPT Plus/Pro):")}`);
1168
- console.log(" Run `codex` and sign in with ChatGPT");
1169
- console.log();
1170
- console.log(` ${logger8.brand("Gemini:")}`);
1171
- console.log(" Run `gemini` and sign in with your Google account");
1172
- console.log();
1173
- console.log(` ${logger8.brand("API Keys:")}`);
1174
- console.log(" Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY");
1175
- console.log();
1176
- return;
1177
- }
1178
- for (const cred of status.sources) {
1179
- if (options.source && cred.source !== options.source) continue;
1180
- if (options.provider && cred.provider !== options.provider) continue;
1181
- const isPrimary = cred === status.preferredSource;
1182
- const icon = isPrimary ? logger8.brand("\u25CF") : logger8.dim("\u25CB");
1183
- const authIcon = cred.authType === "oauth" ? logger8.brand("OAuth") : logger8.dim("API Key");
1184
- console.log(` ${icon} ${logger8.bold(cred.source)}`);
1185
- console.log(` Provider: ${cred.provider}`);
1186
- console.log(` Auth: ${authIcon}`);
1187
- if (cred.accountInfo?.email) {
1188
- console.log(` Account: ${cred.accountInfo.email}`);
1189
- }
1190
- if (cred.accountInfo?.organization) {
1191
- console.log(` Org: ${cred.accountInfo.organization}`);
1192
- }
1193
- if (cred.accountInfo?.plan) {
1194
- console.log(` Plan: ${logger8.brand(cred.accountInfo.plan)}`);
1195
- }
1196
- if (cred.expiresAt) {
1197
- const now = /* @__PURE__ */ new Date();
1198
- const expiresIn = Math.round((cred.expiresAt.getTime() - now.getTime()) / 1e3 / 60);
1199
- if (expiresIn > 0) {
1200
- console.log(` Expires: in ${expiresIn} minutes`);
1201
- } else {
1202
- console.log(` Expires: ${logger8.dim("EXPIRED")}`);
1203
- }
1204
- }
1205
- if (cred.configPath) {
1206
- console.log(` Config: ${logger8.dim(cred.configPath)}`);
1207
- }
1208
- if (cred.envVar) {
1209
- console.log(` Env: ${logger8.dim(cred.envVar)}`);
1210
- }
1211
- if (isPrimary) {
1212
- console.log(` ${logger8.brand("\u2192 Primary source")}`);
1213
- }
1214
- console.log();
1215
- }
1216
- if (status.warnings && status.warnings.length > 0) {
1217
- console.log(` ${logger8.dim("Warnings:")}`);
1218
- for (const warning of status.warnings) {
1219
- console.log(` ! ${warning}`);
1220
- }
1221
- console.log();
1222
- }
1223
- console.log(` ${logger8.dim("Tip:")} RAPID will automatically use detected`);
1224
- console.log(` ${logger8.dim(" ")} credentials when launching AI agents.`);
1225
- console.log();
1226
- } catch (error) {
1227
- logger8.error(error instanceof Error ? error.message : String(error));
1228
- process.exit(1);
1229
- }
1230
- });
1231
- authCommand.command("env").description("Show environment variables for detected credentials").option("--export", "Output as export statements").option("--json", "Output as JSON").action(async (options) => {
1232
- try {
1233
- const credentials = await detectAllCredentials();
1234
- const byProvider = /* @__PURE__ */ new Map();
1235
- for (const cred of credentials) {
1236
- if (!cred.value) continue;
1237
- const existing = byProvider.get(cred.provider);
1238
- if (!existing || cred.authType === "oauth" && cred.envVar) {
1239
- let envVar;
1240
- switch (cred.provider) {
1241
- case "anthropic":
1242
- envVar = cred.authType === "oauth" ? "ANTHROPIC_AUTH_TOKEN" : "ANTHROPIC_API_KEY";
1243
- break;
1244
- case "openai":
1245
- envVar = cred.authType === "oauth" ? "OPENAI_AUTH_TOKEN" : "OPENAI_API_KEY";
1246
- break;
1247
- case "google":
1248
- envVar = cred.authType === "oauth" ? "GOOGLE_AUTH_TOKEN" : cred.envVar || "GEMINI_API_KEY";
1249
- break;
1250
- default:
1251
- continue;
1252
- }
1253
- const masked = cred.value.slice(0, 8) + "..." + cred.value.slice(-4);
1254
- byProvider.set(cred.provider, { envVar, masked });
1255
- }
1256
- }
1257
- if (options.json) {
1258
- const result = {};
1259
- for (const [, { envVar, masked }] of byProvider) {
1260
- result[envVar] = masked;
1261
- }
1262
- console.log(JSON.stringify(result, null, 2));
1263
- return;
1264
- }
1265
- if (options.export) {
1266
- for (const [, { envVar }] of byProvider) {
1267
- console.log(`# ${envVar} detected from external auth`);
1268
- console.log(`# export ${envVar}="<your-token>"`);
1269
- }
1270
- return;
1271
- }
1272
- console.log();
1273
- console.log(` ${logger8.brand("RAPID")} Auth Environment`);
1274
- console.log(` ${logger8.dim("\u2500".repeat(24))}`);
1275
- console.log();
1276
- if (byProvider.size === 0) {
1277
- console.log(` ${logger8.dim("No credentials detected")}`);
1278
- console.log();
1279
- return;
1280
- }
1281
- for (const [provider, { envVar, masked }] of byProvider) {
1282
- console.log(` ${logger8.brand(provider)}`);
1283
- console.log(` ${envVar}=${masked}`);
1284
- console.log();
1285
- }
1286
- console.log(` ${logger8.dim("These will be automatically injected when running agents.")}`);
1287
- console.log();
1288
- } catch (error) {
1289
- logger8.error(error instanceof Error ? error.message : String(error));
1290
- process.exit(1);
1291
- }
1292
- });
1293
-
1294
- // src/commands/mcp.ts
1295
- import { Command as Command9 } from "commander";
1296
- import { writeFile as writeFile3 } from "fs/promises";
1297
- import { join as join3 } from "path";
1298
- import {
1299
- loadConfig as loadConfig7,
1300
- logger as logger9,
1301
- getMcpServers,
1302
- getMcpServerStatus,
1303
- addMcpServerFromTemplate as addMcpServerFromTemplate2,
1304
- addMcpServer,
1305
- removeMcpServer,
1306
- enableMcpServer,
1307
- disableMcpServer,
1308
- writeMcpConfig as writeMcpConfig2,
1309
- writeOpenCodeConfig as writeOpenCodeConfig2,
1310
- MCP_SERVER_TEMPLATES as MCP_SERVER_TEMPLATES2,
1311
- getMcpTemplate
1312
- } from "@a3t/rapid-core";
1313
- import ora8 from "ora";
1314
- var mcpCommand = new Command9("mcp").description(
1315
- "Manage MCP (Model Context Protocol) servers"
1316
- );
1317
- async function saveConfig(rootDir, config) {
1318
- const configPath = join3(rootDir, "rapid.json");
1319
- await writeFile3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1320
- }
1321
- mcpCommand.command("list").description("List configured MCP servers").option("--json", "Output as JSON").option("--templates", "Show available templates instead of configured servers").action(async (options) => {
1322
- try {
1323
- if (options.templates) {
1324
- if (options.json) {
1325
- console.log(JSON.stringify(MCP_SERVER_TEMPLATES2, null, 2));
1326
- return;
1327
- }
1328
- console.log();
1329
- console.log(` ${logger9.brand("Available MCP Server Templates")}`);
1330
- console.log(` ${logger9.dim("\u2500".repeat(40))}`);
1331
- console.log();
1332
- for (const [name, template] of Object.entries(MCP_SERVER_TEMPLATES2)) {
1333
- const typeLabel = template.type === "remote" ? logger9.dim("(remote)") : logger9.dim("(stdio)");
1334
- const secretsLabel = template.requiredSecrets.length > 0 ? logger9.dim(` - requires: ${template.requiredSecrets.join(", ")}`) : logger9.dim(" - no secrets required");
1335
- console.log(` ${logger9.brand("\u2022")} ${name} ${typeLabel}`);
1336
- console.log(` ${template.description}${secretsLabel}`);
1337
- console.log();
1338
- }
1339
- logger9.info(`Use ${logger9.brand("rapid mcp add <name>")} to add a server`);
1340
- console.log();
1341
- return;
1342
- }
1343
- const loaded = await loadConfig7();
1344
- if (!loaded) {
1345
- logger9.error("No rapid.json found. Run `rapid init` first.");
1346
- process.exit(1);
1347
- }
1348
- const { config } = loaded;
1349
- const servers = getMcpServers(config);
1350
- if (options.json) {
1351
- console.log(JSON.stringify({ servers }, null, 2));
1352
- return;
1353
- }
1354
- console.log();
1355
- console.log(` ${logger9.brand("MCP Servers")}`);
1356
- console.log(` ${logger9.dim("\u2500".repeat(40))}`);
1357
- console.log();
1358
- if (servers.length === 0) {
1359
- console.log(` ${logger9.dim("No MCP servers configured")}`);
1360
- console.log();
1361
- logger9.info(`Use ${logger9.brand("rapid mcp add <name>")} to add a server`);
1362
- logger9.info(`Use ${logger9.brand("rapid mcp list --templates")} to see available templates`);
1363
- console.log();
1364
- return;
1365
- }
1366
- for (const server of servers) {
1367
- const icon = server.enabled ? logger9.brand("\u2713") : logger9.dim("\u25CB");
1368
- const typeLabel = server.type === "remote" ? logger9.dim("(remote)") : logger9.dim("(stdio)");
1369
- const statusLabel = server.enabled ? "" : logger9.dim(" [disabled]");
1370
- const location = server.type === "remote" ? logger9.dim(server.url || "") : logger9.dim(server.command || "");
1371
- console.log(` ${icon} ${server.name} ${typeLabel}${statusLabel}`);
1372
- if (location) {
1373
- console.log(` ${location}`);
1374
- }
1375
- console.log();
1376
- }
1377
- } catch (error) {
1378
- logger9.error(error instanceof Error ? error.message : String(error));
1379
- process.exit(1);
1380
- }
1381
- });
1382
- mcpCommand.command("add").description("Add an MCP server").argument("<name>", "Server name (or template name)").option("--type <type>", "Server type: remote or stdio").option("--url <url>", "URL for remote servers").option("--command <cmd>", "Command for stdio servers").option("--args <args>", "Arguments for stdio command (comma-separated)").option("--header <header>", "HTTP header for remote servers (name=value)", collectHeaders, {}).action(async (name, options) => {
1383
- const spinner = ora8(`Adding MCP server '${name}'...`).start();
1384
- try {
1385
- const loaded = await loadConfig7();
1386
- if (!loaded) {
1387
- spinner.fail("No rapid.json found. Run `rapid init` first.");
1388
- process.exit(1);
1389
- }
1390
- let { config } = loaded;
1391
- const { rootDir } = loaded;
1392
- const existingServers = getMcpServers(config);
1393
- if (existingServers.some((s) => s.name === name)) {
1394
- spinner.fail(`MCP server '${name}' already exists`);
1395
- logger9.info(`Use ${logger9.brand(`rapid mcp remove ${name}`)} to remove it first`);
1396
- process.exit(1);
1397
- }
1398
- const template = getMcpTemplate(name);
1399
- if (template && !options.type && !options.url && !options.command) {
1400
- config = addMcpServerFromTemplate2(config, name);
1401
- spinner.text = `Adding '${name}' from template...`;
1402
- } else if (options.type || options.url || options.command) {
1403
- const serverConfig = {
1404
- enabled: true
1405
- };
1406
- if (options.type) {
1407
- serverConfig.type = options.type;
1408
- }
1409
- if (options.url) {
1410
- serverConfig.type = "remote";
1411
- serverConfig.url = options.url;
1412
- }
1413
- if (options.header && Object.keys(options.header).length > 0) {
1414
- serverConfig.headers = options.header;
1415
- }
1416
- if (options.command) {
1417
- serverConfig.type = "stdio";
1418
- serverConfig.command = options.command;
1419
- }
1420
- if (options.args) {
1421
- serverConfig.args = options.args.split(",").map((a) => a.trim());
1422
- }
1423
- config = addMcpServer(config, name, serverConfig);
1424
- } else if (template) {
1425
- config = addMcpServerFromTemplate2(config, name);
1426
- } else {
1427
- spinner.fail(`Unknown MCP server template: ${name}`);
1428
- logger9.info(`Use ${logger9.brand("rapid mcp list --templates")} to see available templates`);
1429
- logger9.info("Or specify --type, --url, or --command for a custom server");
1430
- process.exit(1);
1431
- }
1432
- await saveConfig(rootDir, config);
1433
- await writeMcpConfig2(rootDir, config);
1434
- await writeOpenCodeConfig2(rootDir, config);
1435
- spinner.succeed(`Added MCP server '${name}'`);
1436
- console.log();
1437
- if (template?.requiredSecrets.length) {
1438
- logger9.info("Required secrets:");
1439
- for (const secret of template.requiredSecrets) {
1440
- const ref = template.secretReferences?.[secret];
1441
- console.log(` ${logger9.brand("\u2022")} ${secret}${ref ? logger9.dim(` (${ref})`) : ""}`);
1442
- }
1443
- console.log();
1444
- logger9.info(`Add these to ${logger9.brand("rapid.json")} secrets.items section`);
1445
- console.log();
1446
- }
1447
- } catch (error) {
1448
- spinner.fail("Failed to add MCP server");
1449
- logger9.error(error instanceof Error ? error.message : String(error));
1450
- process.exit(1);
1451
- }
1452
- });
1453
- mcpCommand.command("remove").description("Remove an MCP server").argument("<name>", "Server name to remove").action(async (name) => {
1454
- const spinner = ora8(`Removing MCP server '${name}'...`).start();
1455
- try {
1456
- const loaded = await loadConfig7();
1457
- if (!loaded) {
1458
- spinner.fail("No rapid.json found");
1459
- process.exit(1);
1460
- }
1461
- let { config } = loaded;
1462
- const { rootDir } = loaded;
1463
- config = removeMcpServer(config, name);
1464
- await saveConfig(rootDir, config);
1465
- await writeMcpConfig2(rootDir, config);
1466
- await writeOpenCodeConfig2(rootDir, config);
1467
- spinner.succeed(`Removed MCP server '${name}'`);
1468
- console.log();
1469
- } catch (error) {
1470
- spinner.fail("Failed to remove MCP server");
1471
- logger9.error(error instanceof Error ? error.message : String(error));
1472
- process.exit(1);
1473
- }
1474
- });
1475
- mcpCommand.command("enable").description("Enable a disabled MCP server").argument("<name>", "Server name to enable").action(async (name) => {
1476
- const spinner = ora8(`Enabling MCP server '${name}'...`).start();
1477
- try {
1478
- const loaded = await loadConfig7();
1479
- if (!loaded) {
1480
- spinner.fail("No rapid.json found");
1481
- process.exit(1);
1482
- }
1483
- let { config } = loaded;
1484
- const { rootDir } = loaded;
1485
- config = enableMcpServer(config, name);
1486
- await saveConfig(rootDir, config);
1487
- await writeMcpConfig2(rootDir, config);
1488
- await writeOpenCodeConfig2(rootDir, config);
1489
- spinner.succeed(`Enabled MCP server '${name}'`);
1490
- console.log();
1491
- } catch (error) {
1492
- spinner.fail("Failed to enable MCP server");
1493
- logger9.error(error instanceof Error ? error.message : String(error));
1494
- process.exit(1);
1495
- }
1496
- });
1497
- mcpCommand.command("disable").description("Disable an MCP server (without removing)").argument("<name>", "Server name to disable").action(async (name) => {
1498
- const spinner = ora8(`Disabling MCP server '${name}'...`).start();
1499
- try {
1500
- const loaded = await loadConfig7();
1501
- if (!loaded) {
1502
- spinner.fail("No rapid.json found");
1503
- process.exit(1);
1504
- }
1505
- let { config } = loaded;
1506
- const { rootDir } = loaded;
1507
- config = disableMcpServer(config, name);
1508
- await saveConfig(rootDir, config);
1509
- await writeMcpConfig2(rootDir, config);
1510
- await writeOpenCodeConfig2(rootDir, config);
1511
- spinner.succeed(`Disabled MCP server '${name}'`);
1512
- console.log();
1513
- } catch (error) {
1514
- spinner.fail("Failed to disable MCP server");
1515
- logger9.error(error instanceof Error ? error.message : String(error));
1516
- process.exit(1);
1517
- }
1518
- });
1519
- mcpCommand.command("status").description("Show MCP server status").option("--json", "Output as JSON").action(async (options) => {
1520
- try {
1521
- const loaded = await loadConfig7();
1522
- if (!loaded) {
1523
- logger9.error("No rapid.json found. Run `rapid init` first.");
1524
- process.exit(1);
1525
- }
1526
- const { config } = loaded;
1527
- const servers = getMcpServerStatus(config);
1528
- if (options.json) {
1529
- console.log(JSON.stringify({ servers }, null, 2));
1530
- return;
1531
- }
1532
- console.log();
1533
- console.log(` ${logger9.brand("MCP Server Status")}`);
1534
- console.log(` ${logger9.dim("\u2500".repeat(40))}`);
1535
- console.log();
1536
- if (servers.length === 0) {
1537
- console.log(` ${logger9.dim("No MCP servers configured")}`);
1538
- console.log();
1539
- return;
1540
- }
1541
- let enabledCount = 0;
1542
- let disabledCount = 0;
1543
- for (const server of servers) {
1544
- if (server.enabled) {
1545
- enabledCount++;
1546
- } else {
1547
- disabledCount++;
1548
- }
1549
- const icon = server.status === "enabled" ? logger9.brand("\u2713") : server.status === "disabled" ? logger9.dim("\u25CB") : "\u2717";
1550
- const statusLabel = server.status === "enabled" ? "enabled" : server.status === "disabled" ? logger9.dim("disabled") : logger9.dim(`error: ${server.error}`);
1551
- const typeLabel = server.type === "remote" ? "remote" : "stdio";
1552
- console.log(` ${icon} ${server.name}`);
1553
- console.log(` ${logger9.dim("Type:")} ${typeLabel}`);
1554
- console.log(` ${logger9.dim("Status:")} ${statusLabel}`);
1555
- if (server.url) {
1556
- console.log(` ${logger9.dim("URL:")} ${server.url}`);
1557
- }
1558
- if (server.command) {
1559
- console.log(` ${logger9.dim("Cmd:")} ${server.command}`);
1560
- }
1561
- console.log();
1562
- }
1563
- console.log(` ${logger9.dim("Summary:")} ${enabledCount} enabled, ${disabledCount} disabled`);
1564
- console.log();
1565
- } catch (error) {
1566
- logger9.error(error instanceof Error ? error.message : String(error));
1567
- process.exit(1);
1568
- }
1569
- });
1570
- mcpCommand.command("sync").description("Regenerate .mcp.json and opencode.json from rapid.json").action(async () => {
1571
- const spinner = ora8("Syncing MCP configuration files...").start();
1572
- try {
1573
- const loaded = await loadConfig7();
1574
- if (!loaded) {
1575
- spinner.fail("No rapid.json found");
1576
- process.exit(1);
1577
- }
1578
- const { config, rootDir } = loaded;
1579
- await writeMcpConfig2(rootDir, config);
1580
- await writeOpenCodeConfig2(rootDir, config);
1581
- const servers = getMcpServers(config);
1582
- const enabledCount = servers.filter((s) => s.enabled).length;
1583
- spinner.succeed("MCP configuration synced");
1584
- console.log();
1585
- console.log(` ${logger9.dim("Files updated:")}`);
1586
- console.log(` ${logger9.brand("\u2022")} .mcp.json`);
1587
- console.log(` ${logger9.brand("\u2022")} opencode.json`);
1588
- console.log();
1589
- console.log(` ${logger9.dim("Servers:")} ${enabledCount} enabled`);
1590
- console.log();
1591
- } catch (error) {
1592
- spinner.fail("Failed to sync MCP configuration");
1593
- logger9.error(error instanceof Error ? error.message : String(error));
1594
- process.exit(1);
1595
- }
1596
- });
1597
- function collectHeaders(value, previous) {
1598
- const [name, ...rest] = value.split("=");
1599
- if (name && rest.length > 0) {
1600
- previous[name] = rest.join("=");
1601
- }
1602
- return previous;
1603
- }
1604
-
1605
- // src/index.ts
1606
- var __dirname = dirname(fileURLToPath(import.meta.url));
1607
- var packageJson = JSON.parse(readFileSync(join4(__dirname, "../package.json"), "utf-8"));
1608
- var VERSION = packageJson.version;
1609
- var program = new Command10();
1610
- program.name("rapid").description("AI-assisted development with dev containers").version(VERSION, "-v, --version", "Show version").option("--verbose", "Verbose output").option("-q, --quiet", "Minimal output").option("--config <path>", "Path to rapid.json").hook("preAction", (thisCommand) => {
1611
- const opts = thisCommand.opts();
1612
- if (opts.verbose) {
1613
- setLogLevel("debug");
1614
- } else if (opts.quiet) {
1615
- setLogLevel("error");
1616
- }
1617
- });
1618
- program.addCommand(initCommand);
1619
- program.addCommand(startCommand);
1620
- program.addCommand(devCommand);
1621
- program.addCommand(stopCommand);
1622
- program.addCommand(statusCommand);
1623
- program.addCommand(agentCommand);
1624
- program.addCommand(secretsCommand);
1625
- program.addCommand(authCommand);
1626
- program.addCommand(mcpCommand);
1627
- program.action(() => {
1628
- console.log();
1629
- console.log(` ${logger10.brand("RAPID")} ${logger10.dim(`v${VERSION}`)}`);
1630
- console.log(` ${logger10.dim("AI-assisted development with dev containers")}`);
1631
- console.log();
1632
- program.help();
1633
- });
1634
-
1635
- export {
1636
- program
1637
- };
1638
- //# sourceMappingURL=chunk-MNZPKPED.js.map