palmier 0.1.9 → 0.2.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.
Files changed (81) hide show
  1. package/CLAUDE.md +9 -0
  2. package/README.md +168 -32
  3. package/dist/agents/agent.d.ts +26 -0
  4. package/dist/agents/agent.js +32 -0
  5. package/dist/agents/claude.d.ts +8 -0
  6. package/dist/agents/claude.js +35 -0
  7. package/dist/agents/codex.d.ts +8 -0
  8. package/dist/agents/codex.js +41 -0
  9. package/dist/agents/gemini.d.ts +8 -0
  10. package/dist/agents/gemini.js +39 -0
  11. package/dist/agents/openclaw.d.ts +8 -0
  12. package/dist/agents/openclaw.js +25 -0
  13. package/dist/agents/shared-prompt.d.ts +11 -0
  14. package/dist/agents/shared-prompt.js +26 -0
  15. package/dist/commands/agents.d.ts +2 -0
  16. package/dist/commands/agents.js +19 -0
  17. package/dist/commands/hook.js +32 -5
  18. package/dist/commands/info.d.ts +5 -0
  19. package/dist/commands/info.js +40 -0
  20. package/dist/commands/init.d.ts +7 -2
  21. package/dist/commands/init.js +125 -48
  22. package/dist/commands/mcpserver.d.ts +2 -0
  23. package/dist/commands/mcpserver.js +75 -0
  24. package/dist/commands/pair.d.ts +6 -0
  25. package/dist/commands/pair.js +166 -0
  26. package/dist/commands/plan-generation.md +32 -0
  27. package/dist/commands/run.js +262 -110
  28. package/dist/commands/serve.d.ts +1 -1
  29. package/dist/commands/serve.js +16 -189
  30. package/dist/commands/sessions.d.ts +4 -0
  31. package/dist/commands/sessions.js +30 -0
  32. package/dist/commands/task-generation.md +28 -21
  33. package/dist/config.d.ts +5 -5
  34. package/dist/config.js +24 -6
  35. package/dist/index.js +57 -11
  36. package/dist/nats-client.d.ts +3 -3
  37. package/dist/nats-client.js +2 -2
  38. package/dist/rpc-handler.d.ts +6 -0
  39. package/dist/rpc-handler.js +367 -0
  40. package/dist/session-store.d.ts +12 -0
  41. package/dist/session-store.js +57 -0
  42. package/dist/spawn-command.d.ts +26 -0
  43. package/dist/spawn-command.js +48 -0
  44. package/dist/systemd.d.ts +3 -7
  45. package/dist/systemd.js +54 -114
  46. package/dist/task.d.ts +45 -2
  47. package/dist/task.js +156 -13
  48. package/dist/transports/http-transport.d.ts +6 -0
  49. package/dist/transports/http-transport.js +157 -0
  50. package/dist/transports/nats-transport.d.ts +6 -0
  51. package/dist/transports/nats-transport.js +69 -0
  52. package/dist/types.d.ts +28 -30
  53. package/package.json +34 -35
  54. package/src/agents/agent.ts +62 -0
  55. package/src/agents/claude.ts +39 -0
  56. package/src/agents/codex.ts +46 -0
  57. package/src/agents/gemini.ts +43 -0
  58. package/src/agents/openclaw.ts +29 -0
  59. package/src/agents/shared-prompt.ts +26 -0
  60. package/src/commands/agents.ts +20 -0
  61. package/src/commands/info.ts +44 -0
  62. package/src/commands/init.ts +148 -60
  63. package/src/commands/mcpserver.ts +92 -0
  64. package/src/commands/pair.ts +195 -0
  65. package/src/commands/plan-generation.md +32 -0
  66. package/src/commands/run.ts +399 -197
  67. package/src/commands/serve.ts +18 -242
  68. package/src/commands/sessions.ts +32 -0
  69. package/src/config.ts +30 -10
  70. package/src/index.ts +65 -12
  71. package/src/nats-client.ts +15 -15
  72. package/src/rpc-handler.ts +421 -0
  73. package/src/session-store.ts +68 -0
  74. package/src/spawn-command.ts +78 -0
  75. package/src/systemd.ts +164 -232
  76. package/src/task.ts +168 -15
  77. package/src/transports/http-transport.ts +180 -0
  78. package/src/transports/nats-transport.ts +82 -0
  79. package/src/types.ts +64 -63
  80. package/src/commands/hook.ts +0 -240
  81. package/src/commands/task-generation.md +0 -21
package/CLAUDE.md ADDED
@@ -0,0 +1,9 @@
1
+ # CLAUDE.md
2
+
3
+ ## Getting Started
4
+
5
+ Always read `README.md` first before starting any task. For the full platform architecture spec (NATS protocol, data model, execution flow), see `spec.md` in the [palmier-server](../palmier-server) repo.
6
+
7
+ ## Documentation
8
+
9
+ When making architectural changes, update `README.md` and the server repo's `spec.md` to reflect the new state.
package/README.md CHANGED
@@ -1,12 +1,26 @@
1
- # Palmier Agent
1
+ # Palmier
2
2
 
3
- A Node.js CLI that runs on your machine as a persistent agent. It manages tasks, communicates with the Palmier platform via NATS, and executes Claude Code autonomously.
3
+ A Node.js CLI that runs on your machine as a persistent daemon. It manages tasks, communicates with the Palmier platform via NATS and/or direct HTTP, and executes CLI tools autonomously.
4
+
5
+ ## Connection Modes
6
+
7
+ The host supports three connection modes:
8
+
9
+ | Mode | Transport | Setup | Features |
10
+ |------|-----------|-------|----------|
11
+ | **`nats`** | NATS only | `palmier init --server <url>` → select nats | All features |
12
+ | **`auto`** | Both NATS + HTTP | `palmier init --server <url>` → select auto | All features. PWA auto-detects best route. |
13
+ | **`lan`** | HTTP only | `palmier init --lan` | No push notifications or email MCP. No server needed. |
14
+
15
+ In **auto** mode, the PWA connects directly to the host via HTTP when on the same LAN (lower latency), and falls back to NATS when the host is unreachable. Push notifications and email relay always flow through NATS/server, so no features are lost.
16
+
17
+ In **lan** mode, the host runs a standalone HTTP server. Multiple PWA clients can connect using session tokens. No Palmier web server or NATS broker is needed.
4
18
 
5
19
  ## Prerequisites
6
20
 
7
21
  - **Node.js 20+**
8
- - **Claude Code CLI** installed and authenticated
9
- - **Linux with systemd** (the agent installs as a systemd user service)
22
+ - An agent CLI tool for task execution (e.g., Claude Code, Gemini CLI, OpenAI Codex)
23
+ - **Linux with systemd** (the host installs as a systemd user service)
10
24
 
11
25
  ## Installation
12
26
 
@@ -18,75 +32,197 @@ npm install -g palmier
18
32
 
19
33
  | Command | Description |
20
34
  |---|---|
21
- | `palmier init --token <token>` | Provision the agent with a token from the dashboard |
22
- | `palmier serve` | Run the persistent NATS RPC handler (default command) |
35
+ | `palmier init --server <url>` | Provision the host via the server (prompts for nats/auto mode) |
36
+ | `palmier init --lan` | Provision standalone LAN host (no server needed) |
37
+ | `palmier pair` | Generate an OTP code to pair a new device |
38
+ | `palmier sessions list` | List active session tokens |
39
+ | `palmier sessions revoke <token>` | Revoke a specific session token |
40
+ | `palmier sessions revoke-all` | Revoke all session tokens |
41
+ | `palmier info` | Show host connection info (address, mode) |
42
+ | `palmier serve` | Run the persistent RPC handler (default command) |
23
43
  | `palmier run <task-id>` | Execute a specific task |
24
- | `palmier hook` | Handle Claude Code hook events (invoked by Claude Code, not manually) |
44
+ | `palmier mcpserver` | Start an MCP server exposing Palmier tools (stdio transport) |
45
+ | `palmier agents` | Re-detect installed agent CLIs and update config |
25
46
 
26
47
  ## Setup
27
48
 
28
- 1. Install the agent: `npm install -g palmier`
29
- 2. Register on the Palmier PWA and click **Add Agent**.
30
- 3. Copy the provisioning token.
31
- 4. Run `palmier init --token <token>` in your project directory.
49
+ ### Server-provisioned (nats or auto mode)
50
+
51
+ 1. Install the host: `npm install -g palmier`
52
+ 2. Run `palmier init --server https://your-server-url` in your project directory.
53
+ 3. Select connection mode: `auto` (recommended) or `nats`.
54
+ 4. The host registers with the server, saves config, installs a systemd service, and generates a pairing code.
55
+ 5. Enter the pairing code in the Palmier PWA to connect your device.
56
+
57
+ ### Standalone LAN mode
58
+
59
+ 1. Install the host: `npm install -g palmier`
60
+ 2. Run `palmier init --lan` in your project directory.
61
+ 3. A pairing code and address are displayed. Enter both in the PWA.
62
+
63
+ ### Pairing additional devices
64
+
65
+ Run `palmier pair` on the host to generate a new OTP code. Each paired device gets its own session token.
66
+
67
+ ### Managing sessions
68
+
69
+ ```bash
70
+ # List all paired devices
71
+ palmier sessions list
72
+
73
+ # Revoke a specific device's access
74
+ palmier sessions revoke <token>
75
+
76
+ # Revoke all sessions (unpair all devices)
77
+ palmier sessions revoke-all
78
+ ```
32
79
 
33
80
  The `init` command:
34
- - Saves agent configuration to `~/.config/palmier/agent.json`
35
- - Installs a systemd user service for the agent
36
- - Configures Claude Code hooks for remote interaction
81
+ - Detects installed agent CLIs (Claude Code, Gemini CLI, Codex CLI) and caches the result
82
+ - Saves host configuration to `~/.config/palmier/host.json`
83
+ - Installs a systemd user service for the host
84
+ - Auto-enters pair mode to connect your first device
85
+
86
+ To re-detect agents after installing or removing a CLI, run `palmier agents`.
37
87
 
38
88
  ### Verifying the Service
39
89
 
40
- After `palmier init`, verify the agent is running:
90
+ After `palmier init`, verify the host is running:
41
91
 
42
92
  ```bash
43
93
  # Check service status
44
- systemctl --user status palmier-agent.service
94
+ systemctl --user status palmier.service
45
95
 
46
96
  # View recent logs
47
- journalctl --user -u palmier-agent.service -n 50 --no-pager
97
+ journalctl --user -u palmier.service -n 50 --no-pager
48
98
 
49
99
  # Follow logs in real time
50
- journalctl --user -u palmier-agent.service -f
100
+ journalctl --user -u palmier.service -f
51
101
  ```
52
102
 
53
103
  You should see `Active: active (running)` and log output showing a NATS connection. If the service failed:
54
104
 
55
105
  ```bash
56
106
  # Restart manually
57
- systemctl --user restart palmier-agent.service
107
+ systemctl --user restart palmier.service
58
108
 
59
109
  # Check config is valid
60
- cat ~/.config/palmier/agent.json
110
+ cat ~/.config/palmier/host.json
61
111
  ```
62
112
 
63
113
  ## How It Works
64
114
 
65
- - The agent runs as a **systemd user service**, staying alive in the background.
66
- - The persistent process (`palmier serve`) is a NATS RPC handler. It derives the RPC method from the NATS subject (e.g., `...rpc.task.create` `task.create`) and treats the message body as request parameters.
67
- - **Task IDs** are generated by the agent as UUIDs.
115
+ - The host runs as a **systemd user service**, staying alive in the background.
116
+ - The persistent process (`palmier serve`) is an RPC handler. In NATS mode, it derives the method from the NATS subject; in LAN/auto mode, it exposes HTTP endpoints (`POST /rpc/<method>`). The RPC handler (`src/rpc-handler.ts`) is transport-agnostic.
117
+ - **Session tokens** every RPC request includes a `sessionToken` (in the NATS JSON payload or as an HTTP Bearer token). The host validates the token against `~/.config/palmier/sessions.json` before processing the request. If no sessions exist, validation is skipped.
118
+ - **Task IDs** are generated by the host as UUIDs.
68
119
  - All RPC responses (`task.list`, `task.create`, `task.update`) return **flat task objects** — frontmatter fields at the top level, not nested under a `frontmatter` key.
120
+ - **Plan generation is automatic** — `task.create` and `task.update` generate an execution plan and task name by invoking the configured agent CLI. For `task.update`, plans are regenerated only when `user_prompt` or `agent` changes, or when no plan body exists yet.
121
+ - **Run history** — each task run produces a time-stamped result file (`RESULT-<timestamp>.md`) and a task snapshot (`TASK-<timestamp>.md`) in the task directory. A project-level `history.jsonl` file indexes all runs with `{ task_id, result_file }` entries. The `activity.list` RPC reads this index with pagination (`offset`/`limit`, optional `task_id` filter) and enriches each entry with RESULT frontmatter. The `task.result` RPC requires a `result_file` parameter identifying which result to read.
122
+ - The `task.reports` RPC reads one or more report files (each must end with `.md`, plain filenames only) from a task's directory. If the RESULT frontmatter includes a `report_files` field, the PWA shows clickable links in the result dialog that fetch and display each report.
123
+ - Tasks have no separate name field — the `user_prompt` is the primary identifier and display label.
124
+ - **Triggers can be enabled/disabled** via the `triggers_enabled` frontmatter field (default `true`). When disabled, systemd timers are removed; when re-enabled, they are reinstalled. Tasks can still be run manually regardless.
69
125
  - Incoming tasks are stored as `TASK.md` files in a local `tasks/` directory.
70
- - Task execution spawns **Claude Code in a PTY**, giving the AI full CLI access within the project.
71
- - **Hooks** intercept Claude Code permission, confirmation, and input prompts, resolving them remotely via NATS KV so tasks can run unattended.
126
+ - Task execution is abstracted through an **`AgentTool` interface** (`src/agents/agent.ts`). Each task stores an `agent` field (e.g., `"claude"`) that selects which agent implementation constructs the full command line and arguments. The agent's `getTaskRunCommandLine(task)` method builds the appropriate flags (e.g., `--allowedTools` for Claude based on task permissions). The process is spawned without a shell, and stdin is closed to prevent tools from hanging on an open pipe. The spawned process inherits the default physical GUI session environment (`DISPLAY=:0`) so commands that launch graphical applications (e.g., headed browsers) run within the user's desktop session. Task lifecycle status (`start`, `finish`, `abort`, `fail`) is persisted to a `status.json` file in the task directory. Status changes are broadcast on a unified `host-event.<host_id>.<task_id>` subject via NATS pub/sub (when available) and/or pushed through SSE (in LAN/auto mode) by `run.ts` POSTing to the serve process's `/internal/event` endpoint. All events carry an `event_type` field (`"running-state"`, `"confirm-request"`, or `"confirm-resolved"`) with the same payload shape on both transports. Consumers (PWA, Web Server) subscribe to these notifications and fetch full status from the host via the `task.status` RPC.
127
+ - **Task confirmation** tasks with `requires_confirmation: true` set `pending_confirmation: true` in `status.json` before execution. A `confirm-request` event is published on `host-event` so the Web Server can send push notifications and connected PWA clients show a confirmation dialog. The user confirms or aborts via the PWA, which calls the `task.user_input` RPC on the host. The `serve` process sets `user_input` to the user's response (e.g., `"confirmed"` or `"aborted"`), and `run` watches the file for `user_input` to become defined, then updates `running_state` accordingly.
128
+ - **MCP server** (`palmier mcpserver`) — starts an MCP server over stdio that exposes platform tools to AI agents (e.g., Claude Code). In NATS/auto mode, connects to NATS on startup. Currently exposes a `send-email` tool that relays email requests to the Web Server via NATS (`host.<host_id>.email.send`).
72
129
 
73
130
  ## Project Structure
74
131
 
75
132
  ```
76
133
  src/
77
134
  index.ts # CLI entrypoint (commander setup)
78
- config.ts # Agent configuration (read/write ~/.config/palmier)
79
- nats-client.ts # NATS connection and messaging
135
+ config.ts # Host configuration (read/write ~/.config/palmier)
136
+ rpc-handler.ts # Transport-agnostic RPC handler (with session validation)
137
+ session-store.ts # Session token management (~/.config/palmier/sessions.json)
138
+ nats-client.ts # NATS connection helper
139
+ spawn-command.ts # Shared helper for spawning CLI tools without a shell
80
140
  systemd.ts # systemd service installation
81
141
  task.ts # Task file management
82
142
  types.ts # Shared type definitions
143
+ agents/
144
+ agent.ts # AgentTool interface, registry, and agent detection
145
+ claude.ts # Claude Code agent implementation
146
+ gemini.ts # Gemini CLI agent implementation
147
+ codex.ts # Codex CLI agent implementation
148
+ openclaw.ts # OpenClaw agent implementation
83
149
  commands/
84
- init.ts # Provisioning logic
85
- serve.ts # Persistent NATS RPC handler
150
+ init.ts # Provisioning logic (--server and --lan flows, auto-pair)
151
+ pair.ts # OTP code generation and pairing handler
152
+ sessions.ts # Session token management CLI (list, revoke, revoke-all)
153
+ info.ts # Print host connection info
154
+ agents.ts # Re-detect installed agent CLIs
155
+ serve.ts # Transport selection and startup
86
156
  run.ts # Single task execution
87
- hook.ts # Claude Code hook handler
157
+ mcpserver.ts # MCP server with platform tools (send-email)
158
+ transports/
159
+ nats-transport.ts # NATS subscription loop (host.<hostId>.rpc.>)
160
+ http-transport.ts # HTTP server with RPC, SSE, and internal event endpoints
88
161
  ```
89
162
 
90
- ## Related
163
+ ## MCP Server
164
+
165
+ The host includes an MCP server that exposes Palmier platform tools to AI agents like Claude Code.
166
+
167
+ ### Setup
168
+
169
+ Add to your Claude Code MCP settings:
170
+
171
+ ```json
172
+ {
173
+ "mcpServers": {
174
+ "palmier": {
175
+ "command": "palmier",
176
+ "args": ["mcpserver"]
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ Requires a provisioned host (`palmier init`). In NATS/auto mode, uses NATS relay. In LAN mode with `serverUrl` configured, uses HTTP relay. Not available in standalone LAN mode without a server.
183
+
184
+ ### Available Tools
185
+
186
+ | Tool | Inputs | Description |
187
+ |---|---|---|
188
+ | `send-email` | `to`, `subject`, `text` (required); `html`, `reply_to`, `cc`, `bcc` (optional) | Send an email via the platform's SMTP relay |
189
+
190
+ ## Removing a Host
191
+
192
+ To fully remove a host from a machine:
193
+
194
+ 1. **Unpair the host from the PWA** (via the host menu).
195
+
196
+ 2. **Stop and remove the systemd service:**
197
+
198
+ ```bash
199
+ systemctl --user stop palmier.service
200
+ systemctl --user disable palmier.service
201
+ rm ~/.config/systemd/user/palmier.service
202
+ ```
203
+
204
+ 3. **Remove any task timers and services:**
205
+
206
+ ```bash
207
+ systemctl --user stop palmier-task-*.timer palmier-task-*.service 2>/dev/null
208
+ systemctl --user disable palmier-task-*.timer 2>/dev/null
209
+ rm -f ~/.config/systemd/user/palmier-task-*.timer ~/.config/systemd/user/palmier-task-*.service
210
+ ```
211
+
212
+ 4. **Reload systemd:**
213
+
214
+ ```bash
215
+ systemctl --user daemon-reload
216
+ ```
217
+
218
+ 5. **Remove the host configuration:**
219
+
220
+ ```bash
221
+ rm -rf ~/.config/palmier
222
+ ```
223
+
224
+ 6. **Remove the tasks directory** from your project root:
91
225
 
92
- See the [palmier](../palmier) repo for the server, API, and PWA.
226
+ ```bash
227
+ rm -rf tasks/
228
+ ```
@@ -0,0 +1,26 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ export interface CommandLine {
3
+ command: string;
4
+ args: string[];
5
+ }
6
+ /**
7
+ * Interface that each agent tool must implement.
8
+ * Abstracts how plans are generated and tasks are executed across different AI agents.
9
+ */
10
+ export interface AgentTool {
11
+ /** Return the command and args used to generate a plan from a prompt. */
12
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
13
+ /** Return the command and args used to run a task. If prompt is provided, use it instead of the task's prompt.
14
+ * extraPermissions are transient permissions granted for this run only (not persisted in frontmatter). */
15
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
16
+ /** Detect whether the agent CLI is available and perform any agent-specific
17
+ * initialization. Returns true if the agent was detected and initialized successfully. */
18
+ init(): Promise<boolean>;
19
+ }
20
+ export interface DetectedAgent {
21
+ key: string;
22
+ label: string;
23
+ }
24
+ export declare function detectAgents(): Promise<DetectedAgent[]>;
25
+ export declare function getAgent(name: string): AgentTool;
26
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { ClaudeAgent } from "./claude.js";
2
+ import { GeminiAgent } from "./gemini.js";
3
+ import { CodexAgent } from "./codex.js";
4
+ const agentRegistry = {
5
+ claude: new ClaudeAgent(),
6
+ gemini: new GeminiAgent(),
7
+ codex: new CodexAgent(),
8
+ };
9
+ const agentLabels = {
10
+ claude: "Claude Code",
11
+ gemini: "Gemini CLI",
12
+ codex: "Codex CLI",
13
+ openclaw: "OpenClaw"
14
+ };
15
+ export async function detectAgents() {
16
+ const detected = [];
17
+ for (const [key, agent] of Object.entries(agentRegistry)) {
18
+ const label = agentLabels[key] ?? key;
19
+ const ok = await agent.init();
20
+ if (ok)
21
+ detected.push({ key, label });
22
+ }
23
+ return detected;
24
+ }
25
+ export function getAgent(name) {
26
+ const agent = agentRegistry[name];
27
+ if (!agent) {
28
+ throw new Error(`Unknown agent: "${name}". Available agents: ${Object.keys(agentRegistry).join(", ")}`);
29
+ }
30
+ return agent;
31
+ }
32
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class ClaudeAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class ClaudeAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ return {
6
+ command: "claude",
7
+ args: ["-p", prompt],
8
+ };
9
+ }
10
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
11
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
12
+ const args = ["-c", "--permission-mode", "acceptEdits", "-p", prompt];
13
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
14
+ for (const p of allPerms) {
15
+ args.push("--allowedTools", p.name);
16
+ }
17
+ return { command: "claude", args };
18
+ }
19
+ async init() {
20
+ try {
21
+ execSync("claude --version");
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ try {
27
+ execSync("claude mcp add --transport stdio palmier --scope user -- palmier mcpserver");
28
+ }
29
+ catch (err) {
30
+ console.warn("Warning: failed to install MCP for Claude:", err instanceof Error ? err.message : err);
31
+ }
32
+ return true;
33
+ }
34
+ }
35
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class CodexAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class CodexAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ // TODO: fill in
6
+ return {
7
+ command: "codex",
8
+ args: ["exec", "--skip-git-repo-check", prompt],
9
+ };
10
+ }
11
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
12
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
13
+ // TODO: Update sandbox to workspace-write once https://github.com/openai/codex/issues/12572
14
+ // is fixed.
15
+ const args = ["exec", "--full-auto", "--skip-git-repo-check", "--sandbox", "danger-full-access"];
16
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
17
+ for (const p of allPerms) {
18
+ args.push("--config");
19
+ args.push(`apps.${p.name}.default_tools_approval_mode="approve"`);
20
+ }
21
+ args.push(prompt);
22
+ args.push("resume", "--last");
23
+ return { command: "codex", args };
24
+ }
25
+ async init() {
26
+ try {
27
+ execSync("codex --version");
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ try {
33
+ execSync("codex mcp add palmier palmier mcpserver");
34
+ }
35
+ catch (err) {
36
+ console.warn("Warning: failed to install MCP for Codex:", err instanceof Error ? err.message : err);
37
+ }
38
+ return true;
39
+ }
40
+ }
41
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class GeminiAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class GeminiAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ // TODO: fill in
6
+ return {
7
+ command: "gemini",
8
+ args: ["--approval-mode", "auto_edit", "--prompt", prompt],
9
+ };
10
+ }
11
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
12
+ prompt = prompt ?? (task.body || task.frontmatter.user_prompt);
13
+ const args = ["--resume", "--prompt", prompt + TASK_OUTCOME_SUFFIX];
14
+ const allPerms = [...(task.frontmatter.permissions ?? []), ...(extraPermissions ?? [])];
15
+ if (allPerms.length > 0) {
16
+ args.push("--allowed-tools");
17
+ for (const p of allPerms) {
18
+ args.push(p.name);
19
+ }
20
+ }
21
+ return { command: "gemini", args };
22
+ }
23
+ async init() {
24
+ try {
25
+ execSync("gemini --version");
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ try {
31
+ execSync("gemini mcp add --scope user palmier palmier mcpserver");
32
+ }
33
+ catch (err) {
34
+ console.warn("Warning: failed to install MCP for Gemini:", err instanceof Error ? err.message : err);
35
+ }
36
+ return true;
37
+ }
38
+ }
39
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1,8 @@
1
+ import type { ParsedTask, RequiredPermission } from "../types.js";
2
+ import type { AgentTool, CommandLine } from "./agent.js";
3
+ export declare class ClaudeAgent implements AgentTool {
4
+ getPlanGenerationCommandLine(prompt: string): CommandLine;
5
+ getTaskRunCommandLine(task: ParsedTask, prompt?: string, extraPermissions?: RequiredPermission[]): CommandLine;
6
+ init(): Promise<boolean>;
7
+ }
8
+ //# sourceMappingURL=openclaw.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { execSync } from "child_process";
2
+ import { TASK_OUTCOME_SUFFIX } from "./shared-prompt.js";
3
+ export class ClaudeAgent {
4
+ getPlanGenerationCommandLine(prompt) {
5
+ return {
6
+ command: "openclaw",
7
+ args: ["agent", "--message", prompt],
8
+ };
9
+ }
10
+ getTaskRunCommandLine(task, prompt, extraPermissions) {
11
+ prompt = (prompt ?? (task.body || task.frontmatter.user_prompt)) + TASK_OUTCOME_SUFFIX;
12
+ const args = ["agent", "--message", prompt];
13
+ return { command: "openclaw", args };
14
+ }
15
+ async init() {
16
+ try {
17
+ execSync("openclaw --version");
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ return true;
23
+ }
24
+ }
25
+ //# sourceMappingURL=openclaw.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Appended to the end of every task prompt.
3
+ * Instructs the agent to output a clear success/failure marker
4
+ * so that palmier can determine the task outcome.
5
+ */
6
+ export declare const TASK_OUTCOME_SUFFIX = "\n\n---\nIf you generate report or output files, print each file name on its own line prefixed with [PALMIER_REPORT]: e.g.\n[PALMIER_REPORT] report.md\n[PALMIER_REPORT] summary.md\n\nWhen you are done, output exactly one of these markers as the very last line:\n- Success: [PALMIER_TASK_SUCCESS]\n- Failure: [PALMIER_TASK_FAILURE]\nDo not wrap them in code blocks or add text on the same line.\n\nIf the task fails because a tool was denied or you lack the required permissions, print each required permission on its own line prefixed with [PALMIER_PERMISSION]: e.g.\n[PALMIER_PERMISSION] Read | Read file contents from the repository\n[PALMIER_PERMISSION] Bash(npm test) | Run the test suite via npm\n[PALMIER_PERMISSION] Write | Write generated output files";
7
+ export declare const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
8
+ export declare const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
9
+ export declare const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
10
+ export declare const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
11
+ //# sourceMappingURL=shared-prompt.d.ts.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Appended to the end of every task prompt.
3
+ * Instructs the agent to output a clear success/failure marker
4
+ * so that palmier can determine the task outcome.
5
+ */
6
+ export const TASK_OUTCOME_SUFFIX = `
7
+
8
+ ---
9
+ If you generate report or output files, print each file name on its own line prefixed with [PALMIER_REPORT]: e.g.
10
+ [PALMIER_REPORT] report.md
11
+ [PALMIER_REPORT] summary.md
12
+
13
+ When you are done, output exactly one of these markers as the very last line:
14
+ - Success: [PALMIER_TASK_SUCCESS]
15
+ - Failure: [PALMIER_TASK_FAILURE]
16
+ Do not wrap them in code blocks or add text on the same line.
17
+
18
+ If the task fails because a tool was denied or you lack the required permissions, print each required permission on its own line prefixed with [PALMIER_PERMISSION]: e.g.
19
+ [PALMIER_PERMISSION] Read | Read file contents from the repository
20
+ [PALMIER_PERMISSION] Bash(npm test) | Run the test suite via npm
21
+ [PALMIER_PERMISSION] Write | Write generated output files`;
22
+ export const TASK_SUCCESS_MARKER = "[PALMIER_TASK_SUCCESS]";
23
+ export const TASK_FAILURE_MARKER = "[PALMIER_TASK_FAILURE]";
24
+ export const TASK_REPORT_PREFIX = "[PALMIER_REPORT]";
25
+ export const TASK_PERMISSION_PREFIX = "[PALMIER_PERMISSION]";
26
+ //# sourceMappingURL=shared-prompt.js.map
@@ -0,0 +1,2 @@
1
+ export declare function agentsCommand(): Promise<void>;
2
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { loadConfig, saveConfig } from "../config.js";
2
+ import { detectAgents } from "../agents/agent.js";
3
+ export async function agentsCommand() {
4
+ const config = loadConfig();
5
+ console.log("Detecting installed agents...");
6
+ const agents = await detectAgents();
7
+ config.agents = agents;
8
+ saveConfig(config);
9
+ if (agents.length === 0) {
10
+ console.log("No agent CLIs detected.");
11
+ }
12
+ else {
13
+ console.log("Detected agents:");
14
+ for (const a of agents) {
15
+ console.log(` ${a.key} — ${a.label}`);
16
+ }
17
+ }
18
+ }
19
+ //# sourceMappingURL=agents.js.map