palmier 0.6.8 → 0.6.9

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 (79) hide show
  1. package/dist/agents/agent.d.ts +2 -2
  2. package/dist/agents/aider.d.ts +1 -1
  3. package/dist/agents/aider.js +2 -5
  4. package/dist/agents/claude.d.ts +1 -1
  5. package/dist/agents/claude.js +2 -5
  6. package/dist/agents/cline.d.ts +1 -1
  7. package/dist/agents/cline.js +2 -5
  8. package/dist/agents/codex.d.ts +1 -1
  9. package/dist/agents/codex.js +2 -5
  10. package/dist/agents/copilot.d.ts +1 -1
  11. package/dist/agents/copilot.js +2 -5
  12. package/dist/agents/cursor.d.ts +1 -1
  13. package/dist/agents/cursor.js +2 -5
  14. package/dist/agents/deepagents.d.ts +1 -1
  15. package/dist/agents/deepagents.js +2 -5
  16. package/dist/agents/droid.d.ts +1 -1
  17. package/dist/agents/droid.js +2 -5
  18. package/dist/agents/gemini.d.ts +1 -1
  19. package/dist/agents/gemini.js +2 -5
  20. package/dist/agents/goose.d.ts +1 -1
  21. package/dist/agents/goose.js +2 -5
  22. package/dist/agents/hermes.d.ts +1 -1
  23. package/dist/agents/hermes.js +2 -5
  24. package/dist/agents/kimi.d.ts +1 -1
  25. package/dist/agents/kimi.js +2 -5
  26. package/dist/agents/kiro.d.ts +1 -1
  27. package/dist/agents/kiro.js +2 -5
  28. package/dist/agents/openclaw.d.ts +1 -1
  29. package/dist/agents/openclaw.js +2 -5
  30. package/dist/agents/opencode.d.ts +1 -1
  31. package/dist/agents/opencode.js +2 -5
  32. package/dist/agents/qoder.d.ts +1 -1
  33. package/dist/agents/qoder.js +2 -5
  34. package/dist/agents/qwen.d.ts +1 -1
  35. package/dist/agents/qwen.js +2 -5
  36. package/dist/agents/shared-prompt.js +1 -1
  37. package/dist/commands/run.js +1 -2
  38. package/dist/mcp-tools.d.ts +1 -1
  39. package/dist/mcp-tools.js +2 -2
  40. package/dist/pwa/assets/{index-C8vJwUNi.js → index-CZejk2al.js} +1 -1
  41. package/dist/pwa/assets/{web-NxTETXZK.js → web-C48txJFl.js} +1 -1
  42. package/dist/pwa/assets/{web-6UChJFov.js → web-zj8Blync.js} +1 -1
  43. package/dist/pwa/index.html +1 -1
  44. package/dist/pwa/service-worker.js +1 -1
  45. package/dist/rpc-handler.js +27 -67
  46. package/dist/task.js +2 -3
  47. package/dist/types.d.ts +0 -1
  48. package/package.json +2 -2
  49. package/palmier-server/pwa/src/constants.ts +1 -1
  50. package/src/agents/agent.ts +2 -2
  51. package/src/agents/aider.ts +2 -5
  52. package/src/agents/claude.ts +2 -5
  53. package/src/agents/cline.ts +2 -5
  54. package/src/agents/codex.ts +2 -5
  55. package/src/agents/copilot.ts +2 -5
  56. package/src/agents/cursor.ts +2 -5
  57. package/src/agents/deepagents.ts +2 -5
  58. package/src/agents/droid.ts +2 -5
  59. package/src/agents/gemini.ts +2 -5
  60. package/src/agents/goose.ts +2 -5
  61. package/src/agents/hermes.ts +2 -5
  62. package/src/agents/kimi.ts +2 -5
  63. package/src/agents/kiro.ts +2 -5
  64. package/src/agents/openclaw.ts +2 -5
  65. package/src/agents/opencode.ts +2 -5
  66. package/src/agents/qoder.ts +2 -5
  67. package/src/agents/qwen.ts +2 -5
  68. package/src/agents/shared-prompt.ts +1 -1
  69. package/src/commands/run.ts +1 -2
  70. package/src/mcp-tools.ts +2 -2
  71. package/src/rpc-handler.ts +29 -71
  72. package/src/task.ts +2 -3
  73. package/src/types.ts +0 -1
  74. package/test/agent-instructions.test.ts +84 -19
  75. package/test/agent-output-parsing.test.ts +1 -0
  76. package/test/task-parsing.test.ts +3 -3
  77. package/dist/commands/plan-generation.md +0 -22
  78. package/src/commands/plan-generation.md +0 -22
  79. package/test/fixtures/agent-instructions-snapshot.md +0 -58
package/dist/task.js CHANGED
@@ -22,14 +22,13 @@ export function parseTaskContent(content) {
22
22
  throw new Error("TASK.md is missing valid YAML frontmatter delimiters (---)");
23
23
  }
24
24
  const frontmatter = parseYaml(match[1]);
25
- const body = (match[2] || "").trim();
26
25
  if (!frontmatter.id) {
27
26
  throw new Error("TASK.md frontmatter must include at least: id");
28
27
  }
29
28
  frontmatter.name ??= frontmatter.user_prompt?.slice(0, 60) ?? "";
30
29
  frontmatter.agent ??= "claude";
31
30
  frontmatter.triggers_enabled ??= true;
32
- return { frontmatter, body };
31
+ return { frontmatter };
33
32
  }
34
33
  /**
35
34
  * Write a TASK.md file to the given task directory.
@@ -38,7 +37,7 @@ export function parseTaskContent(content) {
38
37
  export function writeTaskFile(taskDir, task) {
39
38
  fs.mkdirSync(taskDir, { recursive: true });
40
39
  const yamlStr = stringifyYaml(task.frontmatter).trim();
41
- const content = `---\n${yamlStr}\n---\n${task.body}\n`;
40
+ const content = `---\n${yamlStr}\n---\n`;
42
41
  const filePath = path.join(taskDir, "TASK.md");
43
42
  fs.writeFileSync(filePath, content, "utf-8");
44
43
  }
package/dist/types.d.ts CHANGED
@@ -30,7 +30,6 @@ export interface Trigger {
30
30
  }
31
31
  export interface ParsedTask {
32
32
  frontmatter: TaskFrontmatter;
33
- body: string;
34
33
  }
35
34
  /**
36
35
  * - `started`: task is actively running
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palmier",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "description": "Palmier host CLI - provisions, executes tasks, and serves NATS RPC",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hongxu Cai",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "scripts": {
22
22
  "dev": "tsx src/index.ts",
23
- "build": "tsc && node -e \"const fs=require('fs');fs.cpSync('src/commands/plan-generation.md','dist/commands/plan-generation.md');fs.cpSync('src/agents/agent-instructions.md','dist/agents/agent-instructions.md');const p=process.env.PALMIER_PWA_DIST||'../palmier-server/pwa/dist';if(fs.existsSync(p))fs.cpSync(p,'dist/pwa',{recursive:true});else console.warn('PWA dist not found at '+p+', skipping')\"",
23
+ "build": "tsc && node -e \"const fs=require('fs');fs.cpSync('src/agents/agent-instructions.md','dist/agents/agent-instructions.md');const p=process.env.PALMIER_PWA_DIST||'../palmier-server/pwa/dist';if(fs.existsSync(p))fs.cpSync(p,'dist/pwa',{recursive:true});else console.warn('PWA dist not found at '+p+', skipping')\"",
24
24
  "test": "tsx --test test/**/*.test.ts",
25
25
  "prepare": "npm run build",
26
26
  "start": "node dist/index.js"
@@ -1,2 +1,2 @@
1
1
  /** Bump when a breaking host change is made. */
2
- export const MIN_HOST_VERSION = "0.6.7";
2
+ export const MIN_HOST_VERSION = "0.6.8";
@@ -31,8 +31,8 @@ export interface CommandLine {
31
31
  * Abstracts how plans are generated and tasks are executed across different AI agents.
32
32
  */
33
33
  export interface AgentTool {
34
- /** Return the command and args used to generate a plan from a prompt. */
35
- getPlanGenerationCommandLine(prompt: string): CommandLine;
34
+ /** Return the command and args for a short, non-interactive prompt (e.g. generating a task name). */
35
+ getPromptCommandLine(prompt: string): CommandLine;
36
36
 
37
37
  /** Return the command and args used to run a task. If followupPrompt is provided, use it instead of the task's prompt,
38
38
  * and treat it as a continuation of the original run (reuse the same session, etc).
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Aider implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "aider",
12
- args: ["--message", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "aider", args: ["--message", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class ClaudeAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "claude",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "claude", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Cline implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "cline ",
12
- args: ["--yolo", "-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "cline ", args: ["--yolo", "-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class CodexAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "codex",
12
- args: ["exec", "--skip-git-repo-check", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "codex", args: ["exec", "--skip-git-repo-check", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class CopilotAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "copilot",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "copilot", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Cursor implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "cursor",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "cursor", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class DeepAgents implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "deepagents",
12
- args: ["--non-interactive", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "deepagents", args: ["--non-interactive", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class DroidAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "droid",
12
- args: ["exec", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "droid", args: ["exec", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class GeminiAgent implements AgentTool {
8
8
  supportsPermissions = true;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "gemini",
12
- args: ["--prompt", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "gemini", args: ["--prompt", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class GooseAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "goose",
12
- args: ["run", "--text", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "goose", args: ["run", "--text", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Hermes implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "hermes",
12
- args: ["chat", "-q", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "hermes", args: ["chat", "-q", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class KimiAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "kimi",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "kimi", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Kiro implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "kiro-cli",
12
- args: ["--no-interactive", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "kiro-cli", args: ["--no-interactive", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -5,11 +5,8 @@ import { getAgentInstructions } from "./shared-prompt.js";
5
5
 
6
6
  export class OpenClawAgent implements AgentTool {
7
7
  supportsPermissions = false;
8
- getPlanGenerationCommandLine(prompt: string): CommandLine {
9
- return {
10
- command: "openclaw",
11
- args: ["agent", "--local", "--agent", "main", "--message", prompt],
12
- };
8
+ getPromptCommandLine(prompt: string): CommandLine {
9
+ return { command: "openclaw", args: ["agent", "--local", "--agent", "main", "--message", prompt] };
13
10
  }
14
11
 
15
12
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class OpenCodeAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "opencode",
12
- args: ["run", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "opencode", args: ["run", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class Qoder implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "qodercli",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "qodercli", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -6,11 +6,8 @@ import { SHELL } from "../platform/index.js";
6
6
 
7
7
  export class QwenAgent implements AgentTool {
8
8
  supportsPermissions = false;
9
- getPlanGenerationCommandLine(prompt: string): CommandLine {
10
- return {
11
- command: "qwen",
12
- args: ["-p", prompt],
13
- };
9
+ getPromptCommandLine(prompt: string): CommandLine {
10
+ return { command: "qwen", args: ["-p", prompt] };
14
11
  }
15
12
 
16
13
  getTaskRunCommandLine(task: ParsedTask, followupPrompt?: string, extraPermissions?: RequiredPermission[] | "yolo"): CommandLine {
@@ -17,7 +17,7 @@ const AGENT_INSTRUCTIONS_TEMPLATE = fs.readFileSync(
17
17
  */
18
18
  export function getAgentInstructions(task: ParsedTask, skipPermissions?: boolean): string {
19
19
  const port = loadConfig().httpPort ?? 9966;
20
- const taskDescription = task.body || task.frontmatter.user_prompt;
20
+ const taskDescription = task.frontmatter.user_prompt;
21
21
  let instructions = AGENT_INSTRUCTIONS_TEMPLATE
22
22
  .replace(/\{\{ENDPOINT_DOCS\}\}/g, generateEndpointDocs(port, task.frontmatter.id))
23
23
  .replace(/\{\{TASK_DESCRIPTION\}\}/g, taskDescription);
@@ -272,7 +272,7 @@ export async function runCommand(taskId: string): Promise<void> {
272
272
  await appendAndNotify(ctx, {
273
273
  role: "user",
274
274
  time: Date.now(),
275
- content: task.body || task.frontmatter.user_prompt,
275
+ content: task.frontmatter.user_prompt,
276
276
  });
277
277
 
278
278
  const result = await invokeAgentWithRetries(ctx, task);
@@ -362,7 +362,6 @@ async function runCommandTriggeredMode(
362
362
  const perLinePrompt = `${ctx.task.frontmatter.user_prompt}\n\nProcess this input:\n${line}`;
363
363
  const perLineTask: ParsedTask = {
364
364
  frontmatter: { ...ctx.task.frontmatter, user_prompt: perLinePrompt },
365
- body: "",
366
365
  };
367
366
 
368
367
  const result = await invokeAgentWithRetries(ctx, perLineTask);
package/src/mcp-tools.ts CHANGED
@@ -208,14 +208,14 @@ export const agentToolMap = new Map<string, ToolDefinition>(agentTools.map((t) =
208
208
  /**
209
209
  * Generate the HTTP Endpoints markdown section for agent-instructions.md from the tool registry.
210
210
  */
211
- export function generateEndpointDocs(port: number, taskId: string): string {
211
+ export function generateEndpointDocs(port: number, taskId: string, tools: ToolDefinition[] = agentTools): string {
212
212
  const baseUrl = `http://localhost:${port}`;
213
213
  const lines: string[] = [
214
214
  `The following HTTP endpoints are available during task execution. Use curl to call them.`,
215
215
  "",
216
216
  ];
217
217
 
218
- for (const tool of agentTools) {
218
+ for (const tool of tools) {
219
219
  const schema = tool.inputSchema as { properties?: Record<string, { type?: string; description?: string; items?: { type?: string } }>; required?: string[] };
220
220
  const props = schema.properties ?? {};
221
221
  const required = new Set(schema.required ?? []);
@@ -1,9 +1,7 @@
1
1
  import { randomUUID } from "crypto";
2
2
  import * as fs from "fs";
3
3
  import * as path from "path";
4
- import { fileURLToPath } from "url";
5
4
  import { spawn, type ChildProcess } from "child_process";
6
- import { parse as parseYaml } from "yaml";
7
5
  import { type NatsConnection } from "nats";
8
6
  import { listTasks, parseTaskFile, writeTaskFile, getTaskDir, readTaskStatus, writeTaskStatus, readHistory, deleteHistoryEntry, appendTaskList, removeFromTaskList, appendHistory, createRunDir, appendRunMessage, getRunDir } from "./task.js";
9
7
  import { resolvePending, getPending } from "./pending-requests.js";
@@ -18,13 +16,6 @@ import { currentVersion, performUpdate } from "./update-checker.js";
18
16
  import { parseReportFiles, parseTaskOutcome, stripPalmierMarkers } from "./commands/run.js";
19
17
  import type { HostConfig, ParsedTask, RpcMessage, ConversationMessage } from "./types.js";
20
18
 
21
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
-
23
- const PLAN_GENERATION_PROMPT = fs.readFileSync(
24
- path.join(__dirname, "commands", "plan-generation.md"),
25
- "utf-8",
26
- );
27
-
28
19
  /**
29
20
  * Parse RESULT frontmatter and conversation messages.
30
21
  */
@@ -115,39 +106,30 @@ function parseAttr(attrs: string, name: string): string | undefined {
115
106
  }
116
107
 
117
108
  /**
118
- * Run plan generation for a task prompt using the given agent.
119
- * Returns the generated plan body and task name.
109
+ * Generate a concise task name from a user prompt using the given agent.
110
+ * Falls back to the raw prompt on failure.
120
111
  */
121
- async function generatePlan(
112
+ async function generateName(
122
113
  projectRoot: string,
123
114
  userPrompt: string,
124
115
  agentName: string,
125
- ): Promise<{ name: string; body: string }> {
126
- const fullPrompt = PLAN_GENERATION_PROMPT + userPrompt;
127
- const planAgent = getAgent(agentName);
128
- const { command, args, stdin, env: agentEnv } = planAgent.getPlanGenerationCommandLine(fullPrompt);
129
-
130
- const { output } = await spawnCommand(command, args, {
131
- cwd: projectRoot,
132
- timeout: 120_000,
133
- stdin,
134
- ...(agentEnv ? { env: agentEnv } : {}),
135
- });
136
-
137
- let name = "";
138
- const trimmed = output.trim();
139
- let body = trimmed;
140
- const fmMatch = trimmed.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
141
- if (fmMatch) {
142
- try {
143
- const fm = parseYaml(fmMatch[1]) as { task_name?: string };
144
- name = fm.task_name ?? "";
145
- } catch {
146
- // If frontmatter parsing fails, treat entire output as body
147
- }
148
- body = fmMatch[2].trimStart();
116
+ ): Promise<string> {
117
+ const prompt = `Generate a concise 3-6 word name for this task. Reply with ONLY the name, nothing else.\n\nTask: ${userPrompt}`;
118
+ const agent = getAgent(agentName);
119
+ const { command, args, stdin, env: agentEnv } = agent.getPromptCommandLine(prompt);
120
+
121
+ try {
122
+ const { output } = await spawnCommand(command, args, {
123
+ cwd: projectRoot,
124
+ timeout: 30_000,
125
+ stdin,
126
+ ...(agentEnv ? { env: agentEnv } : {}),
127
+ });
128
+ const name = output.trim().replace(/^["']|["']$/g, "").slice(0, 80);
129
+ return name || userPrompt;
130
+ } catch {
131
+ return userPrompt;
149
132
  }
150
- return { name, body };
151
133
  }
152
134
 
153
135
  /** Active follow-up child processes, keyed by "taskId:runId". */
@@ -163,7 +145,6 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
163
145
  const pending = getPending(task.frontmatter.id);
164
146
  return {
165
147
  ...task.frontmatter,
166
- body: task.body,
167
148
  status: status ? {
168
149
  ...status,
169
150
  ...(pending?.type === "permission" ? { pending_permission: pending.params } : {}),
@@ -215,25 +196,13 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
215
196
  command?: string;
216
197
  };
217
198
 
218
- // Only generate a plan for longer prompts that benefit from it
219
- let name = "";
220
- let body = "";
221
- if (params.user_prompt.length <= 50) {
222
- name = params.user_prompt;
223
- } else {
224
- try {
225
- const plan = await generatePlan(config.projectRoot, params.user_prompt, params.agent);
226
- name = plan.name;
227
- body = plan.body;
228
- } catch (err: unknown) {
229
- const error = err as { stdout?: string; stderr?: string };
230
- return { error: "plan generation failed", stdout: error.stdout, stderr: error.stderr };
231
- }
232
- }
199
+ const name = params.user_prompt.length <= 50
200
+ ? params.user_prompt
201
+ : await generateName(config.projectRoot, params.user_prompt, params.agent);
233
202
 
234
203
  const id = randomUUID();
235
204
  const taskDir = getTaskDir(config.projectRoot, id);
236
- const task = {
205
+ const task: ParsedTask = {
237
206
  frontmatter: {
238
207
  id,
239
208
  name,
@@ -246,7 +215,6 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
246
215
  ...(params.foreground_mode ? { foreground_mode: true } : {}),
247
216
  ...(params.command ? { command: params.command } : {}),
248
217
  },
249
- body,
250
218
  };
251
219
 
252
220
  writeTaskFile(taskDir, task);
@@ -272,10 +240,9 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
272
240
  const taskDir = getTaskDir(config.projectRoot, params.id);
273
241
  const existing = parseTaskFile(taskDir);
274
242
 
275
- // Detect whether plan needs regeneration
243
+ // Detect whether name needs regeneration
276
244
  const promptChanged = params.user_prompt !== undefined && params.user_prompt !== existing.frontmatter.user_prompt;
277
245
  const agentChanged = params.agent !== undefined && params.agent !== existing.frontmatter.agent;
278
- const needsRegeneration = promptChanged || agentChanged || !existing.body;
279
246
 
280
247
  // Merge updates
281
248
  if (params.user_prompt !== undefined) existing.frontmatter.user_prompt = params.user_prompt;
@@ -297,19 +264,11 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
297
264
  }
298
265
  }
299
266
 
300
- // Regenerate plan if needed (only for longer prompts)
301
- if (existing.frontmatter.user_prompt.length <= 50) {
302
- existing.frontmatter.name = existing.frontmatter.user_prompt;
303
- existing.body = "";
304
- } else if (needsRegeneration) {
305
- try {
306
- const plan = await generatePlan(config.projectRoot, existing.frontmatter.user_prompt, existing.frontmatter.agent);
307
- existing.frontmatter.name = plan.name;
308
- existing.body = plan.body;
309
- } catch (err: unknown) {
310
- const error = err as { stdout?: string; stderr?: string };
311
- return { error: "plan generation failed", stdout: error.stdout, stderr: error.stderr };
312
- }
267
+ // Regenerate name when prompt or agent changes
268
+ if (promptChanged || agentChanged) {
269
+ existing.frontmatter.name = existing.frontmatter.user_prompt.length <= 50
270
+ ? existing.frontmatter.user_prompt
271
+ : await generateName(config.projectRoot, existing.frontmatter.user_prompt, existing.frontmatter.agent);
313
272
  }
314
273
 
315
274
  writeTaskFile(taskDir, existing);
@@ -356,7 +315,6 @@ export function createRpcHandler(config: HostConfig, nc?: NatsConnection) {
356
315
  ...(params.foreground_mode ? { foreground_mode: true } : {}),
357
316
  ...(params.command ? { command: params.command } : {}),
358
317
  },
359
- body: "",
360
318
  };
361
319
 
362
320
  writeTaskFile(taskDir, task);
package/src/task.ts CHANGED
@@ -29,7 +29,6 @@ export function parseTaskContent(content: string): ParsedTask {
29
29
  }
30
30
 
31
31
  const frontmatter = parseYaml(match[1]) as TaskFrontmatter;
32
- const body = (match[2] || "").trim();
33
32
 
34
33
  if (!frontmatter.id) {
35
34
  throw new Error("TASK.md frontmatter must include at least: id");
@@ -39,7 +38,7 @@ export function parseTaskContent(content: string): ParsedTask {
39
38
  frontmatter.agent ??= "claude";
40
39
  frontmatter.triggers_enabled ??= true;
41
40
 
42
- return { frontmatter, body };
41
+ return { frontmatter };
43
42
  }
44
43
 
45
44
  /**
@@ -50,7 +49,7 @@ export function writeTaskFile(taskDir: string, task: ParsedTask): void {
50
49
  fs.mkdirSync(taskDir, { recursive: true });
51
50
 
52
51
  const yamlStr = stringifyYaml(task.frontmatter).trim();
53
- const content = `---\n${yamlStr}\n---\n${task.body}\n`;
52
+ const content = `---\n${yamlStr}\n---\n`;
54
53
 
55
54
  const filePath = path.join(taskDir, "TASK.md");
56
55
  fs.writeFileSync(filePath, content, "utf-8");
package/src/types.ts CHANGED
@@ -36,7 +36,6 @@ export interface Trigger {
36
36
 
37
37
  export interface ParsedTask {
38
38
  frontmatter: TaskFrontmatter;
39
- body: string;
40
39
  }
41
40
 
42
41
  /**