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,28 @@
1
+ /**
2
+ * voracode update — Self-update command
3
+ *
4
+ * Checks for new versions on GitHub and applies updates.
5
+ */
6
+
7
+ import { Command } from "commander";
8
+
9
+ export const updateCommand = new Command("update")
10
+ .description("Check for updates and update VORACODE")
11
+ .option("--check", "Only check for updates, don't install")
12
+ .option("--channel <channel>", "Release channel (stable|beta)", "stable")
13
+ .option("--force", "Force reinstall current version")
14
+ .action(async (options) => {
15
+ const currentVersion = "0.0.1"; // from package.json
16
+
17
+ console.log(`\n 📦 VORACODE v${currentVersion}`);
18
+ console.log(` 🔄 Channel: ${options.channel}`);
19
+ console.log(" 🔍 Checking for updates...");
20
+
21
+ // TODO: Implement actual GitHub release check
22
+ console.log(" (Coming in Phase 1.4 — update system)");
23
+
24
+ console.log("\n To update manually, run:");
25
+ console.log(" npm update -g voracode");
26
+ console.log(" # or: brew upgrade voracode");
27
+ console.log(" # or: scoop update voracode\n");
28
+ });
@@ -0,0 +1,256 @@
1
+ /**
2
+ * VORACODE Agent — Tool-calling agent loop
3
+ *
4
+ * The core intelligence: model suggests tool calls → tools execute →
5
+ * results fed back → model continues → repeat until done.
6
+ *
7
+ * Uses OpenAI function calling to give the model real tool access.
8
+ */
9
+
10
+ import { ModelRouter, type ChatMessage, type ChatOptions } from "../models/router";
11
+ import { VoraDatabase } from "../storage/database";
12
+ import { ToolExecutor } from "../tools/executor";
13
+ import { SelfImprovementEngine } from "../skills/self-improve/engine";
14
+
15
+ export interface AgentResult {
16
+ success: boolean;
17
+ content: string;
18
+ sessionId: string;
19
+ tokensUsed: number;
20
+ turns: number;
21
+ steps: number;
22
+ error?: string;
23
+ patternSuggestion?: string;
24
+ }
25
+
26
+ export class Agent {
27
+ private router: ModelRouter;
28
+ private db: VoraDatabase;
29
+ private tools: ToolExecutor;
30
+ private sie: SelfImprovementEngine;
31
+ private systemPrompt: string;
32
+
33
+ constructor() {
34
+ this.router = new ModelRouter();
35
+ this.db = new VoraDatabase();
36
+ this.tools = new ToolExecutor(this.db);
37
+ this.sie = new SelfImprovementEngine(this.db);
38
+ this.systemPrompt = this.buildSystemPrompt();
39
+ }
40
+
41
+ private buildSystemPrompt(): string {
42
+ return `You are VORACODE, an AI engineering partner that lives in the terminal.
43
+
44
+ You help users with coding tasks. You have access to real tools that let you read, write, and execute code.
45
+
46
+ ## How you work
47
+ 1. Understand what the user wants
48
+ 2. Use tools to explore the project when needed
49
+ 3. Plan the changes
50
+ 4. Execute step by step using the tools available to you
51
+ 5. Tell the user when you're done
52
+
53
+ ## Rules
54
+ - NEVER execute dangerous commands (rm -rf /, sudo, mkfs, dd, fork bombs)
55
+ - NEVER output API keys, passwords, or secrets
56
+ - Show what you're doing before doing it
57
+ - Ask the user before destructive operations
58
+ - Use the think tool to reason through complex problems before acting
59
+ - When the task is complete, summarize what you did
60
+
61
+ ## Available Tools
62
+ You have file_read, file_write, file_edit, bash, git_status, git_diff, git_commit,
63
+ code_search, web_fetch, think, and list_files at your disposal.
64
+ Use them to accomplish the task. Do NOT just describe what to do — USE the tools.
65
+
66
+ When the task is complete, summarize what was done.`;
67
+ }
68
+
69
+ /**
70
+ * Run the agent loop with full tool-calling
71
+ */
72
+ async runTurn(sessionId: string, userMessage: string, modelRef: string, options?: { maxTurns?: number }): Promise<AgentResult> {
73
+ const startTime = Date.now();
74
+ const maxTurns = options?.maxTurns || 15;
75
+ let totalTokens = 0;
76
+ let turns = 0;
77
+ let steps = 0;
78
+ let toolOnlyTurns = 0; // Detection: model only using tools, never summarizing
79
+
80
+ // Get tool schemas to send with each API call
81
+ const toolSchemas = this.tools.getToolSchemas();
82
+
83
+ // Build message history
84
+ const messages: ChatMessage[] = [
85
+ { role: "system", content: this.systemPrompt },
86
+ { role: "user", content: userMessage },
87
+ ];
88
+
89
+ // Store the initial user message
90
+ this.db.addMessage(sessionId, "user", userMessage);
91
+
92
+ const toolsUsed: string[] = [];
93
+ const filesChanged: string[] = [];
94
+ const commandsRun: string[] = [];
95
+
96
+ try {
97
+ while (turns < maxTurns) {
98
+ turns++;
99
+
100
+ const chatOptions: ChatOptions = {
101
+ model: modelRef,
102
+ maxTokens: 2048,
103
+ temperature: 0.3,
104
+ tools: toolSchemas,
105
+ toolChoice: "auto",
106
+ };
107
+
108
+ // Call the model
109
+ const response = await this.router.chat(modelRef, messages, chatOptions);
110
+ totalTokens += response.usage.totalTokens;
111
+ this.db.recordApiCall(response.usage.inputTokens, response.usage.outputTokens);
112
+
113
+ // Store assistant response
114
+ if (response.content) {
115
+ this.db.addMessage(sessionId, "assistant", response.content, response.usage.totalTokens);
116
+ }
117
+
118
+ // CASE 1: Model wants to use tools
119
+ if (response.toolCalls && response.toolCalls.length > 0) {
120
+ // If model keeps using tools without responding, detect infinite loop
121
+ toolOnlyTurns++;
122
+ if (toolOnlyTurns > 10) {
123
+ return {
124
+ success: true,
125
+ content: "Task reached maximum tool iterations. Results so far are applied.",
126
+ sessionId,
127
+ tokensUsed: totalTokens,
128
+ turns,
129
+ steps,
130
+ };
131
+ }
132
+ // Add the assistant message with tool_calls to history
133
+ messages.push({
134
+ role: "assistant",
135
+ content: response.content || "",
136
+ tool_calls: response.toolCalls,
137
+ });
138
+
139
+ // Track tools used for self-improvement
140
+
141
+ // Execute each tool call
142
+ for (const toolCall of response.toolCalls) {
143
+ steps++;
144
+ toolsUsed.push(toolCall.function.name);
145
+
146
+ // Track file changes and commands for self-improvement
147
+ try {
148
+ const args = JSON.parse(toolCall.function.arguments);
149
+ if ((toolCall.function.name === "file_write" || toolCall.function.name === "file_edit") && args.path) {
150
+ filesChanged.push(args.path);
151
+ }
152
+ if (toolCall.function.name === "bash" && args.command) {
153
+ commandsRun.push(args.command);
154
+ }
155
+ } catch {}
156
+
157
+ let result: unknown;
158
+ let error = false;
159
+
160
+ try {
161
+ result = await this.tools.execute(toolCall.function.name, toolCall.function.arguments);
162
+ } catch (e) {
163
+ result = `Error: ${e instanceof Error ? e.message : String(e)}`;
164
+ error = true;
165
+ }
166
+
167
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
168
+
169
+ // Truncate very long results
170
+ const truncated = resultStr.length > 5000 ? resultStr.slice(0, 5000) + "\n... (truncated)" : resultStr;
171
+
172
+ // Audit log
173
+ this.db.logAudit(
174
+ error ? "tool_error" : "tool_execute",
175
+ JSON.stringify({ tool: toolCall.function.name }),
176
+ !error,
177
+ );
178
+
179
+ // Add tool result to conversation history
180
+ messages.push({
181
+ role: "tool",
182
+ content: truncated,
183
+ tool_call_id: toolCall.id,
184
+ });
185
+ }
186
+
187
+ // Continue the loop — model will see tool results and decide next step
188
+ continue;
189
+ }
190
+
191
+ // CASE 2: Model responded with text (no tool calls) — task is complete
192
+ this.db.updateSession(sessionId, {
193
+ total_tokens: totalTokens,
194
+ total_turns: turns,
195
+ status: "completed",
196
+ });
197
+
198
+ // Self-improvement: record task pattern and check for suggestions
199
+ let patternSuggestion: string | undefined;
200
+ if (toolsUsed.length > 0) {
201
+ const pattern = this.sie.recordTask(
202
+ userMessage,
203
+ toolsUsed,
204
+ filesChanged,
205
+ commandsRun,
206
+ true, // success
207
+ );
208
+
209
+ if (pattern) {
210
+ patternSuggestion = `\n\n💡 VORACODE noticed you've done this ${pattern.successCount + 1} times with ${(pattern.successCount / (pattern.successCount + pattern.failureCount) * 100).toFixed(0)}% success.\n Would you like me to create a reusable skill for this?\n Run: voracode skill create ${pattern.description.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 30)}`;
211
+ }
212
+ }
213
+
214
+ return {
215
+ success: true,
216
+ content: response.content + (patternSuggestion || ""),
217
+ sessionId,
218
+ tokensUsed: totalTokens,
219
+ turns,
220
+ steps,
221
+ patternSuggestion,
222
+ };
223
+ }
224
+
225
+ // MAX TURNS REACHED
226
+ this.db.updateSession(sessionId, {
227
+ total_tokens: totalTokens,
228
+ total_turns: turns,
229
+ status: "completed",
230
+ });
231
+
232
+ return {
233
+ success: true,
234
+ content: "Task reached maximum turns. You can continue with more instructions.",
235
+ sessionId,
236
+ tokensUsed: totalTokens,
237
+ turns,
238
+ steps,
239
+ };
240
+ } catch (error) {
241
+ const errorMsg = error instanceof Error ? error.message : String(error);
242
+ this.db.logAudit("agent_error", errorMsg, false);
243
+ this.db.updateSession(sessionId, { status: "error" });
244
+
245
+ return {
246
+ success: false,
247
+ content: "",
248
+ sessionId,
249
+ tokensUsed: totalTokens,
250
+ turns,
251
+ steps,
252
+ error: errorMsg,
253
+ };
254
+ }
255
+ }
256
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * VORACODE Sub-Agent System — Parallel task execution agents
3
+ *
4
+ * Like Claude Code's sub-agents, Cursor's parallel agents:
5
+ * - Each sub-agent gets its own context window
6
+ * - Runs independently with specific tools
7
+ * - Reports back to main agent
8
+ * - Can use different models per task
9
+ */
10
+
11
+ import { ModelRouter, type ChatMessage } from "../models/router";
12
+ import { ToolExecutor } from "../tools/executor";
13
+ import { VoraDatabase } from "../storage/database";
14
+
15
+ export interface SubAgentTask {
16
+ id: string;
17
+ description: string;
18
+ modelRef?: string;
19
+ tools?: string[];
20
+ context?: string;
21
+ maxTurns?: number;
22
+ }
23
+
24
+ export interface SubAgentResult {
25
+ id: string;
26
+ success: boolean;
27
+ content: string;
28
+ tokensUsed: number;
29
+ turns: number;
30
+ error?: string;
31
+ }
32
+
33
+ export class SubAgentManager {
34
+ private router: ModelRouter;
35
+ private tools: ToolExecutor;
36
+ private db: VoraDatabase;
37
+
38
+ constructor() {
39
+ this.router = new ModelRouter();
40
+ this.db = new VoraDatabase();
41
+ this.tools = new ToolExecutor(this.db);
42
+ }
43
+
44
+ /**
45
+ * Run multiple sub-agents in parallel
46
+ */
47
+ async runParallel(tasks: SubAgentTask[], mainModel = "deepseek/deepseek-chat"): Promise<SubAgentResult[]> {
48
+ const results: SubAgentResult[] = [];
49
+ const BATCH_SIZE = 3; // Max 3 parallel agents
50
+
51
+ for (let i = 0; i < tasks.length; i += BATCH_SIZE) {
52
+ const batch = tasks.slice(i, i + BATCH_SIZE);
53
+ const batchResults = await Promise.all(
54
+ batch.map((task) => this.runSingle(task, mainModel)),
55
+ );
56
+ results.push(...batchResults);
57
+ }
58
+
59
+ return results;
60
+ }
61
+
62
+ /**
63
+ * Run a single sub-agent
64
+ */
65
+ async runSingle(task: SubAgentTask, defaultModel: string): Promise<SubAgentResult> {
66
+ const modelRef = task.modelRef || defaultModel;
67
+ const maxTurns = task.maxTurns || 5;
68
+ let turns = 0;
69
+ let totalTokens = 0;
70
+
71
+ const systemPrompt = `You are a VORACODE sub-agent handling a specific task.
72
+ Your task: ${task.description}
73
+
74
+ You have access to tools: ${(task.tools || ["file_read", "file_write", "bash", "think"]).join(", ")}
75
+
76
+ Focus ONLY on your assigned task. Report back concisely.`;
77
+
78
+ const messages: ChatMessage[] = [
79
+ { role: "system", content: systemPrompt },
80
+ ...(task.context ? [{ role: "user" as const, content: task.context }] : []),
81
+ { role: "user" as const, content: `Execute this task: ${task.description}` },
82
+ ];
83
+
84
+ try {
85
+ while (turns < maxTurns) {
86
+ turns++;
87
+ const response = await this.router.chat(modelRef, messages, {
88
+ model: modelRef,
89
+ maxTokens: 1024,
90
+ temperature: 0.3,
91
+ tools: this.getToolSchemas(task.tools),
92
+ toolChoice: "auto" as const,
93
+ });
94
+
95
+ totalTokens += response.usage.totalTokens;
96
+
97
+ if (response.toolCalls && response.toolCalls.length > 0) {
98
+ messages.push({ role: "assistant", content: response.content || "", tool_calls: response.toolCalls });
99
+
100
+ for (const tc of response.toolCalls) {
101
+ const result = await this.tools.execute(tc.function.name, tc.function.arguments);
102
+ const resultStr = typeof result === "string" ? result : JSON.stringify(result);
103
+ messages.push({ role: "tool", content: resultStr.slice(0, 3000), tool_call_id: tc.id });
104
+ }
105
+ continue;
106
+ }
107
+
108
+ return { id: task.id, success: true, content: response.content, tokensUsed: totalTokens, turns };
109
+ }
110
+
111
+ return { id: task.id, success: true, content: "Sub-agent reached max turns.", tokensUsed: totalTokens, turns };
112
+ } catch (error) {
113
+ return { id: task.id, success: false, content: "", tokensUsed: totalTokens, turns, error: String(error) };
114
+ }
115
+ }
116
+
117
+ private getToolSchemas(allowedTools?: string[]) {
118
+ const all = this.tools.getToolSchemas();
119
+ if (!allowedTools) return all;
120
+ return all.filter((t) => allowedTools.includes(t.function.name));
121
+ }
122
+ }
package/src/main.ts ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * VORACODE — AI Coding Agent
4
+ * One agent, every surface.
5
+ *
6
+ * Premium CLI with ALL features integrated.
7
+ */
8
+
9
+ import { Command } from "commander";
10
+ import { runCommand } from "./cli/run";
11
+ import { initCommand } from "./cli/init";
12
+ import { sessionCommand } from "./cli/session";
13
+ import { modelCommand } from "./cli/model";
14
+ import { skillCommand } from "./cli/skill";
15
+ import { keyCommand } from "./cli/key";
16
+ import { configCommand } from "./cli/config";
17
+ import { pluginCommand } from "./cli/plugin";
18
+ import { mcpCommand } from "./cli/mcp";
19
+ import { statsCommand } from "./cli/stats";
20
+ import { doctorCommand } from "./cli/doctor";
21
+ import { auditCommand } from "./cli/audit";
22
+ import { liteCommand } from "./cli/lite";
23
+ import { proCommand } from "./cli/pro";
24
+ import { updateCommand } from "./cli/update";
25
+ import { version } from "../package.json";
26
+ import { c, printBanner, box, footer } from "./ui/theme";
27
+ import { ALL_PROVIDERS, BUILTIN_MCP_SERVERS } from "./models/adapters/all-providers";
28
+
29
+ const VERSION = version || "0.0.1";
30
+ const NAME = "voracode";
31
+
32
+ async function main(): Promise<void> {
33
+ const program = new Command();
34
+
35
+ program
36
+ .name(NAME)
37
+ .description(`${c.brand}${c.bold}${NAME}${c.reset} — ${c.dim}AI Coding Agent${c.reset}`)
38
+ .version(VERSION, "-v, --version", "Show version")
39
+ .helpOption("-h, --help", "Show help")
40
+ .helpCommand(false);
41
+
42
+ program.addCommand(runCommand);
43
+ program.addCommand(initCommand);
44
+ program.addCommand(sessionCommand);
45
+ program.addCommand(modelCommand);
46
+ program.addCommand(skillCommand);
47
+ program.addCommand(keyCommand);
48
+ program.addCommand(configCommand);
49
+ program.addCommand(pluginCommand);
50
+ program.addCommand(mcpCommand);
51
+ program.addCommand(auditCommand);
52
+ program.addCommand(liteCommand);
53
+ program.addCommand(proCommand);
54
+ program.addCommand(statsCommand);
55
+ program.addCommand(doctorCommand);
56
+ program.addCommand(updateCommand);
57
+
58
+ if (process.argv.length <= 2) {
59
+ printBanner();
60
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}run <task>${c.reset} Execute a task`);
61
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}init${c.reset} Initialize project`);
62
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}session list${c.reset} Manage sessions`);
63
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}model list${c.reset} ${ALL_PROVIDERS.length} providers ready`);
64
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}mcp list${c.reset} ${BUILTIN_MCP_SERVERS.length} MCP servers`);
65
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}doctor${c.reset} Health check`);
66
+ console.log(` ${c.dim}${c.bold}${NAME}${c.reset} ${c.dim}--help${c.reset} All commands`);
67
+ footer();
68
+ return;
69
+ }
70
+
71
+ await program.parseAsync(process.argv);
72
+ }
73
+
74
+ main().catch((error) => {
75
+ console.error(`\n ${c.error}✖ ${c.reset}${c.error}${error instanceof Error ? error.message : String(error)}${c.reset}\n`);
76
+ process.exit(1);
77
+ });