agentix-cli 0.2.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/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # AgentX
2
+
3
+ **Self-hosted multi-agent orchestrator.** Routes messages from Telegram, WhatsApp, crons, and cross-machine A2A mesh to AI agents running on Claude Code, OpenAI, Ollama, or any LLM provider.
4
+
5
+ > **Experimental.** This project is a rapid response to [Anthropic's updated terms of use](https://www.anthropic.com/policies) restricting third-party OAuth integrations (affecting tools like OpenClaw). AgentX is a self-hosted, bring-your-own-key alternative — you run it on your own machines with your own API keys or Claude subscription.
6
+
7
+ ## How it works
8
+
9
+ AgentX is **not** an AI runtime. It's a thin orchestration layer:
10
+
11
+ ```
12
+ ┌──────────────┐
13
+ Telegram ──────────┤ │
14
+ WhatsApp ──────────┤ agentx │──── claude -p "task" --cwd /workspace
15
+ Cron trigger ──────┤ daemon │──── openai API call
16
+ A2A remote call ───┤ │──── ollama generate
17
+ └──────┬───────┘
18
+
19
+ routes messages to
20
+ the right workspace
21
+ with the right prompt
22
+ ```
23
+
24
+ Each agent = a workspace directory. For Claude Code agents, permissions, hooks, MCP servers, skills, and memory are all configured in the workspace's `.claude/` directory — no AgentX code needed.
25
+
26
+ ## Features
27
+
28
+ - **Multi-channel**: Telegram (polling, multi-account), WhatsApp (planned)
29
+ - **Multi-agent**: Named agents with custom permissions, concurrent limits, mention routing
30
+ - **Cron scheduler**: Timezone-aware recurring tasks with run logging
31
+ - **A2A mesh**: Cross-machine agent communication via HTTP, peer discovery, health checks
32
+ - **Streaming**: Real-time response streaming to Telegram with progressive message edits
33
+ - **Typing indicators**: Bots show typing status while processing
34
+ - **Bot-to-bot**: Agents can mention each other to delegate tasks
35
+ - **Session memory**: One conversation session per agent/chat/day for context continuity
36
+ - **Hooks**: Pre/post hooks for channels, crons, and A2A tasks (command, script, or LLM-based)
37
+ - **Provider abstraction**: Switch providers per-agent via config, with capability warnings
38
+ - **Markdown rendering**: Claude's markdown output converted to Telegram MarkdownV2
39
+
40
+ ## Three execution tiers
41
+
42
+ | Tier | How | Auth | Best for |
43
+ |------|-----|------|----------|
44
+ | `claude-code` | Spawns `claude` CLI | Subscription | Full power: subagents, MCP, skills, hooks, 1M context |
45
+ | `sdk` | Claude Agent SDK | API key | Programmatic control, headless servers |
46
+ | `orchestrator` | AgentX's own loop | Any provider key | Non-Claude providers (OpenAI, Ollama, Gemini) |
47
+
48
+ ## Quick start
49
+
50
+ ```bash
51
+ npm install -g agentx-cli
52
+
53
+ # Copy and edit the example config
54
+ cp node_modules/agentx-cli/agentx.example.json agentx.json
55
+
56
+ # Start the daemon
57
+ agentx daemon
58
+ ```
59
+
60
+ Or clone and run from source:
61
+
62
+ ```bash
63
+ git clone https://github.com/nooqta/agentx.git
64
+ cd agentx
65
+ npm install && npm run build
66
+ cp agentx.example.json agentx.json
67
+ # Edit agentx.json with your agents, channels, etc.
68
+ node dist/cli.js daemon
69
+ ```
70
+
71
+ ## Configuration
72
+
73
+ AgentX uses a single `agentx.json` file. Environment variables are expanded (`${VAR_NAME}`).
74
+
75
+ ```jsonc
76
+ {
77
+ "node": {
78
+ "id": "my-machine",
79
+ "name": "My Machine",
80
+ "bind": "127.0.0.1:18800"
81
+ },
82
+
83
+ "providers": {
84
+ "claude": { "apiKey": "${ANTHROPIC_API_KEY}" },
85
+ "openai": { "apiKey": "${OPENAI_API_KEY}" }
86
+ },
87
+
88
+ "agents": {
89
+ "assistant": {
90
+ "name": "Assistant",
91
+ "workspace": "/path/to/workspace",
92
+ "tier": "claude-code",
93
+ "model": "claude-sonnet-4-6",
94
+ "mentions": ["@my_bot"],
95
+ "maxConcurrent": 2,
96
+ "systemPrompt": "You are a helpful assistant.",
97
+ "permissionMode": "default"
98
+ }
99
+ },
100
+
101
+ "channels": {
102
+ "telegram": {
103
+ "enabled": true,
104
+ "accounts": {
105
+ "default": {
106
+ "token": "${TG_BOT_TOKEN}",
107
+ "agentBinding": "assistant"
108
+ }
109
+ },
110
+ "policy": { "dm": "pair", "group": "mention-required" }
111
+ }
112
+ },
113
+
114
+ "crons": {
115
+ "daily-report": {
116
+ "enabled": true,
117
+ "schedule": "0 9 * * *",
118
+ "timezone": "UTC",
119
+ "agent": "assistant",
120
+ "prompt": "Generate today's status report.",
121
+ "timeout": 600
122
+ }
123
+ },
124
+
125
+ "mesh": {
126
+ "enabled": true,
127
+ "peers": [
128
+ { "url": "http://100.67.108.119:18800", "name": "server-2" }
129
+ ]
130
+ }
131
+ }
132
+ ```
133
+
134
+ ### Agent workspace setup (Claude Code tier)
135
+
136
+ Each agent's workspace is a directory with Claude Code configuration:
137
+
138
+ ```
139
+ my-workspace/
140
+ ├── .claude/
141
+ │ ├── settings.json # Permissions, hooks, model
142
+ │ ├── .mcp.json # MCP servers
143
+ │ ├── agents/ # Subagents
144
+ │ └── skills/ # Domain skills
145
+ ├── CLAUDE.md # Agent identity & instructions
146
+ └── ... (project files)
147
+ ```
148
+
149
+ ## HTTP API
150
+
151
+ The daemon exposes a REST API:
152
+
153
+ | Endpoint | Method | Description |
154
+ |----------|--------|-------------|
155
+ | `/health` | GET | System status, agents, crons, mesh |
156
+ | `/agents` | GET | List agents and their status |
157
+ | `/crons` | GET | List cron jobs |
158
+ | `/mesh` | GET | Mesh peer directory |
159
+ | `/task` | POST | Execute a task: `{ "agent": "id", "message": "..." }` |
160
+ | `/mesh/task` | POST | Send task to remote peer: `{ "peer": "name", "message": "..." }` |
161
+ | `/.well-known/agent-card.json` | GET | A2A agent discovery |
162
+
163
+ ## A2A Mesh
164
+
165
+ Run `agentx daemon` on multiple machines. Each node discovers peers and exposes agents via A2A agent cards. Tasks are routed to the correct node automatically.
166
+
167
+ ```
168
+ MacBook (Nadia, DevOps) ←─── Tailscale ───→ Server (Atlas, MTGL)
169
+ :18800 :18800
170
+ ```
171
+
172
+ Peers communicate over HTTP. Use Tailscale or a VPN for secure cross-machine communication.
173
+
174
+ ## Migrating from OpenClaw
175
+
176
+ AgentX is designed as a drop-in replacement for OpenClaw's agent orchestration:
177
+
178
+ | OpenClaw | AgentX |
179
+ |----------|--------|
180
+ | `openclaw.json` agents | `agentx.json` agents section |
181
+ | `openclaw.json` channels.telegram | `agentx.json` channels.telegram |
182
+ | `cron/jobs.json` | `agentx.json` crons section |
183
+ | `exec-approvals.json` | Workspace `.claude/settings.json` permissions |
184
+ | Gateway + Node architecture | Single daemon per machine |
185
+ | OAuth proxy | Direct API key or CLI subscription |
186
+
187
+ ### Migration steps
188
+
189
+ 1. **Stop OpenClaw**
190
+ - macOS: `launchctl unload ~/Library/LaunchAgents/ai.openclaw.*.plist`
191
+ - Linux: `systemctl --user stop openclaw-gateway openclaw-node`
192
+ 2. **Install AgentX**: `npm install -g agentx-cli`
193
+ 3. **Convert config**: Map your `openclaw.json` agents, channels, and cron jobs to `agentx.json` (see `agentx.example.json`)
194
+ 4. **Set up workspaces**: Ensure each agent's workspace has a `.claude/` directory with permissions and hooks
195
+ 5. **Move secrets to `.env`**: Bot tokens, API keys — AgentX auto-loads `.env` from the working directory
196
+ 6. **Start daemon**: `agentx daemon`
197
+
198
+ ### Key differences from OpenClaw
199
+
200
+ - **No OAuth proxy**: You run your own daemon with your own credentials
201
+ - **Workspace = Agent**: Permissions, hooks, and tools live in the workspace `.claude/` dir
202
+ - **Claude Code native**: `tier: "claude-code"` gives you the full Claude Code feature set (subagents, MCP, skills, hooks, memory, worktrees, 1M context)
203
+ - **Provider-agnostic**: Switch any agent to OpenAI, Ollama, or other providers via config
204
+ - **A2A mesh**: Agents across machines communicate natively (OpenClaw required a gateway)
205
+
206
+ ## Built with
207
+
208
+ - [Claude Code CLI](https://claude.ai/code) — AI agent runtime (subscription or API)
209
+ - [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) — Programmatic agent control
210
+ - [Telegram Bot API](https://core.telegram.org/bots/api) — Channel adapter (zero dependencies)
211
+ - [Zod](https://github.com/colinhacks/zod) — Config validation
212
+ - [Commander](https://github.com/tj/commander.js) — CLI framework
213
+
214
+ ## Legal
215
+
216
+ AgentX is a **self-hosted, bring-your-own-key** tool:
217
+
218
+ - Each user provides their own API key or Claude subscription
219
+ - No credentials are stored, shared, or proxied by AgentX
220
+ - Built on official public packages: Claude API, Claude Agent SDK, Claude Code CLI
221
+ - Provider-agnostic — works with any LLM, not locked to Anthropic
222
+ - Same model as LangChain, CrewAI, AutoGen, Dify, n8n
223
+
224
+ ## License
225
+
226
+ MIT
@@ -0,0 +1,85 @@
1
+ {
2
+ "node": {
3
+ "id": "my-machine",
4
+ "name": "My Machine",
5
+ "bind": "127.0.0.1:18800"
6
+ },
7
+
8
+ "providers": {
9
+ "claude": {
10
+ "apiKey": "${ANTHROPIC_API_KEY}",
11
+ "defaultModel": "claude-opus-4-6"
12
+ },
13
+ "openai": {
14
+ "apiKey": "${OPENAI_API_KEY}",
15
+ "defaultModel": "gpt-4o"
16
+ },
17
+ "ollama": {
18
+ "baseUrl": "http://localhost:11434"
19
+ }
20
+ },
21
+
22
+ "agents": {
23
+ "assistant": {
24
+ "name": "Assistant",
25
+ "workspace": "/path/to/assistant-workspace",
26
+ "tier": "claude-code",
27
+ "model": "claude-opus-4-6",
28
+ "mentions": ["@assistant", "assistant"],
29
+ "maxConcurrent": 2,
30
+ "systemPrompt": "You are a helpful assistant."
31
+ },
32
+ "devops": {
33
+ "name": "DevOps",
34
+ "workspace": "/path/to/devops-workspace",
35
+ "tier": "claude-code",
36
+ "model": "claude-sonnet-4-6",
37
+ "mentions": ["@devops"],
38
+ "maxConcurrent": 1,
39
+ "permissionMode": "bypassPermissions"
40
+ }
41
+ },
42
+
43
+ "channels": {
44
+ "telegram": {
45
+ "enabled": false,
46
+ "accounts": {
47
+ "default": {
48
+ "token": "${TG_BOT_TOKEN}",
49
+ "agentBinding": "assistant"
50
+ }
51
+ },
52
+ "policy": {
53
+ "dm": "pair",
54
+ "group": "mention-required"
55
+ }
56
+ },
57
+ "whatsapp": {
58
+ "enabled": false,
59
+ "sessionDir": ".agentx/whatsapp-sessions",
60
+ "agentBinding": "assistant"
61
+ }
62
+ },
63
+
64
+ "crons": {
65
+ "daily-report": {
66
+ "enabled": false,
67
+ "schedule": "0 9 * * *",
68
+ "timezone": "UTC",
69
+ "agent": "assistant",
70
+ "prompt": "Generate today's status report.",
71
+ "timeout": 600,
72
+ "onError": "log"
73
+ }
74
+ },
75
+
76
+ "mesh": {
77
+ "enabled": false,
78
+ "peers": [],
79
+ "discovery": "static",
80
+ "healthCheck": {
81
+ "interval": 60,
82
+ "timeout": 10
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,2 @@
1
+ import{D as a,E as b,F as c}from"./chunk-NZ6W33BD.js";import"./chunk-FRFR27IN.js";import"./chunk-SFQUP3BP.js";import"./chunk-FUYKPFUV.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-AI6DUEPU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,32 @@
1
+ import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{m as g}from"./chunk-FUYKPFUV.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
2
+ ${t}
3
+ ${n}
4
+ ${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
5
+ \`\`\`
6
+ ${p.slice(0,3e3)}
7
+ \`\`\``)}}let a=await w(this.cwd),l=[{role:"system",content:`You are agentx auto-heal. You fix errors in generated code.
8
+
9
+ Project tech stack: ${y(a)}
10
+
11
+ RULES:
12
+ - Fix ONLY the error \u2014 do not refactor or add features
13
+ - Output the COMPLETE fixed file content via create_files
14
+ - Be minimal \u2014 smallest change that fixes the error
15
+ - This is attempt ${t} \u2014 ${t>1?"previous fix attempts failed, try a different approach":"analyze carefully"}`},{role:"user",content:`The following code was generated for task: "${i}"
16
+
17
+ ## Error
18
+ \`\`\`
19
+ ${e}
20
+ \`\`\`
21
+
22
+ ## Affected Files
23
+ ${s.join(`
24
+
25
+ `)}
26
+
27
+ Fix the error. Use create_files with the corrected file content.`}],f=await n.generate(l,{model:this.config.model,maxTokens:8192});return{files:f.files,explanation:f.content}}};function k(u){let e=u.split(`
28
+ `).filter(r=>r.trim());return e.length>30?e.slice(0,30).join(`
29
+ `)+`
30
+ ... (truncated)`:e.join(`
31
+ `)}export{v as a};
32
+ //# sourceMappingURL=chunk-6PVFYFUE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime/heal.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport { execa } from \"execa\"\nimport { createProvider, type ProviderName } from \"@/agent/providers\"\nimport type { GenerationMessage, GeneratedFile } from \"@/agent/providers/types\"\nimport { detectTechStack, formatTechStack } from \"@/agent/context/tech-stack\"\nimport { Memory } from \"./memory\"\n\n// --- Auto-Heal Engine: detect errors, diagnose, fix, verify ---\n// When generated code fails (build error, test failure, runtime crash),\n// the heal engine reads the error, understands the context, generates\n// a fix, and re-verifies it works.\n\nexport interface HealConfig {\n enabled: boolean\n testCommand?: string\n buildCommand?: string\n lintCommand?: string\n maxAttempts: number\n provider: ProviderName\n model?: string\n apiKey?: string\n}\n\nexport interface HealResult {\n healed: boolean\n attempts: number\n error: string\n fix?: string\n filesChanged: string[]\n}\n\nconst DEFAULT_HEAL_CONFIG: HealConfig = {\n enabled: true,\n maxAttempts: 3,\n provider: \"claude-code\",\n}\n\nexport class HealEngine {\n private config: HealConfig\n private memory: Memory\n\n constructor(\n private cwd: string,\n config?: Partial<HealConfig>,\n memory?: Memory\n ) {\n this.config = { ...DEFAULT_HEAL_CONFIG, ...config }\n this.memory = memory || new Memory(cwd)\n }\n\n async detectAndHeal(\n generatedFiles: string[],\n originalTask: string,\n entryId?: string\n ): Promise<HealResult> {\n if (!this.config.enabled) {\n return { healed: false, attempts: 0, error: \"Heal disabled\", filesChanged: [] }\n }\n\n // 1. Run verification commands\n const error = await this.runVerification()\n if (!error) {\n return { healed: true, attempts: 0, error: \"\", filesChanged: [] }\n }\n\n // 2. Attempt to heal\n let attempts = 0\n let currentError = error\n const allChangedFiles: string[] = []\n\n while (attempts < this.config.maxAttempts) {\n attempts++\n\n const fix = await this.generateFix(\n currentError,\n generatedFiles,\n originalTask,\n attempts\n )\n\n if (!fix.files.length) {\n break // Agent couldn't generate a fix\n }\n\n // Apply the fix\n for (const file of fix.files) {\n const filePath = path.isAbsolute(file.path)\n ? file.path\n : path.resolve(this.cwd, file.path)\n\n const dir = path.dirname(filePath)\n await fs.mkdir(dir, { recursive: true })\n await fs.writeFile(filePath, file.content, \"utf8\")\n allChangedFiles.push(file.path)\n }\n\n // Re-verify\n const newError = await this.runVerification()\n if (!newError) {\n // Healed successfully\n if (entryId) {\n await this.memory.recordHeal(entryId, error, true, allChangedFiles)\n }\n return {\n healed: true,\n attempts,\n error,\n fix: fix.explanation,\n filesChanged: allChangedFiles,\n }\n }\n\n currentError = newError\n }\n\n // Failed to heal\n if (entryId) {\n await this.memory.recordHeal(entryId, error, false, allChangedFiles)\n }\n\n return {\n healed: false,\n attempts,\n error: currentError,\n filesChanged: allChangedFiles,\n }\n }\n\n private async runVerification(): Promise<string | null> {\n const commands = [\n this.config.buildCommand,\n this.config.lintCommand,\n this.config.testCommand,\n ].filter(Boolean) as string[]\n\n // Auto-detect commands if not configured\n if (!commands.length) {\n const detected = await this.detectCommands()\n commands.push(...detected)\n }\n\n for (const cmd of commands) {\n try {\n const [bin, ...args] = cmd.split(\" \")\n await execa(bin, args, {\n cwd: this.cwd,\n timeout: 60000,\n reject: true,\n })\n } catch (error: any) {\n const stderr = error.stderr || \"\"\n const stdout = error.stdout || \"\"\n const message = error.message || \"\"\n return truncateError(`Command failed: ${cmd}\\n${stderr}\\n${stdout}\\n${message}`)\n }\n }\n\n return null // All passed\n }\n\n private async detectCommands(): Promise<string[]> {\n const commands: string[] = []\n const pkgPath = path.resolve(this.cwd, \"package.json\")\n\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf8\"))\n const scripts = pkg.scripts || {}\n if (scripts.build) commands.push(\"npm run build\")\n if (scripts.typecheck) commands.push(\"npm run typecheck\")\n if (scripts.lint) commands.push(\"npm run lint\")\n if (scripts.test) commands.push(\"npm run test\")\n } catch {\n // Skip if can't read package.json\n }\n }\n\n // Python projects\n if (existsSync(path.resolve(this.cwd, \"pyproject.toml\"))) {\n if (existsSync(path.resolve(this.cwd, \"conftest.py\"))) {\n commands.push(\"python -m pytest --tb=short -q\")\n }\n }\n\n return commands\n }\n\n private async generateFix(\n error: string,\n affectedFiles: string[],\n originalTask: string,\n attempt: number\n ): Promise<{ files: GeneratedFile[]; explanation: string }> {\n const provider = createProvider(this.config.provider, this.config.apiKey)\n\n // Read affected files\n const fileContents: string[] = []\n for (const file of affectedFiles.slice(0, 5)) {\n const filePath = path.resolve(this.cwd, file)\n if (existsSync(filePath)) {\n const content = await fs.readFile(filePath, \"utf8\")\n fileContents.push(`## ${file}\\n\\`\\`\\`\\n${content.slice(0, 3000)}\\n\\`\\`\\``)\n }\n }\n\n const techStack = await detectTechStack(this.cwd)\n\n const messages: GenerationMessage[] = [\n {\n role: \"system\",\n content: `You are agentx auto-heal. You fix errors in generated code.\n\nProject tech stack: ${formatTechStack(techStack)}\n\nRULES:\n- Fix ONLY the error — do not refactor or add features\n- Output the COMPLETE fixed file content via create_files\n- Be minimal — smallest change that fixes the error\n- This is attempt ${attempt} — ${attempt > 1 ? \"previous fix attempts failed, try a different approach\" : \"analyze carefully\"}`,\n },\n {\n role: \"user\",\n content: `The following code was generated for task: \"${originalTask}\"\n\n## Error\n\\`\\`\\`\n${error}\n\\`\\`\\`\n\n## Affected Files\n${fileContents.join(\"\\n\\n\")}\n\nFix the error. Use create_files with the corrected file content.`,\n },\n ]\n\n const result = await provider.generate(messages, {\n model: this.config.model,\n maxTokens: 8192,\n })\n\n return {\n files: result.files,\n explanation: result.content,\n }\n }\n}\n\nfunction truncateError(error: string): string {\n const lines = error.split(\"\\n\").filter((l) => l.trim())\n if (lines.length > 30) {\n return lines.slice(0, 30).join(\"\\n\") + \"\\n... (truncated)\"\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"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","names":["existsSync","fs","path","execa","DEFAULT_HEAL_CONFIG","HealEngine","cwd","config","memory","Memory","generatedFiles","originalTask","entryId","error","attempts","currentError","allChangedFiles","fix","file","filePath","path","dir","fs","newError","commands","detected","cmd","bin","args","execa","stderr","stdout","message","truncateError","pkgPath","existsSync","scripts","affectedFiles","attempt","provider","createProvider","fileContents","content","techStack","detectTechStack","messages","formatTechStack","result","lines","l"]}
@@ -0,0 +1,3 @@
1
+ import{existsSync as S,promises as v}from"fs";import k from"path";import x from"fast-glob";import{z as l}from"zod";var h=l.object({name:l.string(),description:l.string(),version:l.string().optional(),author:l.string().optional(),tags:l.array(l.string()).optional(),globs:l.array(l.string()).optional(),triggers:l.array(l.object({pattern:l.string(),description:l.string().optional()})).optional()});var L=[".skills",".claude/skills","skills"],d="SKILL.md";async function T(a){let e=[];for(let r of L){let n=k.resolve(a,r);if(!S(n))continue;let c=await x.glob(`**/${d}`,{cwd:n,deep:3});for(let s of c){let t=k.resolve(n,s),i=await w(t);i&&(i.source="local",i.path=t,e.push(i))}}let m=k.resolve(a,d);if(S(m)){let r=await w(m);r&&(r.source="local",r.path=m,e.push(r))}return e}async function w(a){try{let e=await v.readFile(a,"utf8");return F(e)}catch{return null}}function F(a){try{let e=a.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);if(!e)return{frontmatter:{name:"unnamed",description:"No description"},instructions:a.trim(),source:"local"};let[,m,r]=e,n=M(m);return{frontmatter:h.parse(n),instructions:r.trim(),source:"local"}}catch{return null}}function M(a){let e={},m=a.split(`
2
+ `),r="",n=!1,c=[];for(let s of m){let t=s.trim();if(!t)continue;if(t.startsWith("- ")&&n){c.push(t.slice(2).trim().replace(/^["']|["']$/g,""));continue}n&&r&&(e[r]=c,n=!1,c=[]);let i=t.match(/^(\w+)\s*:\s*(.*)$/);if(i){let[,p,g]=i;r=p,g.trim()===""?(n=!0,c=[]):e[p]=g.trim().replace(/^["']|["']$/g,"")}}return n&&r&&(e[r]=c),e}function W(a,e,m){let r=[],n=e.toLowerCase(),c=new Set(n.split(/\s+/));for(let s of a){let t=0,i="";if(s.frontmatter.triggers)for(let o of s.frontmatter.triggers)try{new RegExp(o.pattern,"i").test(e)&&(t=Math.max(t,.9),i=`Trigger match: ${o.description||o.pattern}`)}catch{n.includes(o.pattern.toLowerCase())&&(t=Math.max(t,.7),i=`Keyword match: ${o.pattern}`)}if(s.frontmatter.tags){let o=s.frontmatter.tags.filter(f=>c.has(f.toLowerCase())||n.includes(f.toLowerCase()));if(o.length){let f=Math.min(o.length*.3,.8);f>t&&(t=f,i=`Tag match: ${o.join(", ")}`)}}let p=s.frontmatter.name.toLowerCase().split(/[-_\s]+/),g=s.frontmatter.description.toLowerCase().split(/\s+/),y=new Set([...p,...g]),u=[...c].filter(o=>y.has(o)&&o.length>3);if(u.length>0){let o=Math.min(u.length*.2,.6);o>t&&(t=o,i=`Content match: ${u.join(", ")}`)}t>.1&&r.push({skill:s,relevance:t,matchReason:i})}return r.sort((s,t)=>t.relevance-s.relevance)}export{T as a,w as b,F as c,W as d};
3
+ //# sourceMappingURL=chunk-FRFR27IN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent/skills/loader.ts","../src/agent/skills/types.ts"],"sourcesContent":["import { existsSync, promises as fs } from \"fs\"\nimport path from \"path\"\nimport fg from \"fast-glob\"\nimport { logger } from \"@/utils/logger\"\nimport type { Skill, SkillFrontmatter, SkillMatch } from \"./types\"\nimport { skillFrontmatterSchema } from \"./types\"\n\n// --- Load skills from local files and remote packages ---\n\nconst SKILL_DIRS = [\".skills\", \".claude/skills\", \"skills\"]\nconst SKILL_FILE = \"SKILL.md\"\n\nexport async function loadLocalSkills(cwd: string): Promise<Skill[]> {\n const skills: Skill[] = []\n\n for (const dir of SKILL_DIRS) {\n const skillDir = path.resolve(cwd, dir)\n if (!existsSync(skillDir)) continue\n\n const skillFiles = await fg.glob(`**/${SKILL_FILE}`, {\n cwd: skillDir,\n deep: 3,\n })\n\n for (const file of skillFiles) {\n const fullPath = path.resolve(skillDir, file)\n const skill = await parseSkillFile(fullPath)\n if (skill) {\n skill.source = \"local\"\n skill.path = fullPath\n skills.push(skill)\n }\n }\n }\n\n // Also check for a single SKILL.md at project root\n const rootSkill = path.resolve(cwd, SKILL_FILE)\n if (existsSync(rootSkill)) {\n const skill = await parseSkillFile(rootSkill)\n if (skill) {\n skill.source = \"local\"\n skill.path = rootSkill\n skills.push(skill)\n }\n }\n\n return skills\n}\n\nexport async function parseSkillFile(filePath: string): Promise<Skill | null> {\n try {\n const content = await fs.readFile(filePath, \"utf8\")\n return parseSkillContent(content)\n } catch {\n return null\n }\n}\n\nexport function parseSkillContent(content: string): Skill | null {\n try {\n // Parse YAML frontmatter\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n\n if (!frontmatterMatch) {\n // No frontmatter - treat entire content as instructions with minimal metadata\n return {\n frontmatter: { name: \"unnamed\", description: \"No description\" },\n instructions: content.trim(),\n source: \"local\",\n }\n }\n\n const [, frontmatterRaw, instructions] = frontmatterMatch\n const frontmatter = parseYamlFrontmatter(frontmatterRaw)\n\n const validated = skillFrontmatterSchema.parse(frontmatter)\n\n return {\n frontmatter: validated,\n instructions: instructions.trim(),\n source: \"local\",\n }\n } catch {\n return null\n }\n}\n\nfunction parseYamlFrontmatter(raw: string): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n const lines = raw.split(\"\\n\")\n\n let currentKey = \"\"\n let inArray = false\n let arrayValues: string[] = []\n\n for (const line of lines) {\n const trimmed = line.trim()\n if (!trimmed) continue\n\n // Array item\n if (trimmed.startsWith(\"- \") && inArray) {\n arrayValues.push(trimmed.slice(2).trim().replace(/^[\"']|[\"']$/g, \"\"))\n continue\n }\n\n // Save previous array if we were in one\n if (inArray && currentKey) {\n result[currentKey] = arrayValues\n inArray = false\n arrayValues = []\n }\n\n // Key-value pair\n const kvMatch = trimmed.match(/^(\\w+)\\s*:\\s*(.*)$/)\n if (kvMatch) {\n const [, key, value] = kvMatch\n currentKey = key\n\n if (value.trim() === \"\") {\n // Could be start of an array or nested object\n inArray = true\n arrayValues = []\n } else {\n // Simple value\n result[key] = value.trim().replace(/^[\"']|[\"']$/g, \"\")\n }\n }\n }\n\n // Save last array if any\n if (inArray && currentKey) {\n result[currentKey] = arrayValues\n }\n\n return result\n}\n\nexport function matchSkillsToTask(\n skills: Skill[],\n taskDescription: string,\n outputType?: string\n): SkillMatch[] {\n const matches: SkillMatch[] = []\n const taskLower = taskDescription.toLowerCase()\n const taskWords = new Set(taskLower.split(/\\s+/))\n\n for (const skill of skills) {\n let relevance = 0\n let matchReason = \"\"\n\n // Check trigger patterns\n if (skill.frontmatter.triggers) {\n for (const trigger of skill.frontmatter.triggers) {\n try {\n const regex = new RegExp(trigger.pattern, \"i\")\n if (regex.test(taskDescription)) {\n relevance = Math.max(relevance, 0.9)\n matchReason = `Trigger match: ${trigger.description || trigger.pattern}`\n }\n } catch {\n // Invalid regex - try simple string match\n if (taskLower.includes(trigger.pattern.toLowerCase())) {\n relevance = Math.max(relevance, 0.7)\n matchReason = `Keyword match: ${trigger.pattern}`\n }\n }\n }\n }\n\n // Check tags overlap\n if (skill.frontmatter.tags) {\n const tagOverlap = skill.frontmatter.tags.filter(\n (tag) => taskWords.has(tag.toLowerCase()) || taskLower.includes(tag.toLowerCase())\n )\n if (tagOverlap.length) {\n const tagRelevance = Math.min(tagOverlap.length * 0.3, 0.8)\n if (tagRelevance > relevance) {\n relevance = tagRelevance\n matchReason = `Tag match: ${tagOverlap.join(\", \")}`\n }\n }\n }\n\n // Check name/description overlap\n const nameWords = skill.frontmatter.name.toLowerCase().split(/[-_\\s]+/)\n const descWords = skill.frontmatter.description.toLowerCase().split(/\\s+/)\n const allSkillWords = new Set([...nameWords, ...descWords])\n\n const overlap = [...taskWords].filter((w) => allSkillWords.has(w) && w.length > 3)\n if (overlap.length > 0) {\n const wordRelevance = Math.min(overlap.length * 0.2, 0.6)\n if (wordRelevance > relevance) {\n relevance = wordRelevance\n matchReason = `Content match: ${overlap.join(\", \")}`\n }\n }\n\n if (relevance > 0.1) {\n matches.push({ skill, relevance, matchReason })\n }\n }\n\n return matches.sort((a, b) => b.relevance - a.relevance)\n}\n","import { z } from \"zod\"\n\n// --- Skill types aligned with skills.sh / Agent Skills spec ---\n\nexport const skillFrontmatterSchema = z.object({\n name: z.string(),\n description: z.string(),\n version: z.string().optional(),\n author: z.string().optional(),\n tags: z.array(z.string()).optional(),\n globs: z.array(z.string()).optional(),\n // When to automatically apply this skill\n triggers: z\n .array(\n z.object({\n pattern: z.string(), // regex or keyword pattern\n description: z.string().optional(),\n })\n )\n .optional(),\n})\n\nexport type SkillFrontmatter = z.infer<typeof skillFrontmatterSchema>\n\nexport interface Skill {\n frontmatter: SkillFrontmatter\n instructions: string // Markdown body\n source: \"local\" | \"remote\" | \"generated\"\n path?: string // Local file path\n packageId?: string // e.g., \"intellectronica/agent-skills\"\n}\n\nexport interface SkillMatch {\n skill: Skill\n relevance: number // 0-1 how relevant to current task\n matchReason: string\n}\n\nexport interface SkillPackage {\n owner: string\n repo: string\n skills: Skill[]\n}\n"],"mappings":"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","names":["existsSync","fs","path","fg","z","skillFrontmatterSchema","SKILL_DIRS","SKILL_FILE","loadLocalSkills","cwd","skills","dir","skillDir","path","existsSync","skillFiles","fg","file","fullPath","skill","parseSkillFile","rootSkill","filePath","content","fs","parseSkillContent","frontmatterMatch","frontmatterRaw","instructions","frontmatter","parseYamlFrontmatter","skillFrontmatterSchema","raw","result","lines","currentKey","inArray","arrayValues","line","trimmed","kvMatch","key","value","matchSkillsToTask","taskDescription","outputType","matches","taskLower","taskWords","relevance","matchReason","trigger","tagOverlap","tag","tagRelevance","nameWords","descWords","allSkillWords","overlap","w","wordRelevance","a","b"]}
@@ -0,0 +1,46 @@
1
+ import{existsSync as Y,readFileSync as z,writeFileSync as te,mkdirSync as ne}from"fs";import P from"path";import Q from"os";import U from"chalk";import j from"prompts";import q from"chalk";var C={error(...r){console.log(q.red(...r))},warn(...r){console.log(q.yellow(...r))},info(...r){console.log(q.cyan(...r))},success(...r){console.log(q.green(...r))},break(){console.log("")}};var H=P.join(Q.homedir(),".agentx"),J=P.join(H,"auth.json");function R(){if(!Y(J))return null;try{let r=JSON.parse(z(J,"utf8"));return r.provider&&r.authType&&r.token&&r.model?r:null}catch{return null}}function oe(r){Y(H)||ne(H,{recursive:!0}),te(J,JSON.stringify(r,null,2),"utf8")}function N(r){if(r?.trim()){let p=r.startsWith("sk-ant-oat")?"oauth":"api-key";return{token:r.trim(),authType:p}}let e=process.env.ANTHROPIC_OAUTH_TOKEN;if(e?.trim())return{token:e.trim(),authType:"oauth"};let t=process.env.ANTHROPIC_API_KEY;if(t?.trim())return{token:t.trim(),authType:"api-key"};let n=R();if(n)return{token:n.token,authType:n.authType};let o=Q.homedir(),i=[P.join(o,".openclaw","agents","main","agent","auth-profiles.json"),P.join(o,".openclaw","auth-profiles.json"),P.join(o,".openclaw","credentials","oauth.json"),P.join(o,".claude","oauth.json"),P.join(o,".config","claude","oauth.json"),P.join(o,".config","anthropic","oauth.json")];for(let p of i){let h=re(p);if(h)return{token:h,authType:"oauth"}}return null}function re(r){if(!Y(r))return"";try{let e=JSON.parse(z(r,"utf8"));if(e.profiles){let t="";for(let[n,o]of Object.entries(e.profiles))if(n.startsWith("anthropic")){if(o.type==="oauth"&&o.access){if(o.expires&&Date.now()>o.expires)continue;return o.access}o.type==="token"&&o.token&&(t=o.token)}if(t)return t}return e.anthropic?.access?e.anthropic.access:e.anthropic?.token?e.anthropic.token:""}catch{return""}}var B=[{id:"claude-sonnet-4-20250514",label:"anthropic/claude-sonnet-4",hint:"Claude Sonnet 4 \xB7 ctx 200k \xB7 recommended"},{id:"claude-opus-4-20250514",label:"anthropic/claude-opus-4-5",hint:"Claude Opus 4.5 \xB7 ctx 200k \xB7 reasoning"},{id:"claude-haiku-4-20250514",label:"anthropic/claude-haiku-4",hint:"Claude Haiku 4 \xB7 ctx 200k \xB7 fast"}];async function se(){let r=process.env.ANTHROPIC_API_KEY;if(r?.trim()){let n=r.slice(0,10)+"..."+r.slice(-4),{useExisting:o}=await j({type:"confirm",name:"useExisting",message:`Use existing ANTHROPIC_API_KEY (env, ${n})?`,initial:!0});if(o)return{token:r.trim(),authType:"api-key"}}let e=R();if(e?.authType==="api-key"&&e.token){let n=e.token.slice(0,10)+"..."+e.token.slice(-4),{useExisting:o}=await j({type:"confirm",name:"useExisting",message:`Use stored API key (${n})?`,initial:!0});if(o)return{token:e.token,authType:"api-key"}}let{apiKey:t}=await j({type:"password",name:"apiKey",message:"Enter Anthropic API key",validate:n=>n.startsWith("sk-ant-api")?n.length<40?"API key seems too short":!0:"API key must start with sk-ant-api"});return t?{token:t,authType:"api-key"}:null}async function ie(){C.break();let r=46,e="Run `claude setup-token` in your terminal.",t="Then paste the generated token below.";console.log(` ${U.cyan("Anthropic setup-token")} ${"\u2500".repeat(r-21-1)}\u256E`),console.log(` ${" ".repeat(r)}\u2502`),console.log(` ${e}${" ".repeat(r-e.length)}\u2502`),console.log(` ${t}${" ".repeat(r-t.length)}\u2502`),console.log(` ${" ".repeat(r)}\u2502`),console.log(` ${"\u2500".repeat(r)}\u256F`),C.break();let{token:n}=await j({type:"password",name:"token",message:"Paste Anthropic setup-token",validate:i=>i.trim()?i.length<40?"Token seems too short":!0:"Token is required"});if(!n)return null;let{tokenName:o}=await j({type:"text",name:"tokenName",message:"Token name (blank = default)"});return{token:n,authType:"oauth"}}async function ae(){for(;;){let{provider:r}=await j({type:"select",name:"provider",message:"Model/auth provider",choices:[{title:"Anthropic",value:"anthropic",description:"setup-token + API key"}]});if(r===void 0)return null;let{method:e}=await j({type:"select",name:"method",message:"Anthropic auth method",choices:[{title:"Anthropic token (paste setup-token)",value:"setup-token",description:"run `claude setup-token` elsewhere, then paste the token here"},{title:"Anthropic API key",value:"api-key"},{title:U.dim("Back"),value:"back"}]});if(e===void 0)return null;if(e!=="back")return e==="api-key"?se():e==="setup-token"?ie():null}}async function le(){let r=R(),e=B.map(n=>({title:n.label,value:n.id,description:n.hint+(r?.model===n.id?" \xB7 current":"")}));if(r?.model){let n=B.find(i=>i.id===r.model),o=n?n.label:r.model;e.unshift({title:`Keep current (${o})`,value:r.model,description:"no change"})}let{modelId:t}=await j({type:"select",name:"modelId",message:"Default model",choices:e});return t||null}async function ce(){let r=await ae();if(!r)return null;let e=await le();if(!e)return null;let t={provider:r.authType==="oauth"?"claude-code":"claude",authType:r.authType,token:r.token,model:e};return oe(t),C.break(),C.success("Configuration saved to ~/.agentx/auth.json"),console.log(` Provider: ${U.bold("anthropic")} (${t.authType})`),console.log(` Model: ${U.bold(B.find(n=>n.id===t.model)?.label||t.model)}`),t}async function _e(r){return N(r)?!0:(C.warn("No AI credentials configured."),C.break(),await ce()!==null)}var L=[{name:"create_files",description:"Create one or more files as output. Use this when you need to generate code, documents, configs, or any file-based output.",input_schema:{type:"object",properties:{files:{type:"array",items:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root (e.g., src/components/Button.tsx)"},content:{type:"string",description:"The full content of the file"},language:{type:"string",description:"Programming language or file type"},description:{type:"string",description:"Brief description of what this file does"}},required:["path","content"]}},summary:{type:"string",description:"Brief summary of all generated files"}},required:["files"]},permission:"file-write"},{name:"ask_user",description:"Ask the user a clarifying question when you need more information to proceed. Use this when the request is ambiguous or you need to confirm important decisions.",input_schema:{type:"object",properties:{question:{type:"string",description:"The question to ask the user"},options:{type:"array",items:{type:"string"},description:"Optional list of choices for the user"}},required:["question"]},permission:"none"},{name:"read_file",description:"Read the contents of a file. Use this to inspect existing code, understand patterns, check implementations, or gather context before generating code.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root"},max_lines:{type:"number",description:"Maximum number of lines to read. Defaults to 500. Use for large files."}},required:["path"]},permission:"file-read"},{name:"search_files",description:"Search for files matching a pattern and optionally search their content with a regex. Use this to find relevant code, understand project structure, or locate specific patterns.",input_schema:{type:"object",properties:{pattern:{type:"string",description:"Glob pattern to match files (e.g., 'src/**/*.ts', '*.json')"},content_regex:{type:"string",description:"Optional regex to search within matched files. Returns matching lines."},max_results:{type:"number",description:"Maximum number of results to return. Default: 50."}},required:["pattern"]},permission:"none"},{name:"list_directory",description:"List files and directories at a given path. Use this to explore project structure.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative directory path from project root. Defaults to '.' (root)."},recursive:{type:"boolean",description:"List recursively. Default: false."},max_depth:{type:"number",description:"Maximum depth for recursive listing. Default: 3."}}},permission:"none"},{name:"run_command",description:"Execute a shell command. Use this to run build tools, test commands, linters, or inspect the environment. Commands run in the project root directory.",input_schema:{type:"object",properties:{command:{type:"string",description:"The shell command to execute"},timeout:{type:"number",description:"Timeout in milliseconds. Default: 30000 (30 seconds)."}},required:["command"]},permission:"command"},{name:"edit_file",description:"Apply search-and-replace edits to an existing file. Use this for targeted modifications to existing code rather than rewriting entire files.",input_schema:{type:"object",properties:{path:{type:"string",description:"Relative file path from project root"},edits:{type:"array",items:{type:"object",properties:{old_text:{type:"string",description:"The exact text to find in the file"},new_text:{type:"string",description:"The replacement text"}},required:["old_text","new_text"]},description:"List of search/replace pairs to apply in order"}},required:["path","edits"]},permission:"file-write"}];function pe(r){return(r?L.filter(t=>r.includes(t.name)):L).map(({name:t,description:n,input_schema:o})=>({name:t,description:n,input_schema:o}))}function E(){return pe(["create_files","ask_user"])}function we(){let r=L.filter(t=>t.name!=="create_files"&&t.name!=="ask_user");if(r.length===0)return"";let e=["# Available Capabilities","In addition to generating files, you have the following capabilities:",""];for(let t of r)e.push(`## ${t.name}`),e.push(t.description),e.push("");return e.join(`
2
+ `)}var Ae=L.map(r=>r.name);var V="claude-sonnet-4-20250514",W=8192,M=class{name="claude";apiKey;authType="api-key";constructor(e){if(this.apiKey=e||process.env.ANTHROPIC_API_KEY||"",!this.apiKey){let t=N();t&&(this.apiKey=t.token,this.authType=t.authType)}if(!this.apiKey)throw new Error("Anthropic API key required. Run `agentx model` to configure, set ANTHROPIC_API_KEY, or pass --api-key.")}async generate(e,t){let n=t?.model||V,o=t?.maxTokens||W,i=e.find(c=>c.role==="system"),p=e.filter(c=>c.role!=="system").map(c=>({role:c.role,content:c.content})),h={model:n,max_tokens:o,messages:p,tools:E()};i&&(h.system=i.content),t?.temperature!==void 0&&(h.temperature=t.temperature);let k=await this.callApi(h);return this.parseResponse(k)}async generateRaw(e,t,n,o){let i=o?.model||V,p=o?.maxTokens||W,h={model:i,max_tokens:p,system:t,messages:e,tools:n};o?.temperature!==void 0&&(h.temperature=o.temperature);let k=await this.callApi(h);return{content:k.content.map(d=>d.type==="text"?{type:"text",text:d.text||""}:d.type==="tool_use"?{type:"tool_use",id:d.id||"",name:d.name||"",input:d.input||{}}:{type:"text",text:""}),stop_reason:k.stop_reason,usage:k.usage}}async*stream(e,t){let n=t?.model||V,o=t?.maxTokens||W,i=e.find(f=>f.role==="system"),p=e.filter(f=>f.role!=="system").map(f=>({role:f.role,content:f.content})),h={model:n,max_tokens:o,messages:p,stream:!0,tools:E()};i&&(h.system=i.content);let k=this.buildHeaders(),c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:k,body:JSON.stringify(h)});if(!c.ok){let f=await c.text();yield{type:"error",error:`Anthropic API error (${c.status}): ${f}`};return}let d=c.body?.getReader();if(!d){yield{type:"error",error:"No response body"};return}let m=new TextDecoder,l="",_=[],v="",x,A=0,s=null,g=f=>{let O=(f||"").trim();if(!O||O.includes("```"))return;let T=O.replace(/\r\n/g,`
3
+ `).split(`
4
+ `),w=T.slice(Math.max(0,T.length-20)),u=w.join(`
5
+ `);if(/\b(plan|proposal)\b/i.test(u)&&/(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(u))return`The provider is requesting plan approval.
6
+ Reply with:
7
+ - approve
8
+ - revise: <what to change>
9
+ - cancel`;let y=b=>/^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(b.trim()),D=-1;for(let b=w.length-1;b>=0;b--){let I=w[b].trim();if(I&&(y(I)||I.includes("?"))){D=b;break}}if(D===-1)return;let S=[];for(let b=D;b<w.length&&S.length<8;b++){let I=w[b],K=I.trim();if(S.length>0&&!K||S.length>0&&!/^(options?:|[-*]\s|\d+[\).]\s)/i.test(K)&&!K.includes("?"))break;S.push(I.trimEnd())}let $=S.join(`
10
+ `).trim();if(!($.length<5))return $.length>800?$.slice(0,800).trimEnd():$};try{for(;;){let{done:f,value:O}=await d.read();if(f)break;l+=m.decode(O,{stream:!0});let T=l.split(`
11
+ `);l=T.pop()||"";for(let w of T){if(!w.startsWith("data: "))continue;let u=w.slice(6).trim();if(u!=="[DONE]")try{let a=JSON.parse(u);if(a.type==="content_block_start"&&a.content_block?.type==="tool_use"){if(s){yield{type:"tool_use_end",name:s.name};try{let y=JSON.parse(s.json||"{}");s.name==="create_files"&&y&&(Array.isArray(y.files)&&_.push(...y.files),typeof y.summary=="string"&&y.summary.trim()&&(v+=`
12
+ ${y.summary}`)),s.name==="ask_user"&&y&&typeof y.question=="string"&&y.question.trim()&&(x=y.question,Array.isArray(y.options)&&y.options.length&&(x+=`
13
+ Options: ${y.options.join(", ")}`))}catch{}finally{s=null}}s={name:a.content_block.name,id:a.content_block.id,json:""},yield{type:"tool_use_start",name:a.content_block.name,id:a.content_block.id}}if(a.type==="content_block_delta"&&(a.delta?.type==="text_delta"&&(v+=a.delta.text,yield{type:"text_delta",text:a.delta.text}),a.delta?.type==="input_json_delta"&&(s&&(s.json+=a.delta.partial_json||""),yield{type:"tool_use_delta",json:a.delta.partial_json})),a.type==="content_block_stop"&&s){yield{type:"tool_use_end",name:s.name};try{let y=JSON.parse(s.json||"{}");s.name==="create_files"&&y&&(Array.isArray(y.files)&&_.push(...y.files),typeof y.summary=="string"&&y.summary.trim()&&(v+=`
14
+ ${y.summary}`)),s.name==="ask_user"&&y&&typeof y.question=="string"&&y.question.trim()&&(x=y.question,Array.isArray(y.options)&&y.options.length&&(x+=`
15
+ Options: ${y.options.join(", ")}`))}catch{}finally{s=null}}a.type==="message_delta"&&a.usage&&(A=(a.usage.input_tokens||0)+(a.usage.output_tokens||0)),a.type}catch{}}}}finally{d.releaseLock()}if(s){yield{type:"tool_use_end",name:s.name};try{let f=JSON.parse(s.json||"{}");s.name==="create_files"&&f&&(Array.isArray(f.files)&&_.push(...f.files),typeof f.summary=="string"&&f.summary.trim()&&(v+=`
16
+ ${f.summary}`)),s.name==="ask_user"&&f&&typeof f.question=="string"&&f.question.trim()&&(x=f.question,Array.isArray(f.options)&&f.options.length&&(x+=`
17
+ Options: ${f.options.join(", ")}`))}catch{}s=null}!x&&_.length===0&&(x=g(v)),yield{type:"done",result:{content:v,files:_,followUp:x,tokensUsed:A}}}buildHeaders(){let e={"Content-Type":"application/json","anthropic-version":"2023-06-01"};return this.authType==="oauth"?(e.Authorization=`Bearer ${this.apiKey}`,e["anthropic-beta"]="claude-code-20250219,oauth-2025-04-20",e["user-agent"]="claude-cli/2.1.2 (external, cli)",e["x-app"]="cli",e["anthropic-dangerous-direct-browser-access"]="true"):e["x-api-key"]=this.apiKey,e}async callApi(e){let t=this.buildHeaders(),n=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:t,body:JSON.stringify(e)});if(!n.ok){let o=await n.text();throw new Error(`Anthropic API error (${n.status}): ${o}`)}return await n.json()}parseResponse(e){let t=[],n="",o;for(let i of e.content)if(i.type==="text"&&(n+=i.text||""),i.type==="tool_use"){if(i.name==="create_files"&&i.input){let p=i.input;t.push(...p.files||[]),p.summary&&(n+=`
18
+ ${p.summary}`)}if(i.name==="ask_user"&&i.input){let p=i.input;o=p.question,p.options?.length&&(o+=`
19
+ Options: ${p.options.join(", ")}`)}}return{content:n,files:t,followUp:o,tokensUsed:e.usage.input_tokens+e.usage.output_tokens}}};import{execa as Z}from"execa";var F="claude-sonnet-4-20250514",X=8192,ee={"claude-sonnet-4-20250514":"sonnet","claude-opus-4-20250514":"opus","claude-haiku-4-20250514":"haiku"},G=class{name="claude-code";credential;constructor(){let e=R();if(e){this.credential={type:e.authType,token:e.token};return}let t=N();if(!t)throw new Error(`No Claude credentials found.
20
+ Options:
21
+ 1. Run \`agentx model\` to configure credentials
22
+ 2. Set ANTHROPIC_API_KEY environment variable
23
+ 3. Set ANTHROPIC_OAUTH_TOKEN environment variable`);this.credential={type:t.authType,token:t.token}}async generate(e,t){return this.credential.type==="oauth"?this.generateViaCli(e,t):this.generateViaApi(e,t)}async generateRaw(e,t,n,o){if(this.credential.type==="oauth")throw new Error("generateRaw() not available for OAuth/CLI mode");let i=o?.model||F,p=o?.maxTokens||X,h={model:i,max_tokens:p,system:t,messages:e,tools:n};o?.temperature!==void 0&&(h.temperature=o.temperature);let k={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:k,body:JSON.stringify(h)});if(!c.ok){let l=await c.text();throw new Error(`Anthropic API error (${c.status}): ${l}`)}let d=await c.json();return{content:d.content.map(l=>l.type==="text"?{type:"text",text:l.text||""}:l.type==="tool_use"?{type:"tool_use",id:l.id||"",name:l.name||"",input:l.input||{}}:{type:"text",text:""}),stop_reason:d.stop_reason,usage:d.usage}}get supportsAgenticLoop(){return this.credential.type==="api-key"}async generateViaCli(e,t){let n=t?.model||R()?.model||F,o=ee[n]||n,i=e.find(l=>l.role==="system"),h=e.filter(l=>l.role==="user").map(l=>l.content).join(`
24
+
25
+ `),k=["-p","--output-format","json","--model",o,"--dangerously-skip-permissions"];i&&k.push("--append-system-prompt",i.content),k.push(h);let c={...process.env};delete c.ANTHROPIC_API_KEY,delete c.ANTHROPIC_API_KEY_OLD;let d=await Z("claude",k,{env:c,extendEnv:!1,reject:!1,timeout:3e5,stdin:"ignore"});if(d.exitCode!==0){let l=d.stderr||d.stdout||"Claude CLI failed";throw new Error(`Claude CLI error: ${l}`)}let m=d.stdout.trim();try{let l=JSON.parse(m);if(l.is_error&&l.result)throw new Error(l.result)}catch(l){if(l.message&&!l.message.includes("JSON"))throw l}return this.parseCliOutput(m)}async generateViaApi(e,t){let n=t?.model||F,o=t?.maxTokens||X,i=e.find(m=>m.role==="system"),p=e.filter(m=>m.role!=="system").map(m=>({role:m.role,content:m.content})),h={model:n,max_tokens:o,messages:p,tools:E()};i&&(h.system=i.content),t?.temperature!==void 0&&(h.temperature=t.temperature);let k={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:k,body:JSON.stringify(h)});if(!c.ok){let m=await c.text();throw new Error(`Anthropic API error (${c.status}): ${m}`)}let d=await c.json();return this.parseApiResponse(d)}async*stream(e,t){this.credential.type==="oauth"?yield*this.streamViaCli(e,t):yield*this.streamViaApi(e,t)}async*streamViaCli(e,t){let n=t?.model||R()?.model||F,o=ee[n]||n,i=e.find(s=>s.role==="system"),h=e.filter(s=>s.role==="user").map(s=>s.content).join(`
26
+
27
+ `),k=["-p","--output-format","stream-json","--model",o,"--dangerously-skip-permissions"];i&&k.push("--append-system-prompt",i.content),k.push(h);let c={...process.env};delete c.ANTHROPIC_API_KEY,delete c.ANTHROPIC_API_KEY_OLD;let d=Z("claude",k,{env:c,extendEnv:!1,reject:!1,timeout:3e5,stdin:"ignore"}),m="",l="",_;if(d.stderr&&d.stderr.on("data",s=>{l+=Buffer.isBuffer(s)?s.toString("utf8"):String(s)}),d.stdout){let s=new TextDecoder,g=d.stdout;for await(let f of g){let T=(typeof f=="string"?f:s.decode(f,{stream:!0})).split(`
28
+ `).filter(Boolean);for(let w of T)try{let u=JSON.parse(w);if(u.type==="error"){_=String(u.error||u.message||"Claude CLI error");continue}if(u.is_error&&(u.result||u.message)){_=String(u.result||u.message);continue}u.type==="assistant"&&u.message?(m+=u.message,yield{type:"text_delta",text:u.message}):u.type==="result"&&(m=u.result||m)}catch{m+=w,yield{type:"text_delta",text:w}}}}let v=await d;if(v.exitCode!==0&&!_&&(_=(l||v.stderr||v.stdout||"Claude CLI failed").toString().trim()),_){yield{type:"error",error:`Claude CLI error: ${_}`};return}let x=this.extractFilesFromText(m),A=x.length===0?this.inferFollowUpFromText(m):void 0;yield{type:"done",result:{content:m,files:x,followUp:A,tokensUsed:0}}}async*streamViaApi(e,t){let n=t?.model||F,o=t?.maxTokens||X,i=e.find(g=>g.role==="system"),p=e.filter(g=>g.role!=="system").map(g=>({role:g.role,content:g.content})),h={model:n,max_tokens:o,messages:p,tools:E(),stream:!0};i&&(h.system=i.content);let k={"Content-Type":"application/json","anthropic-version":"2023-06-01","x-api-key":this.credential.token},c=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:k,body:JSON.stringify(h)});if(!c.ok){let g=await c.text();yield{type:"error",error:`Anthropic API error (${c.status}): ${g}`};return}let d=c.body?.getReader();if(!d){yield{type:"error",error:"No response body"};return}let m=new TextDecoder,l="",_="",v=0,x=[],A,s=null;try{for(;;){let{done:g,value:f}=await d.read();if(g)break;l+=m.decode(f,{stream:!0});let O=l.split(`
29
+ `);l=O.pop()||"";for(let T of O){if(!T.startsWith("data: "))continue;let w=T.slice(6).trim();if(w!=="[DONE]")try{let u=JSON.parse(w);if(u.type==="content_block_start"&&u.content_block?.type==="tool_use"){if(s){yield{type:"tool_use_end",name:s.name};try{let a=JSON.parse(s.json||"{}");s.name==="create_files"&&a&&(Array.isArray(a.files)&&x.push(...a.files),typeof a.summary=="string"&&a.summary.trim()&&(_+=`
30
+ ${a.summary}`)),s.name==="ask_user"&&a&&typeof a.question=="string"&&a.question.trim()&&(A=a.question,Array.isArray(a.options)&&a.options.length&&(A+=`
31
+ Options: ${a.options.join(", ")}`))}catch{}finally{s=null}}s={name:u.content_block.name,id:u.content_block.id,json:""},yield{type:"tool_use_start",name:s.name,id:s.id}}if(u.type==="content_block_delta"&&u.delta?.type==="text_delta"&&(_+=u.delta.text,yield{type:"text_delta",text:u.delta.text}),u.type==="content_block_delta"&&u.delta?.type==="input_json_delta"&&(s&&(s.json+=u.delta.partial_json||""),yield{type:"tool_use_delta",json:u.delta.partial_json}),u.type==="content_block_stop"&&s){yield{type:"tool_use_end",name:s.name};try{let a=JSON.parse(s.json||"{}");s.name==="create_files"&&a&&(Array.isArray(a.files)&&x.push(...a.files),typeof a.summary=="string"&&a.summary.trim()&&(_+=`
32
+ ${a.summary}`)),s.name==="ask_user"&&a&&typeof a.question=="string"&&a.question.trim()&&(A=a.question,Array.isArray(a.options)&&a.options.length&&(A+=`
33
+ Options: ${a.options.join(", ")}`))}catch{}finally{s=null}}u.type==="message_delta"&&u.usage&&(v=(u.usage.input_tokens||0)+(u.usage.output_tokens||0))}catch{}}}}finally{d.releaseLock()}if(s){yield{type:"tool_use_end",name:s.name};try{let g=JSON.parse(s.json||"{}");s.name==="create_files"&&g&&(Array.isArray(g.files)&&x.push(...g.files),typeof g.summary=="string"&&g.summary.trim()&&(_+=`
34
+ ${g.summary}`)),s.name==="ask_user"&&g&&typeof g.question=="string"&&g.question.trim()&&(A=g.question,Array.isArray(g.options)&&g.options.length&&(A+=`
35
+ Options: ${g.options.join(", ")}`))}catch{}s=null}x.length===0&&x.push(...this.extractFilesFromText(_)),!A&&x.length===0&&(A=this.inferFollowUpFromText(_)),yield{type:"done",result:{content:_,files:x,followUp:A,tokensUsed:v}}}parseCliOutput(e){let t;try{t=JSON.parse(e)}catch{let p=this.inferFollowUpFromText(e.trim());return{content:e.trim(),files:[],followUp:p,tokensUsed:0}}let n=t.result||t.text||t.content||"",o=this.extractFilesFromText(n),i=o.length===0?this.inferFollowUpFromText(n):void 0;return{content:n,files:o,followUp:i,tokensUsed:t.usage?(t.usage.input_tokens||0)+(t.usage.output_tokens||0):0}}extractFilesFromText(e){let t=[],n=/```[\w]*\s*([\w/._-]+\.\w+)\n([\s\S]*?)```/g,o;for(;(o=n.exec(e))!==null;){let i=o[1],p=o[2];i&&p&&t.push({path:i,content:p.trimEnd()})}return t}inferFollowUpFromText(e){let t=(e||"").trim();if(!t||t.includes("```"))return;let n=t.replace(/\r\n/g,`
36
+ `).split(`
37
+ `),o=n.slice(Math.max(0,n.length-20)),i=o.join(`
38
+ `);if(/\b(plan|proposal)\b/i.test(i)&&/(ready for your review|review (the )?plan|requesting plan approval|awaiting approval|waiting for (your )?approval|approve (the )?plan|approval to proceed)/i.test(i))return`The provider is requesting plan approval.
39
+ Reply with:
40
+ - approve
41
+ - revise: <what to change>
42
+ - cancel`;let h=m=>/^(question|clarification|clarify|i need|need more|before i proceed|to proceed|please (confirm|clarify)|which|what|where|when|how|do you)/i.test(m.trim()),k=-1;for(let m=o.length-1;m>=0;m--){let l=o[m].trim();if(l&&(h(l)||l.includes("?"))){k=m;break}}if(k===-1)return;let c=[];for(let m=k;m<o.length&&c.length<8;m++){let l=o[m],_=l.trim();if(c.length>0&&!_||c.length>0&&!/^(options?:|[-*]\s|\d+[\).]\s)/i.test(_)&&!_.includes("?"))break;c.push(l.trimEnd())}let d=c.join(`
43
+ `).trim();if(!(d.length<5))return d.length>800?d.slice(0,800).trimEnd():d}parseApiResponse(e){let t=[],n="",o;for(let i of e.content)if(i.type==="text"&&(n+=i.text||""),i.type==="tool_use"){if(i.name==="create_files"&&i.input){let p=i.input;t.push(...p.files||[]),p.summary&&(n+=`
44
+ ${p.summary}`)}if(i.name==="ask_user"&&i.input){let p=i.input;o=p.question,p.options?.length&&(o+=`
45
+ Options: ${p.options.join(", ")}`)}}return{content:n,files:t,followUp:o,tokensUsed:e.usage.input_tokens+e.usage.output_tokens}}};function Ne(r="claude-code",e){let t=r;if(r==="claude-code"&&!e){let n=R();n&&(t=n.provider)}switch(t){case"claude-code":return new G;case"claude":return new M(e);case"openai":throw new Error("OpenAI provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx");case"ollama":throw new Error("Ollama provider coming soon. Set provider to 'claude-code' or contribute at github.com/anis-marrouchi/agentx");default:throw new Error(`Unknown provider: ${t}. Supported: claude-code, claude`)}}export{C as a,R as b,oe as c,N as d,ce as e,_e as f,pe as g,E as h,we as i,Ae as j,M as k,G as l,Ne as m};
46
+ //# sourceMappingURL=chunk-FUYKPFUV.js.map