palmier 0.1.9 → 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.
@@ -1,15 +1,20 @@
1
1
  import { randomUUID } from "crypto";
2
- import { execSync } from "child_process";
2
+ import { execSync, exec } from "child_process";
3
+ import { promisify } from "util";
4
+ const execAsync = promisify(exec);
3
5
  import * as fs from "fs";
4
6
  import * as path from "path";
5
7
  import { fileURLToPath } from "url";
6
8
  import { StringCodec } from "nats";
7
9
  import { loadConfig } from "../config.js";
8
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ function shellEscape(arg) {
12
+ return "'" + arg.replace(/'/g, "'\\''") + "'";
13
+ }
9
14
  const TASK_GENERATION_PROMPT = fs.readFileSync(path.join(__dirname, "task-generation.md"), "utf-8");
10
15
  import { connectNats } from "../nats-client.js";
11
16
  import { listTasks, parseTaskFile, writeTaskFile, getTaskDir } from "../task.js";
12
- import { installTaskTimer, removeTaskTimer, getTaskStatus } from "../systemd.js";
17
+ import { installTaskTimer, removeTaskTimer } from "../systemd.js";
13
18
  /**
14
19
  * Start the persistent NATS RPC handler.
15
20
  */
@@ -29,10 +34,10 @@ export async function serveCommand() {
29
34
  };
30
35
  process.on("SIGINT", shutdown);
31
36
  process.on("SIGTERM", shutdown);
32
- // On startup, clean up orphaned pending-hooks keys for this agent
37
+ // On startup, clean up orphaned pending-confirmation keys for this agent
33
38
  try {
34
39
  const js = nc.jetstream();
35
- const kv = await js.views.kv("pending-hooks");
40
+ const kv = await js.views.kv("pending-confirmation");
36
41
  const keys = await kv.keys();
37
42
  for await (const key of keys) {
38
43
  if (key.startsWith(`${config.agentId}.`)) {
@@ -42,7 +47,7 @@ export async function serveCommand() {
42
47
  }
43
48
  }
44
49
  catch (err) {
45
- console.error(`Warning: could not clean up pending-hooks KV: ${err}`);
50
+ console.error(`Warning: could not clean up pending-confirmation KV: ${err}`);
46
51
  }
47
52
  console.log("Agent serving. Waiting for RPC messages...");
48
53
  for await (const msg of sub) {
@@ -76,19 +81,20 @@ export async function serveCommand() {
76
81
  console.error(`RPC error (${method}):`, err);
77
82
  response = { error: String(err) };
78
83
  }
84
+ console.log(`RPC done: ${method}`, JSON.stringify(response).slice(0, 200));
79
85
  if (msg.reply) {
80
86
  msg.respond(sc.encode(JSON.stringify(response)));
81
87
  }
82
88
  }
83
- function flattenTask(task, status) {
84
- return { ...task.frontmatter, body: task.body, ...(status != null ? { status } : {}) };
89
+ function flattenTask(task) {
90
+ return { ...task.frontmatter, body: task.body };
85
91
  }
86
92
  async function handleRpc(request) {
87
93
  switch (request.method) {
88
94
  case "task.list": {
89
95
  const tasks = listTasks(config.projectRoot);
90
96
  return {
91
- tasks: tasks.map((task) => flattenTask(task, getTaskStatus(task.frontmatter.id))),
97
+ tasks: tasks.map((task) => flattenTask(task)),
92
98
  };
93
99
  }
94
100
  case "task.create": {
@@ -98,17 +104,18 @@ export async function serveCommand() {
98
104
  const task = {
99
105
  frontmatter: {
100
106
  id,
101
- name: params.name,
102
107
  user_prompt: params.user_prompt,
108
+ command_line: params.command_line ?? "claude -p --dangerously-skip-permissions",
103
109
  triggers: params.triggers ?? [],
110
+ triggers_enabled: params.triggers_enabled ?? true,
104
111
  requires_confirmation: params.requires_confirmation ?? true,
105
- suppress_permissions: params.suppress_permissions ?? false,
106
- enabled: params.enabled ?? true,
107
112
  },
108
113
  body: params.body || "",
109
114
  };
110
115
  writeTaskFile(taskDir, task);
111
- installTaskTimer(config, task);
116
+ if (task.frontmatter.triggers_enabled) {
117
+ installTaskTimer(config, task);
118
+ }
112
119
  return flattenTask(task);
113
120
  }
114
121
  case "task.update": {
@@ -116,25 +123,25 @@ export async function serveCommand() {
116
123
  const taskDir = getTaskDir(config.projectRoot, params.id);
117
124
  const existing = parseTaskFile(taskDir);
118
125
  // Merge updates
119
- if (params.name !== undefined)
120
- existing.frontmatter.name = params.name;
121
126
  if (params.user_prompt !== undefined)
122
127
  existing.frontmatter.user_prompt = params.user_prompt;
128
+ if (params.command_line !== undefined)
129
+ existing.frontmatter.command_line = params.command_line;
123
130
  if (params.triggers !== undefined)
124
131
  existing.frontmatter.triggers = params.triggers;
132
+ if (params.triggers_enabled !== undefined)
133
+ existing.frontmatter.triggers_enabled = params.triggers_enabled;
125
134
  if (params.requires_confirmation !== undefined)
126
135
  existing.frontmatter.requires_confirmation = params.requires_confirmation;
127
- if (params.suppress_permissions !== undefined)
128
- existing.frontmatter.suppress_permissions = params.suppress_permissions;
129
- if (params.enabled !== undefined)
130
- existing.frontmatter.enabled = params.enabled;
131
136
  if (params.body !== undefined)
132
137
  existing.body = params.body;
133
138
  writeTaskFile(taskDir, existing);
134
- // Reinstall timer with updated config
139
+ // Reinstall or remove timers based on triggers_enabled
135
140
  removeTaskTimer(params.id);
136
- installTaskTimer(config, existing);
137
- return flattenTask(existing, getTaskStatus(params.id));
141
+ if (existing.frontmatter.triggers_enabled) {
142
+ installTaskTimer(config, existing);
143
+ }
144
+ return flattenTask(existing);
138
145
  }
139
146
  case "task.delete": {
140
147
  const params = request.params;
@@ -148,10 +155,10 @@ export async function serveCommand() {
148
155
  }
149
156
  case "task.generate": {
150
157
  const params = request.params;
158
+ const commandLine = params.command_line || "claude -p --dangerously-skip-permissions";
151
159
  const fullPrompt = TASK_GENERATION_PROMPT + params.prompt;
152
160
  try {
153
- const output = execSync("claude -p -", {
154
- input: fullPrompt,
161
+ const output = execSync(`${commandLine} ${shellEscape(fullPrompt)}`, {
155
162
  encoding: "utf-8",
156
163
  cwd: config.projectRoot,
157
164
  timeout: 120_000,
@@ -160,24 +167,34 @@ export async function serveCommand() {
160
167
  }
161
168
  catch (err) {
162
169
  const error = err;
163
- return { error: "claude command failed", stdout: error.stdout, stderr: error.stderr };
170
+ return { error: "generation command failed", stdout: error.stdout, stderr: error.stderr };
164
171
  }
165
172
  }
166
173
  case "task.run": {
167
174
  const params = request.params;
168
175
  const serviceName = `palmier-task-${params.id}.service`;
169
176
  try {
170
- execSync(`systemctl --user start ${serviceName}`, { stdio: "inherit" });
177
+ await execAsync(`systemctl --user start --no-block ${serviceName}`);
171
178
  return { ok: true, task_id: params.id };
172
179
  }
173
180
  catch (err) {
174
- return { error: `Failed to start task service: ${err}` };
181
+ const e = err;
182
+ console.error(`task.run failed for ${params.id}: ${e.stderr || e.message}`);
183
+ return { error: `Failed to start task: ${e.stderr || e.message}` };
175
184
  }
176
185
  }
177
- case "task.status": {
186
+ case "task.abort": {
178
187
  const params = request.params;
179
- const status = getTaskStatus(params.id);
180
- return { task_id: params.id, status };
188
+ const serviceName = `palmier-task-${params.id}.service`;
189
+ try {
190
+ await execAsync(`systemctl --user stop ${serviceName}`);
191
+ return { ok: true, task_id: params.id };
192
+ }
193
+ catch (err) {
194
+ const e = err;
195
+ console.error(`task.abort failed for ${params.id}: ${e.stderr || e.message}`);
196
+ return { error: `Failed to abort task: ${e.stderr || e.message}` };
197
+ }
181
198
  }
182
199
  case "task.logs": {
183
200
  const params = request.params;
@@ -191,6 +208,28 @@ export async function serveCommand() {
191
208
  return { task_id: params.id, logs: error.stdout || "", error: error.stderr };
192
209
  }
193
210
  }
211
+ case "task.result": {
212
+ const params = request.params;
213
+ const resultPath = path.join(config.projectRoot, "tasks", params.id, "RESULT.md");
214
+ try {
215
+ const raw = fs.readFileSync(resultPath, "utf-8");
216
+ // Parse optional frontmatter
217
+ const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
218
+ if (fmMatch) {
219
+ const meta = {};
220
+ for (const line of fmMatch[1].split("\n")) {
221
+ const [key, val] = line.split(": ");
222
+ if (key && val)
223
+ meta[key.trim()] = Number(val.trim());
224
+ }
225
+ return { task_id: params.id, content: fmMatch[2], start_time: meta.start_time, end_time: meta.end_time };
226
+ }
227
+ return { task_id: params.id, content: raw };
228
+ }
229
+ catch {
230
+ return { task_id: params.id, error: "No result file found" };
231
+ }
232
+ }
194
233
  default:
195
234
  return { error: `Unknown method: ${request.method}` };
196
235
  }
@@ -1,21 +1,28 @@
1
- You are a specification writer for a personal computer AI agent. Given a task description, produce a detailed Markdown specification document that the agent can later follow to execute the task. **Do not execute any part of the task yourself.**
2
-
3
- The specification must include the following sections:
4
-
5
- ### 1. Summary
6
- What the task accomplishes
7
-
8
- ### 2. Steps
9
- A numbered sequence of concrete, unambiguous actions.
10
- Use sub-steps for complex actions. Include conditional branches where behavior may vary (e.g., "If file exists, do A; otherwise, do B").
11
-
12
- ### 3. Edge Cases & Risks
13
- Anything that could go wrong and how to handle it:
14
- - Common failure modes
15
- - Platform-specific differences
16
- - Race conditions or timing issues
17
- - Data loss risks and mitigation
18
-
19
- Format the entire document as Markdown with proper headings, code blocks for commands, and tables where appropriate.
20
-
21
- **Task description:**
1
+ You are a planning agent for a personal computer AI agent. Given a task description, produce a detailed Markdown execution plan that the agent can later follow step by step. **Do not execute any part of the task yourself.**
2
+
3
+ The plan must include the following sections:
4
+
5
+ ### 1. Goal
6
+ What the task accomplishes and the expected end state.
7
+
8
+ ### 2. Prerequisites
9
+ What must be true before starting:
10
+ - Required software and versions
11
+ - Files or data that must be present
12
+ - Permissions or access needed
13
+ - Environment state (e.g., running services, network access)
14
+
15
+ ### 3. Plan
16
+ A numbered sequence of concrete, actionable steps to complete the task.
17
+ Use sub-steps for complex actions. Include conditional branches where behavior may vary (e.g., "If file exists, do A; otherwise, do B"). Each step should be specific enough that the agent can execute it without ambiguity.
18
+
19
+ ### 4. Edge Cases & Risks
20
+ Anything that could go wrong and how to handle it:
21
+ - Common failure modes
22
+ - Platform-specific differences
23
+ - Race conditions or timing issues
24
+ - Data loss risks and mitigation
25
+
26
+ Format the entire document as Markdown with proper headings, code blocks for commands, and tables where appropriate.
27
+
28
+ **Task description:**
package/dist/index.js CHANGED
@@ -6,7 +6,6 @@ import { dirname, join } from "path";
6
6
  import { Command } from "commander";
7
7
  import { initCommand } from "./commands/init.js";
8
8
  import { runCommand } from "./commands/run.js";
9
- import { hookCommand } from "./commands/hook.js";
10
9
  import { serveCommand } from "./commands/serve.js";
11
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
11
  const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
@@ -28,12 +27,6 @@ program
28
27
  .action(async (taskId) => {
29
28
  await runCommand(taskId);
30
29
  });
31
- program
32
- .command("hook")
33
- .description("Handle a Claude Code hook event (reads from stdin)")
34
- .action(async () => {
35
- await hookCommand();
36
- });
37
30
  program
38
31
  .command("serve", { isDefault: true })
39
32
  .description("Start the persistent NATS RPC handler")
package/dist/systemd.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AgentConfig } from "./types.js";
2
- import type { ParsedTask, TaskStatus } from "./types.js";
2
+ import type { ParsedTask } from "./types.js";
3
3
  /**
4
4
  * Convert a cron expression (5-field) to a systemd OnCalendar string.
5
5
  * Handles basic cron patterns: minute hour day-of-month month day-of-week
@@ -13,10 +13,6 @@ export declare function installTaskTimer(config: AgentConfig, task: ParsedTask):
13
13
  * Remove a task's systemd timer and service files.
14
14
  */
15
15
  export declare function removeTaskTimer(taskId: string): void;
16
- /**
17
- * Query systemd for task status information.
18
- */
19
- export declare function getTaskStatus(taskId: string): TaskStatus;
20
16
  /**
21
17
  * Run systemctl --user daemon-reload.
22
18
  */
package/dist/systemd.js CHANGED
@@ -57,46 +57,49 @@ export function installTaskTimer(config, task) {
57
57
  // Determine the palmier binary path
58
58
  const palmierBin = process.argv[1] || "palmier";
59
59
  // Generate service unit
60
- const serviceContent = `[Unit]
61
- Description=Palmier Task: ${task.frontmatter.name || taskId}
62
-
63
- [Service]
64
- Type=oneshot
65
- ExecStart=${palmierBin} run ${taskId}
66
- WorkingDirectory=${config.projectRoot}
67
- Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
60
+ const serviceContent = `[Unit]
61
+ Description=Palmier Task: ${taskId}
62
+
63
+ [Service]
64
+ Type=oneshot
65
+ ExecStart=${palmierBin} run ${taskId}
66
+ WorkingDirectory=${config.projectRoot}
67
+ Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
68
68
  `;
69
- // Generate timer unit with OnCalendar entries for each cron trigger
69
+ // Write service unit (always needed for on-demand runs)
70
+ fs.writeFileSync(path.join(UNIT_DIR, serviceName), serviceContent, "utf-8");
71
+ daemonReload();
72
+ // Only create and enable a timer if there are actual triggers
73
+ const triggers = task.frontmatter.triggers || [];
70
74
  const onCalendarLines = [];
71
- for (const trigger of task.frontmatter.triggers || []) {
75
+ for (const trigger of triggers) {
72
76
  if (trigger.type === "cron") {
73
77
  onCalendarLines.push(`OnCalendar=${cronToOnCalendar(trigger.value)}`);
74
78
  }
75
79
  else if (trigger.type === "once") {
76
- // "once" triggers use OnActiveSec or a specific timestamp
77
80
  onCalendarLines.push(`OnActiveSec=${trigger.value}`);
78
81
  }
79
82
  }
80
- const timerContent = `[Unit]
81
- Description=Timer for Palmier Task: ${task.frontmatter.name || taskId}
82
-
83
- [Timer]
84
- ${onCalendarLines.join("\n")}
85
- Persistent=true
86
-
87
- [Install]
88
- WantedBy=timers.target
83
+ if (onCalendarLines.length > 0) {
84
+ const timerContent = `[Unit]
85
+ Description=Timer for Palmier Task: ${taskId}
86
+
87
+ [Timer]
88
+ ${onCalendarLines.join("\n")}
89
+ Persistent=true
90
+
91
+ [Install]
92
+ WantedBy=timers.target
89
93
  `;
90
- // Write unit files
91
- fs.writeFileSync(path.join(UNIT_DIR, serviceName), serviceContent, "utf-8");
92
- fs.writeFileSync(path.join(UNIT_DIR, timerName), timerContent, "utf-8");
93
- // Reload and enable
94
- daemonReload();
95
- if (task.frontmatter.enabled) {
96
- execSync(`systemctl --user enable --now ${timerName}`, { stdio: "inherit" });
97
- }
98
- else {
99
- execSync(`systemctl --user enable ${timerName}`, { stdio: "inherit" });
94
+ fs.writeFileSync(path.join(UNIT_DIR, timerName), timerContent, "utf-8");
95
+ daemonReload();
96
+ try {
97
+ execSync(`systemctl --user enable --now ${timerName}`, { encoding: "utf-8" });
98
+ }
99
+ catch (err) {
100
+ const e = err;
101
+ console.error(`Failed to enable timer ${timerName}: ${e.stderr || err}`);
102
+ }
100
103
  }
101
104
  }
102
105
  /**
@@ -105,101 +108,38 @@ WantedBy=timers.target
105
108
  export function removeTaskTimer(taskId) {
106
109
  const timerName = getTimerName(taskId);
107
110
  const serviceName = getServiceName(taskId);
108
- // Stop and disable
109
- try {
110
- execSync(`systemctl --user stop ${timerName}`, { stdio: "inherit" });
111
- }
112
- catch {
113
- // Timer might not be running
114
- }
115
- try {
116
- execSync(`systemctl --user disable ${timerName}`, { stdio: "inherit" });
117
- }
118
- catch {
119
- // Timer might not be enabled
120
- }
121
- // Remove unit files
122
111
  const timerPath = path.join(UNIT_DIR, timerName);
123
112
  const servicePath = path.join(UNIT_DIR, serviceName);
124
- if (fs.existsSync(timerPath))
125
- fs.unlinkSync(timerPath);
126
- if (fs.existsSync(servicePath))
127
- fs.unlinkSync(servicePath);
128
- daemonReload();
129
- }
130
- /**
131
- * Query systemd for task status information.
132
- */
133
- export function getTaskStatus(taskId) {
134
- const timerName = getTimerName(taskId);
135
- const serviceName = getServiceName(taskId);
136
- let state = "inactive";
137
- let lastRun;
138
- let lastResult;
139
- let nextRun;
140
- // Check if timer is active
141
- try {
142
- const activeState = execSync(`systemctl --user is-active ${timerName}`, {
143
- encoding: "utf-8",
144
- }).trim();
145
- if (activeState === "active") {
146
- state = "active";
113
+ // Only stop/disable the timer if the file exists
114
+ if (fs.existsSync(timerPath)) {
115
+ try {
116
+ execSync(`systemctl --user stop ${timerName}`, { encoding: "utf-8" });
147
117
  }
148
- }
149
- catch {
150
- // Not active
151
- }
152
- // Check if service has failed
153
- try {
154
- const serviceState = execSync(`systemctl --user is-failed ${serviceName}`, {
155
- encoding: "utf-8",
156
- }).trim();
157
- if (serviceState === "failed") {
158
- state = "failed";
118
+ catch {
119
+ // Timer might not be running
159
120
  }
160
- }
161
- catch {
162
- // Not failed
163
- }
164
- // Get last run time and result
165
- try {
166
- const props = execSync(`systemctl --user show ${serviceName} --property=ExecMainStartTimestamp,ExecMainStatus`, { encoding: "utf-8" });
167
- for (const line of props.split("\n")) {
168
- if (line.startsWith("ExecMainStartTimestamp=") && line.trim() !== "ExecMainStartTimestamp=") {
169
- const val = line.split("=", 2)[1].trim();
170
- if (val)
171
- lastRun = val;
172
- }
173
- if (line.startsWith("ExecMainStatus=")) {
174
- const val = parseInt(line.split("=", 2)[1].trim(), 10);
175
- if (!isNaN(val))
176
- lastResult = val;
177
- }
121
+ try {
122
+ execSync(`systemctl --user disable ${timerName}`, { encoding: "utf-8" });
178
123
  }
179
- }
180
- catch {
181
- // Couldn't get properties
182
- }
183
- // Get next run time from timer
184
- try {
185
- const timerProps = execSync(`systemctl --user show ${timerName} --property=NextElapseUSecRealtime`, { encoding: "utf-8" });
186
- for (const line of timerProps.split("\n")) {
187
- if (line.startsWith("NextElapseUSecRealtime=")) {
188
- const val = line.split("=", 2)[1].trim();
189
- if (val)
190
- nextRun = val;
191
- }
124
+ catch {
125
+ // Timer might not be enabled
192
126
  }
127
+ fs.unlinkSync(timerPath);
193
128
  }
194
- catch {
195
- // Couldn't get next run
196
- }
197
- return { state, lastRun, lastResult, nextRun };
129
+ if (fs.existsSync(servicePath))
130
+ fs.unlinkSync(servicePath);
131
+ daemonReload();
198
132
  }
199
133
  /**
200
134
  * Run systemctl --user daemon-reload.
201
135
  */
202
136
  export function daemonReload() {
203
- execSync("systemctl --user daemon-reload", { stdio: "inherit" });
137
+ try {
138
+ execSync("systemctl --user daemon-reload", { encoding: "utf-8" });
139
+ }
140
+ catch (err) {
141
+ const e = err;
142
+ console.error(`daemon-reload failed: ${e.stderr || err}`);
143
+ }
204
144
  }
205
145
  //# sourceMappingURL=systemd.js.map
package/dist/task.js CHANGED
@@ -26,6 +26,8 @@ function parseTaskContent(content) {
26
26
  if (!frontmatter.id) {
27
27
  throw new Error("TASK.md frontmatter must include at least: id");
28
28
  }
29
+ frontmatter.command_line ??= "claude -p --dangerously-skip-permissions";
30
+ frontmatter.triggers_enabled ??= true;
29
31
  return { frontmatter, body };
30
32
  }
31
33
  /**
package/dist/types.d.ts CHANGED
@@ -8,12 +8,11 @@ export interface AgentConfig {
8
8
  }
9
9
  export interface TaskFrontmatter {
10
10
  id: string;
11
- name: string;
12
11
  user_prompt: string;
12
+ command_line: string;
13
13
  triggers: Trigger[];
14
+ triggers_enabled: boolean;
14
15
  requires_confirmation: boolean;
15
- suppress_permissions: boolean;
16
- enabled: boolean;
17
16
  }
18
17
  export interface Trigger {
19
18
  type: "cron" | "once";
@@ -23,31 +22,13 @@ export interface ParsedTask {
23
22
  frontmatter: TaskFrontmatter;
24
23
  body: string;
25
24
  }
26
- export interface TaskStatus {
27
- state: "active" | "inactive" | "failed";
28
- lastRun?: string;
29
- lastResult?: number;
30
- nextRun?: string;
31
- }
32
- export interface TaskWithStatus extends ParsedTask {
33
- status: TaskStatus;
34
- }
35
- export interface HookPayload {
36
- type: "confirm" | "permission" | "input";
25
+ export interface ConfirmPayload {
26
+ type: "confirm";
37
27
  task_id: string;
38
- hook_id: string;
39
28
  agent_id: string;
40
29
  user_id: string;
41
30
  details: Record<string, unknown>;
42
- status: string;
43
- }
44
- export interface ClaudeHookEvent {
45
- hook_name: string;
46
- session_id: string;
47
- tool_name?: string;
48
- tool_input?: Record<string, unknown>;
49
- message?: string;
50
- [key: string]: unknown;
31
+ status: "pending" | "confirmed" | "aborted";
51
32
  }
52
33
  export interface RpcMessage {
53
34
  method: string;
package/package.json CHANGED
@@ -1,35 +1,33 @@
1
- {
2
- "name": "palmier",
3
- "version": "0.1.9",
4
- "description": "Palmier agent CLI - provisions, executes tasks, and serves NATS RPC",
5
- "license": "ISC",
6
- "author": "Hongxu Cai",
7
- "type": "module",
8
- "main": "dist/index.js",
9
- "types": "./dist/index.d.ts",
10
- "bin": {
11
- "palmier": "dist/index.js"
12
- },
13
- "scripts": {
14
- "dev": "tsx src/index.ts",
15
- "build": "tsc && node -e \"require('fs').cpSync('src/commands/task-generation.md','dist/commands/task-generation.md')\"",
16
- "start": "node dist/index.js"
17
- },
18
- "dependencies": {
19
- "commander": "^13.1.0",
20
- "dotenv": "^16.4.7",
21
- "nats": "^2.29.1",
22
- "node-pty": "^1.0.0",
23
- "uuid": "^11.1.0",
24
- "yaml": "^2.7.0"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^22.13.0",
28
- "@types/uuid": "^10.0.0",
29
- "tsx": "^4.19.0",
30
- "typescript": "^5.7.0"
31
- },
32
- "engines": {
33
- "node": ">=20.0.0"
34
- }
35
- }
1
+ {
2
+ "name": "palmier",
3
+ "version": "0.2.0",
4
+ "description": "Palmier agent CLI - provisions, executes tasks, and serves NATS RPC",
5
+ "license": "ISC",
6
+ "author": "Hongxu Cai",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "bin": {
11
+ "palmier": "dist/index.js"
12
+ },
13
+ "scripts": {
14
+ "dev": "tsx src/index.ts",
15
+ "build": "tsc && node -e \"require('fs').cpSync('src/commands/task-generation.md','dist/commands/task-generation.md')\"",
16
+ "prepare": "npm run build",
17
+ "start": "node dist/index.js"
18
+ },
19
+ "dependencies": {
20
+ "commander": "^13.1.0",
21
+ "dotenv": "^16.4.7",
22
+ "nats": "^2.29.1",
23
+ "yaml": "^2.7.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.13.0",
27
+ "tsx": "^4.19.0",
28
+ "typescript": "^5.7.0"
29
+ },
30
+ "engines": {
31
+ "node": ">=20.0.0"
32
+ }
33
+ }