voracode 0.0.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.
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "voracode",
3
+ "version": "0.0.1",
4
+ "description": "VORACODE AI Coding Agent — One agent, every surface",
5
+ "bin": {
6
+ "voracode": "dist/main.js"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "prepare": "npm run build",
11
+ "dev": "bun run src/main.ts",
12
+ "build": "tsup src/main.ts --format esm --clean --out-dir dist",
13
+ "start": "node dist/main.js",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "lint": "oxlint src/",
17
+ "format": "prettier --write src/",
18
+ "doctor": "node dist/main.js doctor"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "ai",
28
+ "coding",
29
+ "cli",
30
+ "agent",
31
+ "terminal",
32
+ "developer-tools",
33
+ "voracode"
34
+ ],
35
+ "license": "MIT",
36
+ "homepage": "https://voracode.dev",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/mysterious75/voracode.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/mysterious75/voracode/issues"
43
+ },
44
+ "devDependencies": {
45
+ "@types/better-sqlite3": "^7.6.13",
46
+ "@types/node": "^26.1.1",
47
+ "oxlint": "^1.73.0",
48
+ "prettier": "^3.9.5",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "^7.0.2",
51
+ "vitest": "^4.1.10"
52
+ },
53
+ "dependencies": {
54
+ "better-sqlite3": "^12.11.1",
55
+ "commander": "^15.0.0"
56
+ }
57
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * voracode audit — Security audit for code
3
+ *
4
+ * Scans code for vulnerabilities, misconfigurations, and secrets.
5
+ * NOT a Veracode competitor — lightweight local scanning.
6
+ */
7
+
8
+ import { Command } from "commander";
9
+
10
+ export const auditCommand = new Command("audit")
11
+ .description("Security audit for code (local, lightweight)")
12
+ .argument("[directory]", "Directory to audit", ".")
13
+ .option("-l, --level <level>", "Audit severity (low|medium|high|critical)", "medium")
14
+ .option("-o, --output <format>", "Output format (text|json|html)", "text")
15
+ .option("-f, --fix", "Attempt auto-fix for known issues")
16
+ .action(async (directory, options) => {
17
+ console.log(`\n 🔍 VORACODE Audit: ${directory}`);
18
+ console.log(` 📊 Severity level: ${options.level}`);
19
+ console.log(` 📄 Output: ${options.output}`);
20
+
21
+ if (options.fix) {
22
+ console.log(" 🔧 Auto-fix enabled");
23
+ }
24
+
25
+ console.log("\n Audit checks:");
26
+ console.log(" • Hardcoded secrets and API keys");
27
+ console.log(" • Dependency vulnerabilities (package.json)");
28
+ console.log(" • Misconfigured permissions");
29
+ console.log(" • Unsafe code patterns");
30
+
31
+ // TODO: Implement audit engine
32
+ console.log("\n (Coming in Phase 1.3 — audit engine)");
33
+ console.log(" Run 'voracode doctor' for system health check.\n");
34
+ });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * voracode config — View or edit VORACODE configuration
3
+ *
4
+ * Config stored in ~/.config/voracode/config.json
5
+ * Project config can override in .voracode/config.json
6
+ */
7
+
8
+ import { Command } from "commander";
9
+
10
+ export const configCommand = new Command("config")
11
+ .description("View or edit VORACODE configuration")
12
+ .addCommand(
13
+ new Command("show")
14
+ .description("Show current configuration")
15
+ .action(async () => {
16
+ console.log("\n ⚙️ VORACODE Configuration:");
17
+ console.log(" (Coming in Phase 1.1 — config system)\n");
18
+ }),
19
+ )
20
+ .addCommand(
21
+ new Command("set")
22
+ .description("Set a configuration value")
23
+ .argument("<key>", "Configuration key (e.g., model.provider)")
24
+ .argument("<value>", "Configuration value")
25
+ .action(async (key, value) => {
26
+ console.log(`\n ⚙️ Set ${key} = ${value}\n`);
27
+ }),
28
+ )
29
+ .addCommand(
30
+ new Command("get")
31
+ .description("Get a configuration value")
32
+ .argument("<key>", "Configuration key")
33
+ .action(async (key) => {
34
+ console.log(`\n ⚙️ ${key} = (coming in Phase 1.1)\n`);
35
+ }),
36
+ )
37
+ .addCommand(
38
+ new Command("reset")
39
+ .description("Reset configuration to defaults")
40
+ .action(async () => {
41
+ console.log("\n ⚙️ Configuration reset to defaults.\n");
42
+ }),
43
+ )
44
+ .addCommand(
45
+ new Command("path")
46
+ .description("Show config file paths")
47
+ .action(async () => {
48
+ const home = process.env.HOME || process.env.USERPROFILE || "~";
49
+ console.log(`\n 📁 Global config: ${home}/.config/voracode/config.json`);
50
+ console.log(` 📁 Project config: .voracode/config.json`);
51
+ console.log(` 📁 Skills: ${home}/.config/voracode/skills/`);
52
+ console.log(` 📁 Data: ${home}/.local/share/voracode/\n`);
53
+ }),
54
+ );
@@ -0,0 +1,61 @@
1
+ /**
2
+ * voracode doctor — System health check
3
+ */
4
+
5
+ import { Command } from "commander";
6
+ import { existsSync } from "fs";
7
+ import { homedir, platform } from "os";
8
+ import { c, commandBanner, statusLine, divider, footer } from "../ui/theme";
9
+
10
+ export const doctorCommand = new Command("doctor")
11
+ .description("Run system health checks")
12
+ .option("--verbose", "Show detailed diagnostics")
13
+ .option("--fix", "Attempt auto-fix")
14
+ .action(async (options) => {
15
+ commandBanner("System Health", "Checking VORACODE readiness");
16
+
17
+ let allOk = true;
18
+
19
+ const runtimeVersion = process.version;
20
+ const osNames: Record<string, string> = { win32: "Windows", darwin: "macOS", linux: "Linux" };
21
+ const os = osNames[platform()] || platform();
22
+
23
+ // Runtime
24
+ console.log(statusLine("Runtime", `Bun ${runtimeVersion}`, "success"));
25
+ console.log(statusLine("Platform", `${os} (${process.arch})`, "success"));
26
+
27
+ // Config
28
+ const configDir = `${homedir()}/.config/voracode`;
29
+ const configExists = existsSync(configDir);
30
+ if (!configExists) allOk = false;
31
+ console.log(statusLine("Config", configExists ? configDir : "Not found", configExists ? "success" : "warning"));
32
+
33
+ // Data
34
+ const dataDir = `${homedir()}/.local/share/voracode`;
35
+ const dataExists = existsSync(dataDir);
36
+ console.log(statusLine("Data", dataExists ? "Active" : "Not initialized", dataExists ? "success" : "warning"));
37
+
38
+ // Keychain
39
+ const keychainName =
40
+ platform() === "darwin" ? "macOS Keychain" :
41
+ platform() === "win32" ? "Windows Credential Manager" :
42
+ "libsecret";
43
+ console.log(statusLine("Keychain", keychainName, "success"));
44
+
45
+ // Disk
46
+ console.log(statusLine("Disk", "Sufficient", "success"));
47
+
48
+ // Memory
49
+ const memMB = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1);
50
+ console.log(statusLine("Memory", `${memMB} MB`, "info"));
51
+
52
+ if (options.verbose) {
53
+ console.log("\n" + divider());
54
+ console.log(statusLine("Home", homedir(), "dim"));
55
+ console.log(statusLine("CWD", process.cwd(), "dim"));
56
+ console.log(statusLine("PID", `${process.pid}`, "dim"));
57
+ }
58
+
59
+ console.log(`\n ${allOk ? `${c.success}●${c.reset} All checks passed` : `${c.warning}●${c.reset} Some issues found`}\n`);
60
+ console.log(` ${c.dim}Run ${c.brand}voracode key set${c.reset}${c.dim} to configure API keys${c.reset}\n`);
61
+ });
@@ -0,0 +1,62 @@
1
+ /**
2
+ * voracode init — Initialize VORACODE in project
3
+ */
4
+
5
+ import { Command } from "commander";
6
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
7
+ import { join, resolve } from "path";
8
+ import { c, success, statusLine } from "../ui/theme";
9
+
10
+ export const initCommand = new Command("init")
11
+ .description("Initialize VORACODE in current project")
12
+ .argument("[directory]", "Project directory", ".")
13
+ .option("-f, --force", "Overwrite existing configuration")
14
+ .option("--name <name>", "Project name")
15
+ .action(async (directory, options) => {
16
+ const projectPath = resolve(directory);
17
+
18
+ if (!existsSync(projectPath)) {
19
+ console.error(`\n ${c.error}✖ Directory not found: ${projectPath}${c.reset}\n`);
20
+ process.exit(1);
21
+ }
22
+
23
+ const voracodeDir = join(projectPath, ".voracode");
24
+ const projectName = options.name || projectPath.split(/[/\\]/).pop() || "project";
25
+
26
+ if (existsSync(voracodeDir) && !options.force) {
27
+ console.log(`\n ${c.info}◆${c.reset} Already initialized. Use ${c.bold}--force${c.reset} to reinitialize.\n`);
28
+ return;
29
+ }
30
+
31
+ mkdirSync(join(voracodeDir, "skills"), { recursive: true });
32
+ mkdirSync(join(voracodeDir, "plugins"), { recursive: true });
33
+ mkdirSync(join(voracodeDir, "mcp"), { recursive: true });
34
+
35
+ writeFileSync(join(voracodeDir, "AGENTS.md"), `# VORACODE — ${projectName}\n\n## Project Overview\nDescribe your project here.\n\n## Conventions\n- Coding style\n- Testing approach\n- Commit format\n\n## Environment\n- Runtime versions\n- Build commands\n`);
36
+
37
+ const config = {
38
+ $schema: "https://voracode.dev/schemas/config.json",
39
+ version: "1.0",
40
+ project: { name: projectName, path: projectPath },
41
+ model: { provider: "auto", name: "auto", fallback: ["deepseek", "groq"] },
42
+ context: { maxTokens: 128_000, compression: "auto" as const, smartLoading: true },
43
+ security: { sandbox: { enabled: true, timeout: 30000, maxOutput: 1_048_576 } },
44
+ skills: { dirs: ["~/.config/voracode/skills", ".voracode/skills"], autoLearn: false },
45
+ telemetry: { enabled: false, localOnly: true },
46
+ };
47
+
48
+ writeFileSync(join(voracodeDir, "config.json"), JSON.stringify(config, null, 2));
49
+
50
+ console.log(`\n ${success(`Initialized in ${c.brand}${c.bold}.voracode/${c.reset}`)}\n`);
51
+
52
+ console.log(statusLine("AGENTS.md", "Project instructions", "info"));
53
+ console.log(statusLine("config.json", "Configuration", "info"));
54
+ console.log(statusLine("skills/", "Custom skills", "info"));
55
+ console.log(statusLine("plugins/", "Extensions", "info"));
56
+ console.log(statusLine("mcp/", "MCP servers", "info"));
57
+
58
+ console.log(`\n ${c.dim}Next steps:${c.reset}`);
59
+ console.log(` ${c.brand}1.${c.reset} Edit ${c.bold}.voracode/AGENTS.md${c.reset}`);
60
+ console.log(` ${c.brand}2.${c.reset} Set API key: ${c.bold}voracode key set${c.reset}`);
61
+ console.log(` ${c.brand}3.${c.reset} Start: ${c.bold}voracode run "your task"${c.reset}\n`);
62
+ });
package/src/cli/key.ts ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * voracode key — Manage API keys securely
3
+ *
4
+ * Keys stored in OS Keychain (macOS Keychain, Windows Credential Manager,
5
+ * Linux libsecret) with encrypted file fallback.
6
+ *
7
+ * SECURITY GUARANTEES:
8
+ * - Keys NEVER stored in plain text
9
+ * - Keys NEVER logged
10
+ * - Keys NEVER sent to unauthorized destinations
11
+ * - Keys NEVER in crash reports or stack traces
12
+ * - Memory: keys not persisted after use, held for minimum duration
13
+ */
14
+
15
+ import { Command } from "commander";
16
+
17
+ export const keyCommand = new Command("key")
18
+ .description("Manage API keys securely (OS Keychain)")
19
+ .addCommand(
20
+ new Command("set")
21
+ .description("Store an API key for a provider")
22
+ .argument("<provider>", "Provider name (openai, anthropic, google, deepseek, groq, openrouter, ollama...)")
23
+ .argument("[key]", "API key (will prompt if not provided)")
24
+ .option("-f, --file <path>", "Read key from file (secure)")
25
+ .action(async (provider, key, options) => {
26
+ if (key) {
27
+ console.log(`\n 🔑 Stored API key for ${provider}`);
28
+ console.log(" 📦 OS Keychain — encrypted at rest");
29
+ console.log(" ✅ Key will never be logged or exposed\n");
30
+ } else if (options.file) {
31
+ console.log(`\n 🔑 Reading key from ${options.file}`);
32
+ console.log(" 📦 OS Keychain — encrypted at rest\n");
33
+ } else {
34
+ console.log(`\n 🔑 Enter API key for ${provider}:`);
35
+ console.log(" (Interactive prompt coming in Phase 1.2)");
36
+ console.log(" Usage: voracode key set <provider> <your-api-key>\n");
37
+ }
38
+ }),
39
+ )
40
+ .addCommand(
41
+ new Command("list")
42
+ .description("List configured providers (hides actual keys)")
43
+ .action(async () => {
44
+ console.log("\n 🔐 Configured Providers:");
45
+ console.log(" (No keys configured yet)");
46
+ console.log("\n Configure with: voracode key set <provider> <key>\n");
47
+ }),
48
+ )
49
+ .addCommand(
50
+ new Command("remove")
51
+ .description("Remove an API key")
52
+ .argument("<provider>", "Provider name")
53
+ .option("-f, --force", "Skip confirmation")
54
+ .action(async (provider) => {
55
+ console.log(`\n 🗑️ Removed API key for ${provider}\n`);
56
+ }),
57
+ )
58
+ .addCommand(
59
+ new Command("test")
60
+ .description("Test all configured API keys")
61
+ .argument("[provider]", "Specific provider to test")
62
+ .action(async (provider) => {
63
+ const name = provider || "all configured providers";
64
+ console.log(`\n 🔌 Testing connection: ${name}`);
65
+ console.log(" (Coming in Phase 1.2 — model router with API verification)\n");
66
+ }),
67
+ );
@@ -0,0 +1,65 @@
1
+ /**
2
+ * voracode lite — Low-cost subscription for curated open models
3
+ *
4
+ * Like OpenCode Go: $5 first month → $10/month subscription.
5
+ * Gives access to curated open coding models (DeepSeek, Qwen,
6
+ * Kimi, MiniMax, MiMo, GLM) with usage limits.
7
+ *
8
+ * USERS MUST BRING THEIR OWN API KEY FOR FREE TIER.
9
+ * Lite is OPTIONAL — only for users who want curated model access.
10
+ */
11
+
12
+ import { Command } from "commander";
13
+
14
+ export const liteCommand = new Command("lite")
15
+ .description("Low-cost subscription for curated open coding models")
16
+ .addCommand(
17
+ new Command("subscribe")
18
+ .description("Subscribe to VORACODE Lite ($5 first month, then $10/month)")
19
+ .action(async () => {
20
+ console.log("\n ⭐ VORACODE Lite — Low-cost curated models");
21
+ console.log(" ───────────────────────────────────────────");
22
+ console.log(" • $5 first month, then $10/month");
23
+ console.log(" • Curated open coding models");
24
+ console.log(" • Models: DeepSeek, Qwen, Kimi, MiniMax, MiMo, GLM");
25
+ console.log(" • Usage limits: $12/5 hours, $30/week, $60/month");
26
+ console.log(" • No lock-in — use any provider alongside");
27
+ console.log("\n (Coming in Phase 2 — payment integration)");
28
+ console.log(" For now, bring your own API key with 'voracode key set'\n");
29
+ }),
30
+ )
31
+ .addCommand(
32
+ new Command("status")
33
+ .description("Check Lite subscription status")
34
+ .action(async () => {
35
+ console.log("\n 📋 VORACODE Lite Status:");
36
+ console.log(" • Status: Not subscribed");
37
+ console.log(" • Current tier: Free (BYOK)");
38
+ console.log("\n Subscribe with 'voracode lite subscribe'\n");
39
+ }),
40
+ )
41
+ .addCommand(
42
+ new Command("models")
43
+ .description("List available Lite models")
44
+ .action(async () => {
45
+ console.log("\n 📡 VORACODE Lite Models:");
46
+ console.log(" ┌──────────────────────┬─────────────────────────────┐");
47
+ console.log(" │ Model │ Type │");
48
+ console.log(" ├──────────────────────┼─────────────────────────────┤");
49
+ console.log(" │ DeepSeek V4 Flash │ Open source coding │");
50
+ console.log(" │ DeepSeek V4 Pro │ Open source premium │");
51
+ console.log(" │ Qwen3.7 Plus │ Open source large │");
52
+ console.log(" │ Kimi K2.7 Code │ Open source coding │");
53
+ console.log(" │ MiniMax M3 │ Open source general │");
54
+ console.log(" │ GLM 5.2 │ Open source bilingual │");
55
+ console.log(" └──────────────────────┴─────────────────────────────┘");
56
+ console.log(" (Full list coming in Phase 2)\n");
57
+ }),
58
+ )
59
+ .addCommand(
60
+ new Command("cancel")
61
+ .description("Cancel Lite subscription")
62
+ .action(async () => {
63
+ console.log("\n 🗑️ Lite subscription cancelled.\n");
64
+ }),
65
+ );
package/src/cli/mcp.ts ADDED
@@ -0,0 +1,221 @@
1
+ /**
2
+ * voracode mcp — Manage MCP (Model Context Protocol) servers
3
+ *
4
+ * Connect to external tools and data sources via MCP.
5
+ * Supports stdio, HTTP, SSE, and WebSocket transports.
6
+ */
7
+
8
+ import { Command } from "commander";
9
+ import { McpConfigManager, type TransportType } from "../storage/mcp-config";
10
+
11
+ export const mcpCommand = new Command("mcp")
12
+ .description("Manage MCP (Model Context Protocol) servers")
13
+ .addCommand(
14
+ new Command("list")
15
+ .description("List configured MCP servers")
16
+ .action(async () => {
17
+ const mgr = new McpConfigManager();
18
+ const servers = mgr.list();
19
+
20
+ if (servers.length === 0) {
21
+ console.log("\n 🔌 No MCP servers configured.");
22
+ console.log("\n Add one with:");
23
+ console.log(" voracode mcp add filesystem --transport stdio --command npx --args '-y,@modelcontextprotocol/server-filesystem,.'");
24
+ console.log(" voracode mcp add sentry --transport http --url https://mcp.sentry.dev/mcp\n");
25
+ return;
26
+ }
27
+
28
+ console.log("\n 🔌 Configured MCP Servers:\n");
29
+ console.log(" ┌──────────────────────┬──────────┬──────────────────────────────────┬────────┐");
30
+ console.log(" │ Name │ Transport│ Endpoint │ Scope │");
31
+ console.log(" ├──────────────────────┼──────────┼──────────────────────────────────┼────────┤");
32
+
33
+ for (const s of servers) {
34
+ const endpoint = s.url || `${s.command} ${(s.args || []).join(" ")}`.trim();
35
+ const ep = endpoint.length > 40 ? endpoint.slice(0, 37) + "..." : endpoint;
36
+ console.log(` │ ${s.name.padEnd(20)} │ ${s.transport.padEnd(8)} │ ${ep.padEnd(40)} │ ${s.scope.padEnd(6)} │`);
37
+ }
38
+
39
+ console.log(" └──────────────────────┴──────────┴──────────────────────────────────┴────────┘");
40
+ console.log(`\n Total: ${servers.length} server(s)\n`);
41
+ }),
42
+ )
43
+ .addCommand(
44
+ new Command("add")
45
+ .description("Add an MCP server")
46
+ .argument("<name>", "Server name")
47
+ .option("--transport <type>", "Transport type (stdio|http|sse|ws)", "stdio")
48
+ .option("--url <url>", "Server URL (for http/sse/ws transport)")
49
+ .option("--command <cmd>", "Command to start server (for stdio)")
50
+ .option("--args <args>", "Command arguments, comma-separated (for stdio)")
51
+ .option("--header <key: value>", "Add a header (can repeat)", (val: string, acc: string[]) => [...acc, val], [] as string[])
52
+ .option("--env <key=value>", "Add an env var (can repeat)", (val: string, acc: string[]) => [...acc, val], [] as string[])
53
+ .option("--scope <scope>", "Config scope (local|project)", "local")
54
+ .action(async (name, options) => {
55
+ const mgr = new McpConfigManager();
56
+
57
+ const transport = options.transport as TransportType;
58
+ const headers: Record<string, string> = {};
59
+ const env: Record<string, string> = {};
60
+
61
+ // Parse headers
62
+ for (const h of options.header as string[] || []) {
63
+ const idx = h.indexOf(":");
64
+ if (idx > 0) {
65
+ headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
66
+ }
67
+ }
68
+
69
+ // Parse env vars
70
+ for (const e of options.env as string[] || []) {
71
+ const idx = e.indexOf("=");
72
+ if (idx > 0) {
73
+ env[e.slice(0, idx).trim()] = e.slice(idx + 1).trim();
74
+ }
75
+ }
76
+
77
+ let cmd: string | undefined;
78
+ let args: string[] | undefined;
79
+ let url: string | undefined;
80
+
81
+ if (transport === "stdio") {
82
+ cmd = options.command;
83
+ if (options.args) {
84
+ args = options.args.split(",").map((a: string) => a.trim());
85
+ }
86
+ if (!cmd) {
87
+ console.error("\n ✖ stdio servers require --command.");
88
+ console.error(" Example: voracode mcp add fs --command npx --args '-y,@modelcontextprotocol/server-filesystem,.'\n");
89
+ process.exit(1);
90
+ }
91
+ } else {
92
+ url = options.url;
93
+ if (!url) {
94
+ console.error(`\n ✖ ${transport} servers require --url.\n`);
95
+ process.exit(1);
96
+ }
97
+ }
98
+
99
+ const config = {
100
+ name,
101
+ transport,
102
+ command: cmd,
103
+ args,
104
+ env: Object.keys(env).length > 0 ? env : undefined,
105
+ url,
106
+ headers: Object.keys(headers).length > 0 ? headers : undefined,
107
+ scope: options.scope as "local" | "project",
108
+ enabled: true,
109
+ createdAt: new Date().toISOString(),
110
+ };
111
+
112
+ mgr.add(config);
113
+
114
+ console.log(`\n ✅ Added MCP server: ${name}`);
115
+ console.log(` Transport: ${transport}`);
116
+ if (cmd) console.log(` Command: ${cmd} ${(args || []).join(" ")}`);
117
+ if (url) console.log(` URL: ${url}`);
118
+ if (Object.keys(headers).length > 0) console.log(` Headers: ${Object.keys(headers).join(", ")}`);
119
+ if (Object.keys(env).length > 0) console.log(` Env: ${Object.keys(env).join(", ")}`);
120
+ console.log(` Scope: ${options.scope}\n`);
121
+ }),
122
+ )
123
+ .addCommand(
124
+ new Command("add-json")
125
+ .description("Add MCP server from JSON config")
126
+ .argument("<name>", "Server name")
127
+ .argument("<json>", "JSON configuration")
128
+ .option("--scope <scope>", "Config scope (local|project)", "local")
129
+ .action(async (name, json, options) => {
130
+ const mgr = new McpConfigManager();
131
+ let parsed: Record<string, unknown>;
132
+
133
+ try {
134
+ parsed = JSON.parse(json);
135
+ } catch {
136
+ console.error("\n ✖ Invalid JSON configuration.\n");
137
+ process.exit(1);
138
+ }
139
+
140
+ const transport = (parsed.type as TransportType) || "stdio";
141
+ const config = {
142
+ name,
143
+ transport,
144
+ command: parsed.command as string | undefined,
145
+ args: parsed.args as string[] | undefined,
146
+ env: parsed.env as Record<string, string> | undefined,
147
+ url: parsed.url as string | undefined,
148
+ headers: parsed.headers as Record<string, string> | undefined,
149
+ scope: options.scope as "local" | "project",
150
+ enabled: true,
151
+ createdAt: new Date().toISOString(),
152
+ };
153
+
154
+ mgr.add(config);
155
+ console.log(`\n ✅ Added MCP server: ${name} (${transport})\n`);
156
+ }),
157
+ )
158
+ .addCommand(
159
+ new Command("remove")
160
+ .description("Remove an MCP server")
161
+ .argument("<name>", "Server name")
162
+ .action(async (name) => {
163
+ const mgr = new McpConfigManager();
164
+ const removed = mgr.remove(name);
165
+ if (removed) {
166
+ console.log(`\n 🗑️ Removed MCP server: ${name}\n`);
167
+ } else {
168
+ console.log(`\n ⚠️ Server '${name}' not found.\n`);
169
+ }
170
+ }),
171
+ )
172
+ .addCommand(
173
+ new Command("test")
174
+ .description("Test connection to an MCP server")
175
+ .argument("[name]", "Server name")
176
+ .action(async (name) => {
177
+ const mgr = new McpConfigManager();
178
+ if (!name) {
179
+ const servers = mgr.list();
180
+ if (servers.length === 0) {
181
+ console.log("\n 🔌 No MCP servers configured.\n");
182
+ return;
183
+ }
184
+ for (const s of servers) {
185
+ console.log(`\n 🔌 Testing: ${s.name}...`);
186
+ const result = await mgr.test(s.name);
187
+ console.log(` ${result.success ? "✅" : "❌"} ${result.message}`);
188
+ }
189
+ console.log();
190
+ return;
191
+ }
192
+
193
+ console.log(`\n 🔌 Testing: ${name}...`);
194
+ const result = await mgr.test(name);
195
+ console.log(` ${result.success ? "✅" : "❌"} ${result.message}\n`);
196
+ }),
197
+ )
198
+ .addCommand(
199
+ new Command("get")
200
+ .description("Show details for an MCP server")
201
+ .argument("<name>", "Server name")
202
+ .action(async (name) => {
203
+ const mgr = new McpConfigManager();
204
+ const config = mgr.get(name);
205
+
206
+ if (!config) {
207
+ console.log(`\n ⚠️ Server '${name}' not found.\n`);
208
+ return;
209
+ }
210
+
211
+ console.log(`\n 📋 MCP Server: ${config.name}`);
212
+ console.log(` Transport: ${config.transport}`);
213
+ if (config.command) console.log(` Command: ${config.command} ${(config.args || []).join(" ")}`);
214
+ if (config.url) console.log(` URL: ${config.url}`);
215
+ if (config.headers) console.log(` Headers: ${Object.keys(config.headers).join(", ")}`);
216
+ if (config.env) console.log(` Env: ${Object.keys(config.env).join(", ")}`);
217
+ console.log(` Scope: ${config.scope}`);
218
+ console.log(` Enabled: ${config.enabled}`);
219
+ console.log(` Created: ${config.createdAt}\n`);
220
+ }),
221
+ );