@c4ccz/zero 1.0.0 → 1.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 +10 -7
- 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/extended.ts +8 -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/tools/linter.ts +26 -0
- package/packages/core/src/tools/repo-map.ts +11 -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/bun.lock +0 -35
- package/bunfig.toml +0 -6
- package/packages/cli/package.json +0 -23
- package/packages/core/package.json +0 -41
- package/packages/sdk/package.json +0 -16
- package/packages/tui/package.json +0 -19
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZERO Permission System
|
|
3
|
+
* Handles user approval flow for sensitive operations
|
|
4
|
+
*
|
|
5
|
+
* Sources:
|
|
6
|
+
* - crush: permission gates with auto-allow patterns
|
|
7
|
+
* - opencode: permission evaluation engine
|
|
8
|
+
* - gemini-cli: policy engine with TOML config
|
|
9
|
+
* - cline: approval flow with always-allow patterns
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
PermissionHandler,
|
|
14
|
+
PermissionRequest,
|
|
15
|
+
PermissionDecision,
|
|
16
|
+
PermissionSet,
|
|
17
|
+
PermissionLevel,
|
|
18
|
+
} from "../types.js";
|
|
19
|
+
|
|
20
|
+
export interface PermissionRule {
|
|
21
|
+
pattern: string;
|
|
22
|
+
level: PermissionLevel;
|
|
23
|
+
reason?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PermissionConfig {
|
|
27
|
+
defaults: PermissionSet;
|
|
28
|
+
rules: PermissionRule[];
|
|
29
|
+
alwaysAllow: string[];
|
|
30
|
+
alwaysDeny: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const DEFAULT_PERMISSION_CONFIG: PermissionConfig = {
|
|
34
|
+
defaults: {
|
|
35
|
+
fileRead: "allow",
|
|
36
|
+
fileWrite: "ask",
|
|
37
|
+
shellExecution: "ask",
|
|
38
|
+
networkAccess: "ask",
|
|
39
|
+
mcpAccess: "allow",
|
|
40
|
+
},
|
|
41
|
+
rules: [],
|
|
42
|
+
alwaysAllow: [
|
|
43
|
+
"read_file",
|
|
44
|
+
"list_directory",
|
|
45
|
+
"search_files",
|
|
46
|
+
"search_code",
|
|
47
|
+
"git_status",
|
|
48
|
+
"git_diff",
|
|
49
|
+
],
|
|
50
|
+
alwaysDeny: [],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a permission handler with configurable rules
|
|
55
|
+
*/
|
|
56
|
+
export function createPermissionHandler(
|
|
57
|
+
config: PermissionConfig = DEFAULT_PERMISSION_CONFIG,
|
|
58
|
+
interactiveHandler?: (request: PermissionRequest) => Promise<PermissionDecision>,
|
|
59
|
+
): PermissionHandler {
|
|
60
|
+
const approvedPatterns = new Set(config.alwaysAllow);
|
|
61
|
+
const deniedPatterns = new Set(config.alwaysDeny);
|
|
62
|
+
|
|
63
|
+
return async (request: PermissionRequest): Promise<PermissionDecision> => {
|
|
64
|
+
// Check always-deny list
|
|
65
|
+
if (deniedPatterns.has(request.resource)) {
|
|
66
|
+
return "deny";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check always-allow list
|
|
70
|
+
if (approvedPatterns.has(request.resource)) {
|
|
71
|
+
return "allow";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check custom rules
|
|
75
|
+
for (const rule of config.rules) {
|
|
76
|
+
if (matchPattern(request.resource, rule.pattern)) {
|
|
77
|
+
if (rule.level === "allow") return "allow";
|
|
78
|
+
if (rule.level === "deny") return "deny";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check defaults based on type
|
|
83
|
+
const defaultLevel = getDefaultLevel(config.defaults, request.type);
|
|
84
|
+
if (defaultLevel === "allow") return "allow";
|
|
85
|
+
if (defaultLevel === "deny") return "deny";
|
|
86
|
+
|
|
87
|
+
// Ask user if interactive handler is available
|
|
88
|
+
if (interactiveHandler) {
|
|
89
|
+
const decision = await interactiveHandler(request);
|
|
90
|
+
if (decision === "allow_always") {
|
|
91
|
+
approvedPatterns.add(request.resource);
|
|
92
|
+
return "allow";
|
|
93
|
+
}
|
|
94
|
+
return decision;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Default to deny if no interactive handler
|
|
98
|
+
return "deny";
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getDefaultLevel(defaults: PermissionSet, type: PermissionRequest["type"]): PermissionLevel {
|
|
103
|
+
switch (type) {
|
|
104
|
+
case "file_read": return defaults.fileRead;
|
|
105
|
+
case "file_write": return defaults.fileWrite;
|
|
106
|
+
case "shell": return defaults.shellExecution;
|
|
107
|
+
case "network": return defaults.networkAccess;
|
|
108
|
+
case "mcp": return defaults.mcpAccess;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function matchPattern(resource: string, pattern: string): boolean {
|
|
113
|
+
if (pattern === resource) return true;
|
|
114
|
+
if (pattern.includes("*")) {
|
|
115
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
116
|
+
return regex.test(resource);
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Non-interactive permission handler that auto-approves based on config
|
|
123
|
+
*/
|
|
124
|
+
export function createAutoPermissionHandler(config?: PermissionConfig): PermissionHandler {
|
|
125
|
+
return createPermissionHandler(config || DEFAULT_PERMISSION_CONFIG);
|
|
126
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZERO Prompt Template Engine
|
|
3
|
+
* Adapted from: crush (Charm/Go) - MIT
|
|
4
|
+
*
|
|
5
|
+
* Template-based prompt generation with dynamic context injection.
|
|
6
|
+
* Supports Go-style templates with variables and conditionals.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface PromptContext {
|
|
10
|
+
provider?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
workingDir?: string;
|
|
13
|
+
platform?: string;
|
|
14
|
+
date?: string;
|
|
15
|
+
isGitRepo?: boolean;
|
|
16
|
+
gitStatus?: string;
|
|
17
|
+
gitBranch?: string;
|
|
18
|
+
contextFiles?: Array<{ path: string; content: string }>;
|
|
19
|
+
skills?: string[];
|
|
20
|
+
userName?: string;
|
|
21
|
+
customVars?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Simple template engine (Go template style)
|
|
26
|
+
* Supports: {{.Variable}}, {{if .Var}}...{{end}}, {{range .Arr}}...{{end}}
|
|
27
|
+
*/
|
|
28
|
+
export class PromptEngine {
|
|
29
|
+
private templates = new Map<string, string>();
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register a named template
|
|
33
|
+
*/
|
|
34
|
+
register(name: string, template: string): void {
|
|
35
|
+
this.templates.set(name, template);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Render a template with context
|
|
40
|
+
*/
|
|
41
|
+
render(templateOrName: string, context: PromptContext): string {
|
|
42
|
+
const template = this.templates.get(templateOrName) || templateOrName;
|
|
43
|
+
return this.processTemplate(template, context);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private processTemplate(template: string, ctx: PromptContext): string {
|
|
47
|
+
let result = template;
|
|
48
|
+
|
|
49
|
+
// Helper: convert PascalCase to camelCase for context lookup
|
|
50
|
+
const lookupValue = (varName: string): any => {
|
|
51
|
+
// Try exact match first
|
|
52
|
+
if ((ctx as any)[varName] !== undefined) return (ctx as any)[varName];
|
|
53
|
+
// Try camelCase version (PascalCase → camelCase)
|
|
54
|
+
const camelCase = varName.charAt(0).toLowerCase() + varName.slice(1);
|
|
55
|
+
if ((ctx as any)[camelCase] !== undefined) return (ctx as any)[camelCase];
|
|
56
|
+
// Try custom vars
|
|
57
|
+
if (ctx.customVars?.[varName] !== undefined) return ctx.customVars[varName];
|
|
58
|
+
if (ctx.customVars?.[camelCase] !== undefined) return ctx.customVars[camelCase];
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Process conditionals: {{if .Var}}...{{end}}
|
|
63
|
+
result = result.replace(/\{\{if\s+\.(\w+)\}\}([\s\S]*?)\{\{end\}\}/g, (_, varName, content) => {
|
|
64
|
+
const value = lookupValue(varName);
|
|
65
|
+
if (value) {
|
|
66
|
+
return this.processTemplate(content, ctx);
|
|
67
|
+
}
|
|
68
|
+
return "";
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Process variables: {{.Variable}}
|
|
72
|
+
result = result.replace(/\{\{\.(\w+)\}\}/g, (_, varName) => {
|
|
73
|
+
const value = lookupValue(varName);
|
|
74
|
+
if (value !== undefined && value !== null) return String(value);
|
|
75
|
+
return "";
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Process date
|
|
79
|
+
result = result.replace(/\{\{\.Date\}\}/g, ctx.date || new Date().toISOString().split("T")[0]);
|
|
80
|
+
|
|
81
|
+
// Process platform
|
|
82
|
+
result = result.replace(/\{\{\.Platform\}\}/g, ctx.platform || process.platform);
|
|
83
|
+
|
|
84
|
+
// Process context files
|
|
85
|
+
if (ctx.contextFiles && ctx.contextFiles.length > 0) {
|
|
86
|
+
result = result.replace(/\{\{range\s+\.ContextFiles\}\}([\s\S]*?)\{\{end\}\}/g, (_, inner) => {
|
|
87
|
+
return ctx.contextFiles!.map((file) => {
|
|
88
|
+
let rendered = inner;
|
|
89
|
+
rendered = rendered.replace(/\{\{\.Path\}\}/g, file.path);
|
|
90
|
+
rendered = rendered.replace(/\{\{\.Content\}\}/g, file.content);
|
|
91
|
+
return rendered;
|
|
92
|
+
}).join("\n");
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Build a full system prompt with all context
|
|
101
|
+
*/
|
|
102
|
+
buildSystemPrompt(basePrompt: string, context: PromptContext): string {
|
|
103
|
+
const sections: string[] = [basePrompt];
|
|
104
|
+
|
|
105
|
+
if (context.workingDir) {
|
|
106
|
+
sections.push(`\n## Environment\n- Working Directory: ${context.workingDir}`);
|
|
107
|
+
sections.push(`- Platform: ${context.platform || process.platform}`);
|
|
108
|
+
sections.push(`- Date: ${context.date || new Date().toISOString().split("T")[0]}`);
|
|
109
|
+
if (context.model) sections.push(`- Model: ${context.model}`);
|
|
110
|
+
if (context.provider) sections.push(`- Provider: ${context.provider}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (context.isGitRepo && context.gitBranch) {
|
|
114
|
+
sections.push(`\n## Git\n- Branch: ${context.gitBranch}`);
|
|
115
|
+
if (context.gitStatus) sections.push(`- Status: ${context.gitStatus}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (context.contextFiles && context.contextFiles.length > 0) {
|
|
119
|
+
sections.push("\n## Context Files");
|
|
120
|
+
for (const file of context.contextFiles) {
|
|
121
|
+
sections.push(`\n### ${file.path}\n\`\`\`\n${file.content}\n\`\`\``);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return sections.join("\n");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Default ZERO system prompt
|
|
131
|
+
*/
|
|
132
|
+
export const ZERO_SYSTEM_PROMPT = `You are ZERO, an expert AI coding assistant with access to powerful tools.
|
|
133
|
+
|
|
134
|
+
## Core Capabilities
|
|
135
|
+
- Read, write, and edit files with precision
|
|
136
|
+
- Execute shell commands safely
|
|
137
|
+
- Search codebases efficiently
|
|
138
|
+
- Generate diffs and apply patches
|
|
139
|
+
- Manage git repositories
|
|
140
|
+
- Lint code for quality issues
|
|
141
|
+
- Generate repository maps
|
|
142
|
+
|
|
143
|
+
## Guidelines
|
|
144
|
+
1. Always read relevant files before making changes
|
|
145
|
+
2. Use search/replace for precise edits (not full file rewrites)
|
|
146
|
+
3. Explain your reasoning when making architectural decisions
|
|
147
|
+
4. Follow existing code style and patterns
|
|
148
|
+
5. Test changes when possible
|
|
149
|
+
6. Ask for clarification when requirements are ambiguous
|
|
150
|
+
|
|
151
|
+
## Tool Usage
|
|
152
|
+
- Use read_file before editing to understand context
|
|
153
|
+
- Use search_code to find relevant patterns
|
|
154
|
+
- Use edit_file for targeted changes
|
|
155
|
+
- Use lint to verify code quality
|
|
156
|
+
- Use repo_map to understand project structure
|
|
157
|
+
- Use git_diff to review changes before committing`;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create a pre-configured prompt engine
|
|
161
|
+
*/
|
|
162
|
+
export function createPromptEngine(): PromptEngine {
|
|
163
|
+
const engine = new PromptEngine();
|
|
164
|
+
|
|
165
|
+
engine.register("zero-system", ZERO_SYSTEM_PROMPT);
|
|
166
|
+
|
|
167
|
+
engine.register("code-review", `You are ZERO's code review specialist. Analyze the following code for:
|
|
168
|
+
- Bugs and potential issues
|
|
169
|
+
- Security vulnerabilities
|
|
170
|
+
- Performance problems
|
|
171
|
+
- Code style and best practices
|
|
172
|
+
- Test coverage gaps
|
|
173
|
+
|
|
174
|
+
{{if .WorkingDir}}Working directory: {{.WorkingDir}}{{end}}
|
|
175
|
+
|
|
176
|
+
Provide clear, actionable feedback with specific line references.`);
|
|
177
|
+
|
|
178
|
+
engine.register("architect", `You are ZERO's software architect. Help design and plan:
|
|
179
|
+
- System architecture and component design
|
|
180
|
+
- API design and data models
|
|
181
|
+
- Technology selection and trade-offs
|
|
182
|
+
- Migration strategies
|
|
183
|
+
|
|
184
|
+
{{if .WorkingDir}}Project: {{.WorkingDir}}{{end}}
|
|
185
|
+
{{if .GitBranch}}Branch: {{.GitBranch}}{{end}}`);
|
|
186
|
+
|
|
187
|
+
return engine;
|
|
188
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LLMProvider } from "../types.js";
|
|
2
|
+
import { OpenAICompatibleClient } from "./registry.js";
|
|
3
|
+
export function createDeepSeekProvider(): LLMProvider { return { id:"deepseek",name:"DeepSeek",type:"openai",models:[{id:"deepseek-v4-pro",name:"DeepSeek V4 Pro",contextWindow:1000000,maxOutputTokens:8192,supportsImages:false,supportsToolUse:true,supportsStreaming:true},{id:"deepseek-v4-flash",name:"DeepSeek V4 Flash",contextWindow:1000000,maxOutputTokens:8192,supportsImages:false,supportsToolUse:true,supportsStreaming:true}],createClient:(c)=>new OpenAICompatibleClient(c,"https://api.deepseek.com/v1")}; }
|
|
4
|
+
export function createGrokProvider(): LLMProvider { return { id:"grok",name:"Grok (xAI)",type:"openai",models:[{id:"grok-4.5",name:"Grok 4.5",contextWindow:500000,maxOutputTokens:8192,supportsImages:true,supportsToolUse:true,supportsStreaming:true}],createClient:(c)=>new OpenAICompatibleClient(c,"https://api.x.ai/v1")}; }
|
|
5
|
+
export function createOpenRouterProvider(): LLMProvider { return { id:"openrouter",name:"OpenRouter",type:"openai",models:[{id:"openai/gpt-4o",name:"GPT-4o",contextWindow:128000,maxOutputTokens:4096,supportsImages:true,supportsToolUse:true,supportsStreaming:true}],createClient:(c)=>new OpenAICompatibleClient({...c,headers:{"HTTP-Referer":"https://zero.dev","X-Title":"ZERO",...c.headers}},"https://openrouter.ai/api/v1")}; }
|
|
6
|
+
export function createTogetherProvider(): LLMProvider { return { id:"together",name:"Together AI",type:"openai",models:[{id:"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",name:"Llama 3.1 405B",contextWindow:131072,maxOutputTokens:4096,supportsImages:false,supportsToolUse:false,supportsStreaming:true}],createClient:(c)=>new OpenAICompatibleClient(c,"https://api.together.xyz/v1")}; }
|
|
7
|
+
export function createFireworksProvider(): LLMProvider { return { id:"fireworks",name:"Fireworks AI",type:"openai",models:[{id:"accounts/fireworks/models/llama-v3p1-405b-instruct",name:"Llama 3.1 405B",contextWindow:131072,maxOutputTokens:4096,supportsImages:false,supportsToolUse:false,supportsStreaming:true}],createClient:(c)=>new OpenAICompatibleClient(c,"https://api.fireworks.ai/inference/v1")}; }
|
|
8
|
+
export function getExtendedProviders(): LLMProvider[] { return [createDeepSeekProvider(),createGrokProvider(),createOpenRouterProvider(),createTogetherProvider(),createFireworksProvider()]; }
|