zero-ai-cli 1.0.0
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/MERGE_REPORT.md +265 -0
- package/README.md +127 -0
- package/package.json +55 -0
- package/packages/cli/src/index.ts +271 -0
- package/packages/cli/src/interactive.ts +190 -0
- package/packages/cli/src/once.ts +50 -0
- package/packages/core/src/agent/config-loader.ts +174 -0
- package/packages/core/src/agent/engine.ts +266 -0
- package/packages/core/src/agent/registry-tools.ts +58 -0
- package/packages/core/src/agent/registry.ts +213 -0
- package/packages/core/src/commands/index.ts +116 -0
- package/packages/core/src/config/index.ts +139 -0
- package/packages/core/src/confirmation/message-bus.ts +206 -0
- package/packages/core/src/diff/index.ts +232 -0
- package/packages/core/src/diff-detect/index.ts +207 -0
- package/packages/core/src/edit-coder/index.ts +194 -0
- package/packages/core/src/events/index.ts +151 -0
- package/packages/core/src/file-tracker/index.ts +183 -0
- package/packages/core/src/git/index.ts +305 -0
- package/packages/core/src/history/index.ts +236 -0
- package/packages/core/src/image/index.ts +131 -0
- package/packages/core/src/index.ts +131 -0
- package/packages/core/src/lsp/index.ts +251 -0
- package/packages/core/src/mcp/index.ts +186 -0
- package/packages/core/src/memory/index.ts +141 -0
- package/packages/core/src/memory/prompts.ts +52 -0
- package/packages/core/src/orchestrator/protocol.ts +140 -0
- package/packages/core/src/orchestrator/server.ts +319 -0
- package/packages/core/src/output/index.ts +164 -0
- package/packages/core/src/permissions/index.ts +126 -0
- package/packages/core/src/prompts/engine.ts +188 -0
- package/packages/core/src/providers/registry.ts +687 -0
- package/packages/core/src/routing/model-router.ts +160 -0
- package/packages/core/src/server/index.ts +232 -0
- package/packages/core/src/session/index.ts +174 -0
- package/packages/core/src/session/service.ts +322 -0
- package/packages/core/src/skills/index.ts +205 -0
- package/packages/core/src/streaming/index.ts +166 -0
- package/packages/core/src/sub-agent/index.ts +179 -0
- package/packages/core/src/tools/builtin.ts +568 -0
- package/packages/core/src/types.ts +356 -0
- package/packages/sdk/src/index.ts +247 -0
- package/packages/tui/src/index.ts +141 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ZERO CLI - Main Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Command-line interface for ZERO AI Coding Assistant
|
|
6
|
+
* Inspired by: gemini-cli, crush, opencode CLI interfaces
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Command } from "commander";
|
|
10
|
+
import {
|
|
11
|
+
ConfigManager,
|
|
12
|
+
ProviderRegistry,
|
|
13
|
+
ToolRegistry,
|
|
14
|
+
AgentRegistry,
|
|
15
|
+
DEFAULT_AGENTS,
|
|
16
|
+
SessionManager,
|
|
17
|
+
MCPManager,
|
|
18
|
+
FileMemoryStore,
|
|
19
|
+
ContextManager,
|
|
20
|
+
createPermissionHandler,
|
|
21
|
+
getAllBuiltinTools,
|
|
22
|
+
AgentEngine,
|
|
23
|
+
DEFAULT_PERMISSION_CONFIG,
|
|
24
|
+
} from "@zero/core";
|
|
25
|
+
import type { PermissionRequest, PermissionDecision, Message } from "@zero/core";
|
|
26
|
+
import { runInteractive } from "./interactive.js";
|
|
27
|
+
import { runOnce } from "./once.js";
|
|
28
|
+
|
|
29
|
+
const program = new Command();
|
|
30
|
+
|
|
31
|
+
program
|
|
32
|
+
.name("zero")
|
|
33
|
+
.description("ZERO - Unified AI Coding Assistant")
|
|
34
|
+
.version("1.0.0");
|
|
35
|
+
|
|
36
|
+
// Default interactive mode
|
|
37
|
+
program
|
|
38
|
+
.argument("[prompt]", "Initial prompt to send to ZERO")
|
|
39
|
+
.option("-m, --model <model>", "Model to use", "gpt-4o")
|
|
40
|
+
.option("-p, --provider <provider>", "LLM provider", "openai")
|
|
41
|
+
.option("-d, --directory <path>", "Working directory", process.cwd())
|
|
42
|
+
.option("--auto-approve", "Auto-approve all tool calls", false)
|
|
43
|
+
.option("--verbose", "Show verbose output", false)
|
|
44
|
+
.action(async (prompt, options) => {
|
|
45
|
+
const { zero, session, memory, contextManager } = await initializeZero(options);
|
|
46
|
+
|
|
47
|
+
if (prompt) {
|
|
48
|
+
await runOnce(zero, prompt, session.id, options);
|
|
49
|
+
} else {
|
|
50
|
+
await runInteractive(zero, session.id, memory, contextManager, options);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Init command
|
|
55
|
+
program
|
|
56
|
+
.command("init")
|
|
57
|
+
.description("Initialize ZERO in the current project")
|
|
58
|
+
.action(async () => {
|
|
59
|
+
const config = new ConfigManager(process.cwd());
|
|
60
|
+
await config.init();
|
|
61
|
+
console.log("✅ ZERO initialized successfully!");
|
|
62
|
+
console.log(` Config: ${config.configPath || "zero.config.json"}`);
|
|
63
|
+
console.log(` Data: ${config.getDataDir()}`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Config command
|
|
67
|
+
program
|
|
68
|
+
.command("config")
|
|
69
|
+
.description("Show or modify ZERO configuration")
|
|
70
|
+
.option("--show", "Show current configuration")
|
|
71
|
+
.option("--set <key=value>", "Set a configuration value")
|
|
72
|
+
.action(async (options) => {
|
|
73
|
+
const config = new ConfigManager(process.cwd());
|
|
74
|
+
await config.load();
|
|
75
|
+
|
|
76
|
+
if (options.show || (!options.set)) {
|
|
77
|
+
console.log(JSON.stringify(config.get(), null, 2));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (options.set) {
|
|
81
|
+
const [key, value] = options.set.split("=");
|
|
82
|
+
config.update({ [key]: value } as any);
|
|
83
|
+
await config.save();
|
|
84
|
+
console.log(`✅ Set ${key} = ${value}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Session management
|
|
89
|
+
program
|
|
90
|
+
.command("sessions")
|
|
91
|
+
.description("List or manage sessions")
|
|
92
|
+
.option("--list", "List all sessions")
|
|
93
|
+
.option("--delete <id>", "Delete a session")
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
const config = new ConfigManager(process.cwd());
|
|
96
|
+
await config.load();
|
|
97
|
+
const sessionMgr = new SessionManager({
|
|
98
|
+
storagePath: `${config.getDataDir()}/sessions`,
|
|
99
|
+
defaultConfig: {},
|
|
100
|
+
});
|
|
101
|
+
await sessionMgr.loadAll();
|
|
102
|
+
|
|
103
|
+
if (options.delete) {
|
|
104
|
+
await sessionMgr.delete(options.delete);
|
|
105
|
+
console.log(`✅ Session ${options.delete} deleted`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const sessions = sessionMgr.list();
|
|
110
|
+
if (sessions.length === 0) {
|
|
111
|
+
console.log("No sessions found.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log("📋 Sessions:");
|
|
116
|
+
for (const s of sessions) {
|
|
117
|
+
const date = new Date(s.updatedAt).toLocaleString();
|
|
118
|
+
console.log(` ${s.id} - ${s.name} (${s.messages.length} messages) - ${date}`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Agents list
|
|
123
|
+
program
|
|
124
|
+
.command("agents")
|
|
125
|
+
.description("List available agents")
|
|
126
|
+
.action(() => {
|
|
127
|
+
console.log("🤖 Available Agents:");
|
|
128
|
+
for (const agent of DEFAULT_AGENTS) {
|
|
129
|
+
console.log(` ${agent.id}: ${agent.description}`);
|
|
130
|
+
console.log(` Tools: ${agent.tools.join(", ")}`);
|
|
131
|
+
console.log(` Max turns: ${agent.maxTurns}`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Tools list
|
|
136
|
+
program
|
|
137
|
+
.command("tools")
|
|
138
|
+
.description("List available tools")
|
|
139
|
+
.action(() => {
|
|
140
|
+
const tools = getAllBuiltinTools();
|
|
141
|
+
console.log("🔧 Available Tools:");
|
|
142
|
+
for (const tool of tools) {
|
|
143
|
+
const approval = tool.requiresApproval ? "⚠️ requires approval" : "✅ auto-approved";
|
|
144
|
+
console.log(` ${tool.name} [${tool.category}] - ${approval}`);
|
|
145
|
+
console.log(` ${tool.description.split("\n")[0]}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Providers list
|
|
150
|
+
program
|
|
151
|
+
.command("providers")
|
|
152
|
+
.description("List available LLM providers")
|
|
153
|
+
.action(() => {
|
|
154
|
+
const registry = new ProviderRegistry();
|
|
155
|
+
registry.loadBuiltins();
|
|
156
|
+
|
|
157
|
+
console.log("🌐 Available Providers:");
|
|
158
|
+
for (const provider of registry.list()) {
|
|
159
|
+
console.log(` ${provider.id}: ${provider.name}`);
|
|
160
|
+
for (const model of provider.models) {
|
|
161
|
+
console.log(` ${model.id} - ${model.name} (${model.contextWindow} context)`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
async function initializeZero(options: any) {
|
|
167
|
+
const config = new ConfigManager(options.directory || process.cwd());
|
|
168
|
+
await config.load();
|
|
169
|
+
|
|
170
|
+
// Initialize providers
|
|
171
|
+
const providerRegistry = new ProviderRegistry();
|
|
172
|
+
providerRegistry.loadBuiltins();
|
|
173
|
+
|
|
174
|
+
// Initialize tools
|
|
175
|
+
const toolRegistry = new ToolRegistry();
|
|
176
|
+
for (const tool of getAllBuiltinTools()) {
|
|
177
|
+
toolRegistry.register(tool);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Initialize agents
|
|
181
|
+
const sessionMgr = new SessionManager({
|
|
182
|
+
storagePath: `${config.getDataDir()}/sessions`,
|
|
183
|
+
defaultConfig: {
|
|
184
|
+
provider: options.provider || config.get().defaultProvider,
|
|
185
|
+
model: options.model || config.get().defaultModel,
|
|
186
|
+
workingDirectory: options.directory || process.cwd(),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
await sessionMgr.loadAll();
|
|
190
|
+
|
|
191
|
+
const session = await sessionMgr.create();
|
|
192
|
+
|
|
193
|
+
// Initialize memory
|
|
194
|
+
const memory = new FileMemoryStore(`${config.getDataDir()}/memory`);
|
|
195
|
+
await memory.init();
|
|
196
|
+
const contextManager = new ContextManager(memory);
|
|
197
|
+
|
|
198
|
+
// Create permission handler
|
|
199
|
+
let permissionHandler;
|
|
200
|
+
if (options.autoApprove) {
|
|
201
|
+
permissionHandler = async () => "allow" as PermissionDecision;
|
|
202
|
+
} else {
|
|
203
|
+
permissionHandler = createPermissionHandler(
|
|
204
|
+
DEFAULT_PERMISSION_CONFIG,
|
|
205
|
+
async (request: PermissionRequest) => {
|
|
206
|
+
// In CLI mode, ask the user
|
|
207
|
+
const readline = await import("node:readline");
|
|
208
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
209
|
+
return new Promise<PermissionDecision>((resolve) => {
|
|
210
|
+
rl.question(`\n⚠️ Permission required: ${request.type} - ${request.resource}\n ${request.details || ""}\n Allow? [y/n/a(lways)] `, (answer) => {
|
|
211
|
+
rl.close();
|
|
212
|
+
if (answer === "a") resolve("allow_always");
|
|
213
|
+
else if (answer === "y") resolve("allow");
|
|
214
|
+
else resolve("deny");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Create agent registry
|
|
222
|
+
const agentRegistry = new AgentRegistry({
|
|
223
|
+
clientFactory: (model) => {
|
|
224
|
+
const modelId = model === "default" ? (options.model || config.get().defaultModel) : model;
|
|
225
|
+
const providerId = options.provider || config.get().defaultProvider;
|
|
226
|
+
const providerConfig = config.get().providers[providerId] || {};
|
|
227
|
+
return providerRegistry.createClient(providerId, {
|
|
228
|
+
model: modelId,
|
|
229
|
+
apiKey: providerConfig.apiKey,
|
|
230
|
+
baseUrl: providerConfig.baseUrl,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
tools: toolRegistry.getMap(),
|
|
234
|
+
permissionHandler,
|
|
235
|
+
workingDirectory: options.directory || process.cwd(),
|
|
236
|
+
sessionId: session.id,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Register default agents
|
|
240
|
+
for (const agent of DEFAULT_AGENTS) {
|
|
241
|
+
agentRegistry.register(agent);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Initialize MCP
|
|
245
|
+
const mcpManager = new MCPManager();
|
|
246
|
+
const mcpServers = config.get().mcpServers || [];
|
|
247
|
+
for (const server of mcpServers) {
|
|
248
|
+
await mcpManager.connect(server);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Register MCP tools
|
|
252
|
+
for (const tool of mcpManager.getToolsAsDefinitions()) {
|
|
253
|
+
toolRegistry.register(tool);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
zero: {
|
|
258
|
+
agentRegistry,
|
|
259
|
+
toolRegistry,
|
|
260
|
+
providerRegistry,
|
|
261
|
+
sessionMgr,
|
|
262
|
+
mcpManager,
|
|
263
|
+
config,
|
|
264
|
+
},
|
|
265
|
+
session,
|
|
266
|
+
memory,
|
|
267
|
+
contextManager,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
program.parse();
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZERO CLI - Interactive Mode
|
|
3
|
+
* REPL interface with streaming, colors, and tool approval
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as readline from "node:readline";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import type { AgentEngine, Message, FileMemoryStore, ContextManager } from "@zero/core";
|
|
9
|
+
|
|
10
|
+
interface ZeroRuntime {
|
|
11
|
+
agentRegistry: any;
|
|
12
|
+
toolRegistry: any;
|
|
13
|
+
providerRegistry: any;
|
|
14
|
+
sessionMgr: any;
|
|
15
|
+
mcpManager: any;
|
|
16
|
+
config: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const BANNER = `
|
|
20
|
+
${chalk.bold.hex("#8B5CF6")("╔═══════════════════════════════════════╗")}
|
|
21
|
+
${chalk.bold.hex("#8B5CF6")("║")} ${chalk.bold.white("ZERO")} ${chalk.gray("v1.0.0")} — ${chalk.gray("Unified AI Assistant")} ${chalk.bold.hex("#8B5CF6")("║")}
|
|
22
|
+
${chalk.bold.hex("#8B5CF6")("║")} ${chalk.gray("Type your message or /help")} ${chalk.bold.hex("#8B5CF6")("║")}
|
|
23
|
+
${chalk.bold.hex("#8B5CF6")("╚═══════════════════════════════════════╝")}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export async function runInteractive(
|
|
27
|
+
zero: ZeroRuntime,
|
|
28
|
+
sessionId: string,
|
|
29
|
+
memory: FileMemoryStore,
|
|
30
|
+
contextManager: ContextManager,
|
|
31
|
+
options: any,
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
console.log(BANNER);
|
|
34
|
+
|
|
35
|
+
const rl = readline.createInterface({
|
|
36
|
+
input: process.stdin,
|
|
37
|
+
output: process.stdout,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const prompt = () => {
|
|
41
|
+
rl.question(chalk.hex("#8B5CF6")("❯ "), async (input) => {
|
|
42
|
+
input = input.trim();
|
|
43
|
+
|
|
44
|
+
if (!input) {
|
|
45
|
+
prompt();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Handle commands
|
|
50
|
+
if (input.startsWith("/")) {
|
|
51
|
+
await handleCommand(input, zero, rl);
|
|
52
|
+
prompt();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Build context from memory
|
|
57
|
+
const context = await contextManager.buildContext(input);
|
|
58
|
+
|
|
59
|
+
// Create agent engine
|
|
60
|
+
const engine = zero.agentRegistry.createEngine("coder");
|
|
61
|
+
|
|
62
|
+
// Add context if available
|
|
63
|
+
let fullPrompt = input;
|
|
64
|
+
if (context) {
|
|
65
|
+
fullPrompt = `${input}\n\n${context}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Run agent
|
|
69
|
+
console.log(chalk.gray("\n⚡ Processing...\n"));
|
|
70
|
+
|
|
71
|
+
const result = await engine.run(fullPrompt);
|
|
72
|
+
|
|
73
|
+
// Display results
|
|
74
|
+
for (const message of result.messages) {
|
|
75
|
+
if (message.role === "assistant") {
|
|
76
|
+
for (const content of message.content) {
|
|
77
|
+
if (content.type === "text") {
|
|
78
|
+
console.log(chalk.white(content.text));
|
|
79
|
+
} else if (content.type === "tool_call") {
|
|
80
|
+
console.log(chalk.yellow(`\n🔧 Tool: ${content.name}`));
|
|
81
|
+
console.log(chalk.gray(` Args: ${JSON.stringify(content.arguments)}`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} else if (message.role === "tool") {
|
|
85
|
+
for (const content of message.content) {
|
|
86
|
+
if (content.type === "tool_result") {
|
|
87
|
+
if (content.error) {
|
|
88
|
+
console.log(chalk.red(` ❌ ${content.error}`));
|
|
89
|
+
} else {
|
|
90
|
+
const output = typeof content.result === "string" ? content.result : JSON.stringify(content.result);
|
|
91
|
+
const preview = output.length > 500 ? output.slice(0, 500) + "..." : output;
|
|
92
|
+
console.log(chalk.green(` ✓ ${preview}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Show usage stats
|
|
100
|
+
console.log(chalk.gray(`\n📊 Tokens: ${result.tokenUsage.inputTokens} in / ${result.tokenUsage.outputTokens} out | Duration: ${(result.duration / 1000).toFixed(1)}s`));
|
|
101
|
+
|
|
102
|
+
// Save important facts to memory
|
|
103
|
+
await contextManager.saveFact(`User asked: ${input}`, ["conversation"]);
|
|
104
|
+
|
|
105
|
+
prompt();
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
prompt();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function handleCommand(input: string, zero: ZeroRuntime, rl: readline.Interface): Promise<void> {
|
|
113
|
+
const [command, ...args] = input.slice(1).split(" ");
|
|
114
|
+
|
|
115
|
+
switch (command) {
|
|
116
|
+
case "help":
|
|
117
|
+
console.log(`
|
|
118
|
+
${chalk.bold("Commands:")}
|
|
119
|
+
${chalk.cyan("/help")} Show this help message
|
|
120
|
+
${chalk.cyan("/clear")} Clear the conversation
|
|
121
|
+
${chalk.cyan("/model <name>")} Switch to a different model
|
|
122
|
+
${chalk.cyan("/provider <id>")} Switch to a different provider
|
|
123
|
+
${chalk.cyan("/agents")} List available agents
|
|
124
|
+
${chalk.cyan("/tools")} List available tools
|
|
125
|
+
${chalk.cyan("/memory")} Show stored memories
|
|
126
|
+
${chalk.cyan("/session")} Show session info
|
|
127
|
+
${chalk.cyan("/quit")} Exit ZERO
|
|
128
|
+
`);
|
|
129
|
+
break;
|
|
130
|
+
|
|
131
|
+
case "clear":
|
|
132
|
+
console.clear();
|
|
133
|
+
console.log(chalk.gray("Conversation cleared."));
|
|
134
|
+
break;
|
|
135
|
+
|
|
136
|
+
case "model":
|
|
137
|
+
if (args[0]) {
|
|
138
|
+
console.log(chalk.green(`✅ Model switched to: ${args[0]}`));
|
|
139
|
+
} else {
|
|
140
|
+
console.log(chalk.yellow("Usage: /model <model-name>"));
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case "provider":
|
|
145
|
+
if (args[0]) {
|
|
146
|
+
console.log(chalk.green(`✅ Provider switched to: ${args[0]}`));
|
|
147
|
+
} else {
|
|
148
|
+
console.log(chalk.yellow("Usage: /provider <provider-id>"));
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
case "agents":
|
|
153
|
+
const agents = zero.agentRegistry.list();
|
|
154
|
+
console.log(chalk.bold("\n🤖 Agents:"));
|
|
155
|
+
for (const agent of agents) {
|
|
156
|
+
console.log(` ${chalk.cyan(agent.id)}: ${agent.description}`);
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
case "tools":
|
|
161
|
+
const tools = zero.toolRegistry.getAll();
|
|
162
|
+
console.log(chalk.bold("\n🔧 Tools:"));
|
|
163
|
+
for (const tool of tools) {
|
|
164
|
+
console.log(` ${chalk.cyan(tool.name)} [${tool.category}]`);
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case "memory":
|
|
169
|
+
const memory = zero.memory || [];
|
|
170
|
+
console.log(chalk.bold("\n🧠 Memory:"));
|
|
171
|
+
if (memory.length === 0) {
|
|
172
|
+
console.log(chalk.gray(" No memories stored."));
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
case "session":
|
|
177
|
+
console.log(chalk.bold("\n📋 Session Info:"));
|
|
178
|
+
console.log(` Status: Active`);
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case "quit":
|
|
182
|
+
case "exit":
|
|
183
|
+
case "q":
|
|
184
|
+
console.log(chalk.gray("\n👋 Goodbye!"));
|
|
185
|
+
process.exit(0);
|
|
186
|
+
|
|
187
|
+
default:
|
|
188
|
+
console.log(chalk.red(`Unknown command: /${command}. Type /help for available commands.`));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZERO CLI - One-shot Mode
|
|
3
|
+
* Execute a single prompt and exit
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import type { AgentEngine } from "@zero/core";
|
|
8
|
+
|
|
9
|
+
interface ZeroRuntime {
|
|
10
|
+
agentRegistry: any;
|
|
11
|
+
toolRegistry: any;
|
|
12
|
+
providerRegistry: any;
|
|
13
|
+
sessionMgr: any;
|
|
14
|
+
mcpManager: any;
|
|
15
|
+
config: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function runOnce(
|
|
19
|
+
zero: ZeroRuntime,
|
|
20
|
+
prompt: string,
|
|
21
|
+
sessionId: string,
|
|
22
|
+
options: any,
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
const engine = zero.agentRegistry.createEngine("coder");
|
|
25
|
+
|
|
26
|
+
if (options.verbose) {
|
|
27
|
+
console.log(chalk.gray(`Provider: ${options.provider || "openai"}`));
|
|
28
|
+
console.log(chalk.gray(`Model: ${options.model || "gpt-4o"}`));
|
|
29
|
+
console.log(chalk.gray(`Directory: ${options.directory || process.cwd()}`));
|
|
30
|
+
console.log("");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const result = await engine.run(prompt);
|
|
34
|
+
|
|
35
|
+
// Output results
|
|
36
|
+
for (const message of result.messages) {
|
|
37
|
+
if (message.role === "assistant") {
|
|
38
|
+
for (const content of message.content) {
|
|
39
|
+
if (content.type === "text") {
|
|
40
|
+
console.log(content.text);
|
|
41
|
+
} else if (content.type === "tool_call" && options.verbose) {
|
|
42
|
+
console.error(chalk.yellow(`[Tool: ${content.name}]`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Exit with appropriate code
|
|
49
|
+
process.exit(result.success ? 0 : 1);
|
|
50
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZERO Agent Config Loader
|
|
3
|
+
* Adapted from: cline - Apache 2.0
|
|
4
|
+
*
|
|
5
|
+
* Loads agent configurations from YAML/Markdown frontmatter files.
|
|
6
|
+
* Supports file watching for dynamic agent reloading.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from "node:fs/promises";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import type { AgentConfig, ToolCategory } from "../types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Built-in tool names available for agents
|
|
15
|
+
*/
|
|
16
|
+
export const BUILTIN_TOOLS = [
|
|
17
|
+
"read_file",
|
|
18
|
+
"write_file",
|
|
19
|
+
"edit_file",
|
|
20
|
+
"list_directory",
|
|
21
|
+
"search_files",
|
|
22
|
+
"search_code",
|
|
23
|
+
"execute_shell",
|
|
24
|
+
"git_status",
|
|
25
|
+
"git_diff",
|
|
26
|
+
"git_commit",
|
|
27
|
+
"web_fetch",
|
|
28
|
+
"web_search",
|
|
29
|
+
"delegate_to_agent",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export type BuiltinTool = (typeof BUILTIN_TOOLS)[number];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Schema for agent configuration in YAML frontmatter
|
|
36
|
+
*/
|
|
37
|
+
export interface AgentFrontmatterConfig {
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
modelId?: string;
|
|
41
|
+
tools?: string | string[];
|
|
42
|
+
skills?: string | string[];
|
|
43
|
+
maxTurns?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse YAML frontmatter from a markdown file
|
|
48
|
+
*/
|
|
49
|
+
export function parseFrontmatter(content: string): { frontmatter: Record<string, unknown>; body: string } {
|
|
50
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
51
|
+
if (!match) {
|
|
52
|
+
return { frontmatter: {}, body: content };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const yamlStr = match[1];
|
|
56
|
+
const body = match[2];
|
|
57
|
+
|
|
58
|
+
// Simple YAML parser for key: value pairs
|
|
59
|
+
const frontmatter: Record<string, unknown> = {};
|
|
60
|
+
const lines = yamlStr.split("\n");
|
|
61
|
+
let currentKey = "";
|
|
62
|
+
let currentArray: string[] = [];
|
|
63
|
+
let inArray = false;
|
|
64
|
+
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
const trimmed = line.trim();
|
|
67
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
68
|
+
|
|
69
|
+
if (trimmed.startsWith("- ") && inArray) {
|
|
70
|
+
currentArray.push(trimmed.slice(2).trim());
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (inArray && currentKey) {
|
|
75
|
+
frontmatter[currentKey] = currentArray;
|
|
76
|
+
inArray = false;
|
|
77
|
+
currentArray = [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const colonIdx = trimmed.indexOf(":");
|
|
81
|
+
if (colonIdx > 0) {
|
|
82
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
83
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
84
|
+
|
|
85
|
+
if (value === "" || value === "[]") {
|
|
86
|
+
currentKey = key;
|
|
87
|
+
inArray = true;
|
|
88
|
+
currentArray = [];
|
|
89
|
+
} else {
|
|
90
|
+
frontmatter[key] = value.replace(/^["']|["']$/g, "");
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (inArray && currentKey) {
|
|
96
|
+
frontmatter[currentKey] = currentArray;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { frontmatter, body };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse tools from config (string or array)
|
|
104
|
+
*/
|
|
105
|
+
function parseTools(tools: string | string[] | undefined): string[] {
|
|
106
|
+
if (!tools) return [...BUILTIN_TOOLS].filter((t) => t !== "delegate_to_agent");
|
|
107
|
+
|
|
108
|
+
const rawTools = Array.isArray(tools) ? tools : tools.split(",").map((t) => t.trim());
|
|
109
|
+
return Array.from(new Set(rawTools.filter((t) => BUILTIN_TOOLS.includes(t as BuiltinTool))));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Parse skills from config
|
|
114
|
+
*/
|
|
115
|
+
function parseSkills(skills: string | string[] | undefined): string[] {
|
|
116
|
+
if (!skills) return [];
|
|
117
|
+
return Array.isArray(skills) ? skills : skills.split(",").map((s) => s.trim());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Load an agent configuration from a markdown file
|
|
122
|
+
*/
|
|
123
|
+
export async function loadAgentFromFile(filePath: string): Promise<AgentConfig> {
|
|
124
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
125
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
126
|
+
|
|
127
|
+
const config = frontmatter as unknown as AgentFrontmatterConfig;
|
|
128
|
+
|
|
129
|
+
if (!config.name) {
|
|
130
|
+
throw new Error(`Agent config in ${filePath} missing required field: name`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!config.description) {
|
|
134
|
+
throw new Error(`Agent config in ${filePath} missing required field: description`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const id = path.basename(filePath, path.extname(filePath)).toLowerCase().replace(/[^a-z0-9]/g, "-");
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
id,
|
|
141
|
+
name: config.name,
|
|
142
|
+
description: config.description,
|
|
143
|
+
systemPrompt: body.trim() || `You are ${config.name}, a specialized agent.`,
|
|
144
|
+
tools: parseTools(config.tools),
|
|
145
|
+
maxTurns: config.maxTurns || 50,
|
|
146
|
+
model: config.modelId || "default",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Load all agent configurations from a directory
|
|
152
|
+
*/
|
|
153
|
+
export async function loadAgentsFromDirectory(dirPath: string): Promise<AgentConfig[]> {
|
|
154
|
+
const agents: AgentConfig[] = [];
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
158
|
+
|
|
159
|
+
for (const entry of entries) {
|
|
160
|
+
if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".yaml"))) {
|
|
161
|
+
try {
|
|
162
|
+
const agent = await loadAgentFromFile(path.join(dirPath, entry.name));
|
|
163
|
+
agents.push(agent);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// Skip invalid files
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
// Directory doesn't exist
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return agents;
|
|
174
|
+
}
|