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.
Files changed (44) hide show
  1. package/MERGE_REPORT.md +265 -0
  2. package/README.md +127 -0
  3. package/package.json +55 -0
  4. package/packages/cli/src/index.ts +271 -0
  5. package/packages/cli/src/interactive.ts +190 -0
  6. package/packages/cli/src/once.ts +50 -0
  7. package/packages/core/src/agent/config-loader.ts +174 -0
  8. package/packages/core/src/agent/engine.ts +266 -0
  9. package/packages/core/src/agent/registry-tools.ts +58 -0
  10. package/packages/core/src/agent/registry.ts +213 -0
  11. package/packages/core/src/commands/index.ts +116 -0
  12. package/packages/core/src/config/index.ts +139 -0
  13. package/packages/core/src/confirmation/message-bus.ts +206 -0
  14. package/packages/core/src/diff/index.ts +232 -0
  15. package/packages/core/src/diff-detect/index.ts +207 -0
  16. package/packages/core/src/edit-coder/index.ts +194 -0
  17. package/packages/core/src/events/index.ts +151 -0
  18. package/packages/core/src/file-tracker/index.ts +183 -0
  19. package/packages/core/src/git/index.ts +305 -0
  20. package/packages/core/src/history/index.ts +236 -0
  21. package/packages/core/src/image/index.ts +131 -0
  22. package/packages/core/src/index.ts +131 -0
  23. package/packages/core/src/lsp/index.ts +251 -0
  24. package/packages/core/src/mcp/index.ts +186 -0
  25. package/packages/core/src/memory/index.ts +141 -0
  26. package/packages/core/src/memory/prompts.ts +52 -0
  27. package/packages/core/src/orchestrator/protocol.ts +140 -0
  28. package/packages/core/src/orchestrator/server.ts +319 -0
  29. package/packages/core/src/output/index.ts +164 -0
  30. package/packages/core/src/permissions/index.ts +126 -0
  31. package/packages/core/src/prompts/engine.ts +188 -0
  32. package/packages/core/src/providers/registry.ts +687 -0
  33. package/packages/core/src/routing/model-router.ts +160 -0
  34. package/packages/core/src/server/index.ts +232 -0
  35. package/packages/core/src/session/index.ts +174 -0
  36. package/packages/core/src/session/service.ts +322 -0
  37. package/packages/core/src/skills/index.ts +205 -0
  38. package/packages/core/src/streaming/index.ts +166 -0
  39. package/packages/core/src/sub-agent/index.ts +179 -0
  40. package/packages/core/src/tools/builtin.ts +568 -0
  41. package/packages/core/src/types.ts +356 -0
  42. package/packages/sdk/src/index.ts +247 -0
  43. package/packages/tui/src/index.ts +141 -0
  44. package/tsconfig.json +26 -0
@@ -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
+ }