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.
@@ -0,0 +1,103 @@
1
+ /**
2
+ * voracode model — Manage AI providers and models
3
+ *
4
+ * Universal adapter system. BYOK only — no free managed models.
5
+ */
6
+
7
+ import { Command } from "commander";
8
+ import { ModelRouter } from "../models/router";
9
+
10
+ export const modelCommand = new Command("model")
11
+ .description("Manage AI providers and models")
12
+ .addCommand(
13
+ new Command("list")
14
+ .description("List available providers and models")
15
+ .option("-p, --provider <name>", "Filter by provider")
16
+ .action(async (options) => {
17
+ const router = new ModelRouter();
18
+
19
+ console.log("\n 📡 Available AI Providers:\n");
20
+
21
+ if (options.provider) {
22
+ const models = router.getModels(options.provider);
23
+ const hasKey = router.hasValidKey(options.provider);
24
+ console.log(` Provider: ${options.provider}`);
25
+ console.log(` Key: ${hasKey ? "✅ configured" : "❌ not configured"}`);
26
+ console.log(` Models:`);
27
+ for (const m of models) {
28
+ console.log(` • ${m}`);
29
+ }
30
+ console.log();
31
+ return;
32
+ }
33
+
34
+ const providers = [
35
+ { name: "OpenAI-compatible", protocols: "openai, deepseek, groq, together, ollama, openrouter, huggingface, fireworks, cerebras" },
36
+ { name: "Anthropic Messages", protocols: "anthropic" },
37
+ { name: "Google AI", protocols: "google" },
38
+ { name: "Custom", protocols: "Any OpenAI-compatible endpoint" },
39
+ ];
40
+
41
+ console.log(" ┌──────────────────────┬─────────────────────────────────────────────┐");
42
+ console.log(" │ Protocol │ Providers │");
43
+ console.log(" ├──────────────────────┼─────────────────────────────────────────────┤");
44
+ for (const p of providers) {
45
+ console.log(` │ ${p.name.padEnd(20)} │ ${p.protocols.padEnd(43)} │`);
46
+ }
47
+ console.log(" └──────────────────────┴─────────────────────────────────────────────┘");
48
+
49
+ console.log("\n 🔑 Key Status:");
50
+ for (const name of ["openai", "anthropic", "deepseek", "groq", "google", "openrouter"]) {
51
+ const status = router.hasValidKey(name) ? "✅" : "❌";
52
+ console.log(` ${status} ${name}`);
53
+ }
54
+
55
+ console.log("\n ⚠️ VORACODE is BYOK — no free managed models.");
56
+ console.log(" Configure: voracode key set <provider> <key>");
57
+ console.log(" Or set env: PROVIDER_API_KEY=sk-...\n");
58
+ }),
59
+ )
60
+ .addCommand(
61
+ new Command("set")
62
+ .description("Set active model for session")
63
+ .argument("<model>", "Model identifier (provider/model-name)")
64
+ .action(async (model) => {
65
+ if (!model.includes("/")) {
66
+ console.error("\n ✖ Use format: provider/model-name");
67
+ console.error(" Example: voracode model set deepseek/deepseek-chat\n");
68
+ process.exit(1);
69
+ }
70
+ console.log(`\n ✅ Active model set to: ${model}\n`);
71
+ }),
72
+ )
73
+ .addCommand(
74
+ new Command("test")
75
+ .description("Test connection to a provider")
76
+ .argument("[provider]", "Provider name")
77
+ .action(async (provider) => {
78
+ const router = new ModelRouter();
79
+ const name = provider || "deepseek";
80
+
81
+ const key = router.getKey(name);
82
+ if (!key) {
83
+ console.log(`\n ❌ No API key for '${name}'`);
84
+ console.log(` Configure: voracode key set ${name} <key>\n`);
85
+ return;
86
+ }
87
+
88
+ console.log(`\n 🔌 Testing: ${name}...`);
89
+
90
+ try {
91
+ const result = await router.chat(`${name}/test`, [
92
+ { role: "user", content: "Say 'ok' if you receive this." },
93
+ ], { maxTokens: 10 });
94
+ console.log(` ✅ Connection successful!`);
95
+ console.log(` Response: ${result.content.slice(0, 100)}`);
96
+ console.log(` Model: ${result.model}`);
97
+ console.log(` Tokens: ${result.usage.totalTokens}\n`);
98
+ } catch (error) {
99
+ console.log(` ❌ Connection failed:`);
100
+ console.log(` ${error instanceof Error ? error.message : String(error)}\n`);
101
+ }
102
+ }),
103
+ );
@@ -0,0 +1,49 @@
1
+ /**
2
+ * voracode plugin — Manage plugins
3
+ *
4
+ * Plugins extend VORACODE with custom tools, event hooks,
5
+ * and integrations. Published as voracode-plugin-* npm packages.
6
+ */
7
+
8
+ import { Command } from "commander";
9
+
10
+ export const pluginCommand = new Command("plugin")
11
+ .description("Manage plugins")
12
+ .alias("plugins")
13
+ .addCommand(
14
+ new Command("list")
15
+ .description("List installed plugins")
16
+ .action(async () => {
17
+ console.log("\n 🔌 Installed Plugins:");
18
+ console.log(" (Coming in Phase 1.4 — plugin system)\n");
19
+ }),
20
+ )
21
+ .addCommand(
22
+ new Command("install")
23
+ .description("Install a plugin")
24
+ .argument("<name>", "Plugin name (npm package)")
25
+ .option("-g, --global", "Install globally")
26
+ .option("-f, --force", "Replace existing version")
27
+ .action(async (name, options) => {
28
+ console.log(`\n 📦 Installing plugin: ${name}`);
29
+ if (options.global) console.log(" (global install)");
30
+ console.log(" (Coming in Phase 1.4 — plugin system)\n");
31
+ }),
32
+ )
33
+ .addCommand(
34
+ new Command("remove")
35
+ .description("Remove a plugin")
36
+ .argument("<name>", "Plugin name")
37
+ .action(async (name) => {
38
+ console.log(`\n 🗑️ Removing plugin: ${name}\n`);
39
+ }),
40
+ )
41
+ .addCommand(
42
+ new Command("create")
43
+ .description("Scaffold a new plugin project")
44
+ .argument("<name>", "Plugin name")
45
+ .action(async (name) => {
46
+ console.log(`\n ✨ Scaffolding plugin: ${name}`);
47
+ console.log(" (Coming in Phase 1.4 — plugin system)\n");
48
+ }),
49
+ );
package/src/cli/pro.ts ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * voracode pro — Pay-as-you-go credits for frontier models
3
+ *
4
+ * Like OpenCode Zen: credits-based, pay per token.
5
+ * Access to frontier models (GPT, Claude, Gemini) plus
6
+ * team management, auto-reload, monthly limits.
7
+ *
8
+ * USERS MUST BRING THEIR OWN API KEY FOR FREE TIER.
9
+ * Pro is OPTIONAL — for users who want managed access to frontier models.
10
+ */
11
+
12
+ import { Command } from "commander";
13
+
14
+ export const proCommand = new Command("pro")
15
+ .description("Pay-as-you-go credits for frontier AI models")
16
+ .addCommand(
17
+ new Command("balance")
18
+ .description("Check credit balance")
19
+ .action(async () => {
20
+ console.log("\n 💳 VORACODE Pro Balance:");
21
+ console.log(" • Current balance: $0.00");
22
+ console.log(" • Auto-reload: Off");
23
+ console.log("\n Add credits with 'voracode pro add'\n");
24
+ }),
25
+ )
26
+ .addCommand(
27
+ new Command("add")
28
+ .description("Add credits to your account")
29
+ .argument("[amount]", "Amount in USD", "20")
30
+ .action(async (amount) => {
31
+ console.log(`\n 💳 Adding $${amount} to VORACODE Pro`);
32
+ console.log(" (Coming in Phase 2 — payment integration)\n");
33
+ }),
34
+ )
35
+ .addCommand(
36
+ new Command("models")
37
+ .description("List available Pro models with pricing")
38
+ .action(async () => {
39
+ console.log("\n 📡 VORACODE Pro Models (per 1M tokens):");
40
+ console.log(" ┌──────────────────────┬──────────┬──────────┐");
41
+ console.log(" │ Model │ Input │ Output │");
42
+ console.log(" ├──────────────────────┼──────────┼──────────┤");
43
+ console.log(" │ GPT-5.6 Sol │ $5.00 │ $30.00 │");
44
+ console.log(" │ GPT-5.6 Terra │ $2.50 │ $15.00 │");
45
+ console.log(" │ Claude Opus 4.8 │ $5.00 │ $25.00 │");
46
+ console.log(" │ Claude Sonnet 4.6 │ $3.00 │ $15.00 │");
47
+ console.log(" │ Gemini 3.1 Pro │ $2.00 │ $12.00 │");
48
+ console.log(" │ Grok 4.5 │ $2.00 │ $6.00 │");
49
+ console.log(" └──────────────────────┴──────────┴──────────┘");
50
+ console.log(" (Full pricing coming in Phase 2)\n");
51
+ }),
52
+ )
53
+ .addCommand(
54
+ new Command("autoreload")
55
+ .description("Configure auto-reload settings")
56
+ .argument("[threshold]", "Balance threshold for auto-reload", "5")
57
+ .argument("[amount]", "Auto-reload amount", "20")
58
+ .option("--disable", "Disable auto-reload")
59
+ .action(async (threshold, amount, options) => {
60
+ if (options.disable) {
61
+ console.log("\n 🔄 Auto-reload disabled.\n");
62
+ } else {
63
+ console.log(`\n 🔄 Auto-reload set: reload $${amount} when balance < $${threshold}\n`);
64
+ }
65
+ }),
66
+ )
67
+ .addCommand(
68
+ new Command("limits")
69
+ .description("Set monthly usage limits")
70
+ .argument("[amount]", "Monthly limit in USD")
71
+ .option("--disable", "Disable monthly limit")
72
+ .action(async (amount, options) => {
73
+ if (options.disable) {
74
+ console.log("\n 📊 Monthly limit disabled.\n");
75
+ } else if (amount) {
76
+ console.log(`\n 📊 Monthly limit set to $${amount}/month\n`);
77
+ } else {
78
+ console.log("\n 📊 Monthly spending limit: Not set\n");
79
+ }
80
+ }),
81
+ )
82
+ .addCommand(
83
+ new Command("team")
84
+ .description("Manage team settings")
85
+ .option("--invite <email>", "Invite a team member")
86
+ .option("--remove <email>", "Remove a team member")
87
+ .option("--list", "List team members")
88
+ .option("--role <email:role>", "Set member role (admin|member)")
89
+ .action(async (options) => {
90
+ console.log("\n 👥 VORACODE Pro Team:");
91
+ if (options.invite) console.log(` 📧 Inviting: ${options.invite}`);
92
+ if (options.remove) console.log(` 🗑️ Removing: ${options.remove}`);
93
+ if (options.list) console.log(" (Team list coming in Phase 2)");
94
+ if (options.role) console.log(` 👤 Setting role: ${options.role}`);
95
+ console.log(" (Coming in Phase 2 — team management)\n");
96
+ }),
97
+ );
package/src/cli/run.ts ADDED
@@ -0,0 +1,101 @@
1
+ /**
2
+ * voracode run — Execute a task with AI
3
+ *
4
+ * Premium execution experience with Apple-style output.
5
+ */
6
+
7
+ import { Command } from "commander";
8
+ import { SessionManager } from "../session/manager";
9
+ import { ModelRouter } from "../models/router";
10
+ import { VoraDatabase } from "../storage/database";
11
+ import { c, spinner, success, error as err, footer } from "../ui/theme";
12
+
13
+ export const runCommand = new Command("run")
14
+ .description("Execute a task with AI")
15
+ .argument("[message]", "Task description")
16
+ .option("-m, --model <model>", "Model to use (provider/model-name)")
17
+ .option("-a, --agent <agent>", "Agent mode (build|plan|debug)")
18
+ .option("-y, --yes", "Auto-approve all actions")
19
+ .option("--session <id>", "Continue an existing session")
20
+ .option("--fork", "Fork session when continuing")
21
+ .option("--output <format>", "Output format (text|json|silent)", "text")
22
+ .option("--dry-run", "Show plan without executing")
23
+ .action(async (message, options) => {
24
+ if (!message) {
25
+ console.error(`\n ${c.error}✖ Please provide a task description.${c.reset}\n`);
26
+ console.error(` ${c.dim}Usage: ${c.brand}${c.bold}voracode${c.reset}${c.dim} run "your task here"${c.reset}\n`);
27
+ process.exit(1);
28
+ }
29
+
30
+ const sessions = new SessionManager();
31
+ const router = new ModelRouter();
32
+ const db = new VoraDatabase();
33
+
34
+ let modelRef = options.model || "openrouter/openrouter/free";
35
+ if (options.model) {
36
+ modelRef = options.model.includes("/") ? options.model : `${router.detectProvider(options.model)}/${options.model}`;
37
+ }
38
+
39
+ const provider = modelRef.split("/")[0];
40
+ if (!router.hasValidKey(provider)) {
41
+ console.log(`\n ${c.warning}◆${c.reset} No API key for ${c.bold}${provider}${c.reset}`);
42
+ console.log(` ${c.dim}Configure: voracode key set ${provider} <your-api-key>${c.reset}\n`);
43
+ process.exit(1);
44
+ }
45
+
46
+ let sessionId: string;
47
+ if (options.session) {
48
+ const existing = sessions.getSession(options.session);
49
+ if (!existing) {
50
+ console.error(`\n ${c.error}✖ Session not found: ${options.session}${c.reset}\n`);
51
+ process.exit(1);
52
+ }
53
+ sessionId = options.fork ? sessions.fork(options.session) || sessions.create(modelRef) : options.session;
54
+ } else {
55
+ sessionId = sessions.create(modelRef);
56
+ }
57
+
58
+ const isSilent = options.output === "silent";
59
+ const isJson = options.output === "json";
60
+
61
+ if (!isSilent && !isJson) {
62
+ console.log(`\n ${c.brand}◆${c.reset} ${c.bold}Running${c.reset} ${c.dim}${message}${c.reset}`);
63
+ console.log(` ${c.dim}Model:${c.reset} ${modelRef}`);
64
+ console.log(` ${c.dim}Session:${c.reset} ${sessionId.slice(0, 8)}...\n`);
65
+ if (options["dry-run"]) {
66
+ console.log(` ${c.warning}◆${c.reset} ${c.dim}Dry-run mode — no execution${c.reset}\n`);
67
+ }
68
+ }
69
+
70
+ try {
71
+ const result = await sessions.run(sessionId, message, modelRef, { maxTurns: 25 });
72
+
73
+ if (isJson) {
74
+ console.log(JSON.stringify(result, null, 2));
75
+ } else if (isSilent) {
76
+ console.log(result.content);
77
+ } else {
78
+ if (result.success) {
79
+ console.log(` ${c.success}${c.bold} Completed${c.reset} ${c.dim}${result.turns} turns · ${result.steps} steps · ${result.tokensUsed} tokens${c.reset}\n`);
80
+
81
+ // Render the AI response beautifully
82
+ console.log(result.content);
83
+
84
+ if (result.patternSuggestion) {
85
+ console.log(`\n ${c.cyan}◆${c.reset} ${c.dim}VORACODE noticed a pattern — run ${c.bold}voracode skill patterns${c.reset}${c.dim} to see${c.reset}`);
86
+ }
87
+
88
+ console.log(`\n ${c.dim}Session: ${c.brand}${sessionId.slice(0, 8)}${c.reset}`);
89
+ console.log(` ${c.dim}Data: ${sessions.getDbPath()}${c.reset}\n`);
90
+ } else {
91
+ console.log(`\n ${c.error}✖ ${result.error || "Task failed"}${c.reset}\n`);
92
+ process.exit(1);
93
+ }
94
+ }
95
+ } catch (error) {
96
+ console.error(`\n ${c.error}✖ ${error instanceof Error ? error.message : String(error)}${c.reset}\n`);
97
+ process.exit(1);
98
+ }
99
+
100
+ footer();
101
+ });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * voracode session — Manage AI agent sessions
3
+ */
4
+
5
+ import { Command } from "commander";
6
+ import { SessionManager } from "../session/manager";
7
+
8
+ // Shared manager instance
9
+ const getManager = () => new SessionManager();
10
+
11
+ export const sessionCommand = new Command("session")
12
+ .description("Manage AI agent sessions")
13
+ .addCommand(
14
+ new Command("list")
15
+ .description("List all sessions")
16
+ .option("--limit <number>", "Max sessions to show", "10")
17
+ .action(async (options) => {
18
+ const sessions = getManager();
19
+ const list = sessions.list(Number(options.limit));
20
+
21
+ if (list.length === 0) {
22
+ console.log("\n 📋 No sessions yet. Run 'voracode run' to create one.\n");
23
+ return;
24
+ }
25
+
26
+ console.log(`\n 📋 Recent sessions (last ${options.limit}):\n`);
27
+ for (const s of list) {
28
+ const date = new Date(s.createdAt + "Z").toLocaleString();
29
+ console.log(` 🆔 ${s.id.slice(0, 8)}... | ${s.title.slice(0, 40).padEnd(40)} | ${s.status.padEnd(10)} | ${date}`);
30
+ }
31
+ console.log(`\n Total: ${list.length} session(s)\n`);
32
+ }),
33
+ )
34
+ .addCommand(
35
+ new Command("show")
36
+ .description("Show session details")
37
+ .argument("<id>", "Session ID")
38
+ .action(async (id) => {
39
+ const sessions = getManager();
40
+ const session = sessions.getSession(id);
41
+
42
+ if (!session) {
43
+ console.error(`\n ✖ Session not found: ${id}\n`);
44
+ process.exit(1);
45
+ }
46
+
47
+ console.log(`\n 📄 Session: ${id}`);
48
+ console.log(` Title: ${session.title}`);
49
+ console.log(` Model: ${session.modelProvider}/${session.modelName}`);
50
+ console.log(` Status: ${session.status}`);
51
+ console.log(` Tokens: ${session.totalTokens}`);
52
+ console.log(` Turns: ${session.totalTurns}`);
53
+ console.log(` Created: ${session.createdAt}`);
54
+ console.log(` Updated: ${session.updatedAt}`);
55
+
56
+ const messages = sessions.getMessages(id);
57
+ console.log(` Messages: ${messages.length}\n`);
58
+ }),
59
+ )
60
+ .addCommand(
61
+ new Command("resume")
62
+ .description("Resume an existing session")
63
+ .argument("<id>", "Session ID to resume")
64
+ .option("-f, --fork", "Fork into a new session")
65
+ .action(async (id, options) => {
66
+ const sessions = getManager();
67
+ const session = sessions.getSession(id);
68
+
69
+ if (!session) {
70
+ console.error(`\n ✖ Session not found: ${id}\n`);
71
+ process.exit(1);
72
+ }
73
+
74
+ if (options.fork) {
75
+ const newId = sessions.fork(id);
76
+ if (newId) {
77
+ console.log(`\n 🌿 Forked session ${id.slice(0, 8)}... → ${newId.slice(0, 8)}...`);
78
+ console.log(` Run: voracode run "your task" --session ${newId}\n`);
79
+ }
80
+ } else {
81
+ console.log(`\n 🔄 Resuming session ${id.slice(0, 8)}...`);
82
+ console.log(` Last turn: ${session.totalTurns} turns, ${session.totalTokens} tokens`);
83
+ console.log(` Run: voracode run "your task" --session ${id}\n`);
84
+ }
85
+ }),
86
+ )
87
+ .addCommand(
88
+ new Command("delete")
89
+ .description("Delete a session")
90
+ .argument("<id>", "Session ID to delete")
91
+ .option("-f, --force", "Skip confirmation")
92
+ .action(async (id) => {
93
+ const sessions = getManager();
94
+ const session = sessions.getSession(id);
95
+
96
+ if (!session) {
97
+ console.error(`\n ✖ Session not found: ${id}\n`);
98
+ process.exit(1);
99
+ }
100
+
101
+ sessions.delete(id);
102
+ console.log(`\n 🗑️ Session ${id.slice(0, 8)}... deleted.\n`);
103
+ }),
104
+ )
105
+ .addCommand(
106
+ new Command("export")
107
+ .description("Export session as JSON")
108
+ .argument("<id>", "Session ID to export")
109
+ .argument("[file]", "Output file path")
110
+ .action(async (id, file) => {
111
+ const sessions = getManager();
112
+ const session = sessions.getSession(id);
113
+
114
+ if (!session) {
115
+ console.error(`\n ✖ Session not found: ${id}\n`);
116
+ process.exit(1);
117
+ }
118
+
119
+ const messages = sessions.getMessages(id);
120
+ const data = JSON.stringify({ session, messages }, null, 2);
121
+
122
+ if (file) {
123
+ await Bun.write(file, data);
124
+ console.log(`\n 💾 Session exported to ${file}\n`);
125
+ } else {
126
+ console.log(data);
127
+ }
128
+ }),
129
+ )
130
+ .addCommand(
131
+ new Command("import")
132
+ .description("Import session from JSON")
133
+ .argument("<file>", "JSON file path or URL")
134
+ .action(async (file) => {
135
+ console.log(`\n 📥 Importing session from ${file}\n`);
136
+ // TODO: implement import
137
+ }),
138
+ );
@@ -0,0 +1,156 @@
1
+ /**
2
+ * voracode skill — Manage skills
3
+ *
4
+ * Skills are reusable prompt templates that tell the agent how to handle
5
+ * specific tasks. Built-in skills + auto-learned skills from self-improvement.
6
+ */
7
+
8
+ import { Command } from "commander";
9
+ import { SelfImprovementEngine } from "../skills/self-improve/engine";
10
+ import { VoraDatabase } from "../storage/database";
11
+ import { existsSync, readFileSync, readdirSync } from "fs";
12
+ import { join } from "path";
13
+ import { homedir } from "os";
14
+
15
+ export const skillCommand = new Command("skill")
16
+ .description("Manage skills")
17
+ .alias("skills")
18
+ .addCommand(
19
+ new Command("list")
20
+ .description("List available skills")
21
+ .action(async () => {
22
+ const skillsDir = join(homedir(), ".config", "voracode", "skills");
23
+ const builtinSkills = [
24
+ { name: "web-search", description: "Search the web for information", source: "builtin" },
25
+ { name: "web-fetch", description: "Fetch and extract web content", source: "builtin" },
26
+ { name: "research", description: "Multi-source research with synthesis", source: "builtin" },
27
+ { name: "code-review", description: "AI-powered code review", source: "builtin" },
28
+ { name: "git-helper", description: "Git automation assistance", source: "builtin" },
29
+ { name: "deploy", description: "One-command deployment templates", source: "builtin" },
30
+ { name: "learn", description: "Self-improvement pattern tracker", source: "builtin" },
31
+ ];
32
+
33
+ // Find learned skills
34
+ const learnedSkills: Array<{ name: string; description: string; source: string }> = [];
35
+ if (existsSync(skillsDir)) {
36
+ for (const dir of readdirSync(skillsDir)) {
37
+ const skillPath = join(skillsDir, dir, "SKILL.md");
38
+ if (existsSync(skillPath)) {
39
+ try {
40
+ const content = readFileSync(skillPath, "utf-8");
41
+ const descMatch = content.match(/description:\s*(.+)/);
42
+ learnedSkills.push({
43
+ name: dir,
44
+ description: descMatch ? descMatch[1].trim() : "Learned skill",
45
+ source: "learned",
46
+ });
47
+ } catch {}
48
+ }
49
+ }
50
+ }
51
+
52
+ console.log("\n 🛠️ Available Skills:\n");
53
+
54
+ if (builtinSkills.length > 0) {
55
+ console.log(" Built-in:");
56
+ for (const s of builtinSkills) {
57
+ console.log(` 📦 ${s.name.padEnd(20)} ${s.description}`);
58
+ }
59
+ }
60
+
61
+ if (learnedSkills.length > 0) {
62
+ console.log("\n Learned (auto-generated):");
63
+ for (const s of learnedSkills) {
64
+ console.log(` 🧠 ${s.name.padEnd(20)} ${s.description}`);
65
+ }
66
+ }
67
+
68
+ console.log(`\n Total: ${builtinSkills.length + learnedSkills.length} skills\n`);
69
+ }),
70
+ )
71
+ .addCommand(
72
+ new Command("patterns")
73
+ .description("Show learning patterns detected")
74
+ .action(async () => {
75
+ const db = new VoraDatabase();
76
+ const sie = new SelfImprovementEngine(db);
77
+ const patterns = sie.getPatterns();
78
+
79
+ if (patterns.length === 0) {
80
+ console.log("\n 🧠 No patterns detected yet.");
81
+ console.log(" Use VORACODE regularly — it will learn from your tasks.\n");
82
+ return;
83
+ }
84
+
85
+ console.log("\n 🧠 Detected Patterns:\n");
86
+ for (const p of patterns) {
87
+ const total = p.successCount + p.failureCount;
88
+ const rate = total > 0 ? ((p.successCount / total) * 100).toFixed(0) : "0";
89
+ const status = p.userDecision === "accepted" ? "✅" : p.userDecision === "rejected" ? "❌" : "⏳";
90
+ console.log(` ${status} ${p.description.slice(0, 50).padEnd(50)} ${rate}% success (${total}x)`);
91
+ }
92
+
93
+ const stats = sie.getStats();
94
+ console.log(`\n 📊 Storage: ${stats.storageKB}KB | Patterns: ${stats.patterns} | Skills: ${stats.skills}\n`);
95
+ }),
96
+ )
97
+ .addCommand(
98
+ new Command("create")
99
+ .description("Create a skill from the latest pattern")
100
+ .argument("[pattern-id]", "Pattern ID to create skill from")
101
+ .action(async (patternId) => {
102
+ const db = new VoraDatabase();
103
+ const sie = new SelfImprovementEngine(db);
104
+ const patterns = sie.getPatterns();
105
+
106
+ if (patterns.length === 0) {
107
+ console.log("\n ⚠️ No patterns available to create skills from.");
108
+ console.log(" Use VORACODE more to generate patterns.\n");
109
+ return;
110
+ }
111
+
112
+ let pattern = patterns[0];
113
+ if (patternId) {
114
+ const found = patterns.find((p) => p.id === Number(patternId));
115
+ if (found) pattern = found;
116
+ }
117
+
118
+ const skillName = sie.generateSkill(pattern);
119
+ console.log(`\n ✅ Created skill: ${skillName}`);
120
+ console.log(` 📁 Location: ~/.config/voracode/skills/${skillName}/SKILL.md`);
121
+ console.log(` 🧠 Based on: ${pattern.description}`);
122
+ console.log(` 📊 Success rate: ${((pattern.successCount / (pattern.successCount + pattern.failureCount)) * 100).toFixed(0)}%\n`);
123
+ }),
124
+ )
125
+ .addCommand(
126
+ new Command("install")
127
+ .description("Install a skill from marketplace or path")
128
+ .argument("<source>", "Skill name, GitHub URL, or local path")
129
+ .action(async (source) => {
130
+ console.log(`\n 📦 Installing skill from: ${source}`);
131
+ console.log(" (Marketplace integration coming in Phase 2)\n");
132
+ }),
133
+ )
134
+ .addCommand(
135
+ new Command("remove")
136
+ .description("Remove an installed skill")
137
+ .argument("<name>", "Skill name")
138
+ .action(async (name) => {
139
+ console.log(`\n 🗑️ Removing skill: ${name}\n`);
140
+ }),
141
+ )
142
+ .addCommand(
143
+ new Command("rollback")
144
+ .description("Rollback a skill to its previous version")
145
+ .argument("<name>", "Skill name")
146
+ .action(async (name) => {
147
+ const db = new VoraDatabase();
148
+ const sie = new SelfImprovementEngine(db);
149
+ const success = sie.rollbackSkill(name);
150
+ if (success) {
151
+ console.log(`\n ✅ Rolled back skill: ${name}\n`);
152
+ } else {
153
+ console.log(`\n ⚠️ No previous version found for: ${name}\n`);
154
+ }
155
+ }),
156
+ );
@@ -0,0 +1,16 @@
1
+ /**
2
+ * voracode stats — Usage statistics and analytics
3
+ *
4
+ * All stats are LOCAL by default. No data sent anywhere.
5
+ * User must explicitly opt-in to share anonymized usage.
6
+ */
7
+
8
+ import { Command } from "commander";
9
+
10
+ export const statsCommand = new Command("stats")
11
+ .description("Show usage statistics (local only, zero telemetry)")
12
+ .option("--days <number>", "Number of days to show", "7")
13
+ .action(async (options) => {
14
+ console.log(`\n 📊 VORACODE Usage (last ${options.days} days):`);
15
+ console.log(" (Coming in Phase 1.3 — SQLite stats tracking)\n");
16
+ });