teamshare-bridge 0.21.10 → 0.21.11

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.
@@ -35,51 +35,138 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.cmdPlanner = cmdPlanner;
37
37
  /**
38
- * `planner` command: decompose a task into subtasks using the agent's
39
- * harness. The agent receives a planning brief, uses the `submit_plan`
40
- * MCP tool to return structured subtasks, and the result is written to
41
- * a temp file for the bridge to read and emit back to the backend.
38
+ * `planner` command: run a REAL planning session with the planner agent's
39
+ * harness (plan-mode parity). Two modes:
42
40
  *
43
- * The `submit_plan` MCP tool validates the plan (cycle detection, index
44
- * checks) but does NOT persist the backend's `createChildren()` handles
45
- * all persistence after receiving the validated plan.
41
+ * - `task` plan a SINGLE task: read the full task, research the project,
42
+ * rewrite the description + acceptance criteria, add checklist
43
+ * items, write `plan.md`, and (optionally) propose sub-tasks.
44
+ * NEVER creates replacement tasks.
45
+ * - `overview` — plan the WHOLE dispatched batch: read every queued task,
46
+ * produce the shared plan on the plan task, the execution
47
+ * ORDER and the dependency edges. NEVER invents tasks.
46
48
  *
47
- * Falls back to headless LLM (complete()) when no harness is available.
49
+ * The agent returns its plan via the `submit_plan` MCP tool, which writes a
50
+ * temp file for the bridge to read and emit back to the backend. The backend
51
+ * applies it (ordering, nested sub-tasks, plan note) — this CLI only plans.
48
52
  */
49
53
  const node_fs_1 = require("node:fs");
50
54
  const node_path_1 = require("node:path");
51
55
  const node_os_1 = require("node:os");
52
56
  const api_1 = require("../../lib/api");
53
- const llm_1 = require("../../lib/llm/llm");
57
+ const brief_1 = require("../../lib/brief");
54
58
  const lock_1 = require("../../lib/lock");
55
59
  const daemon_reporter_1 = require("../../lib/chat/daemon-reporter");
56
60
  const session_stream_1 = require("../../lib/chat/session-stream");
57
- const brief_1 = require("../../lib/brief");
58
61
  const utils_1 = require("../utils");
59
62
  const harness_1 = require("./harness");
60
63
  const shared_1 = require("./shared");
61
64
  const presence_1 = require("../../lib/workspace/presence");
62
- const PLANNER_PROMPT_TEMPLATE = `You are a software build planner. Break the task below into 2-5 concrete,
63
- independently verifiable subtasks, in dependency order.
64
-
65
- For each subtask, include a "verification" array with 2-4 concrete, checkable criteria
66
- (e.g. "build succeeds with no errors", "all tests pass", "endpoint returns 200", "docs updated").
67
- These become checklist items the agent must verify.
68
-
69
- Use the submit_plan MCP tool to submit your decomposition. Return ONLY the tool call,
70
- no prose explanation.
71
-
72
- TASK: {description}`;
65
+ function sanitizeTitle(raw, fallback) {
66
+ const firstLine = (raw ?? "")
67
+ .split(/\r?\n/)
68
+ .map((l) => l.trim())
69
+ .find((l) => l.length > 0);
70
+ const cleaned = (firstLine ?? "")
71
+ .replace(/^[#>*\-\s]+/, "")
72
+ .replace(/[*_`#>]/g, "")
73
+ .replace(/\s+/g, " ")
74
+ .trim();
75
+ const title = cleaned ||
76
+ (raw ?? "")
77
+ .replace(/[*_`#>]/g, "")
78
+ .replace(/\s+/g, " ")
79
+ .trim();
80
+ if (!title)
81
+ return fallback;
82
+ return title.length > 120 ? `${title.slice(0, 117)}…` : title;
83
+ }
84
+ /** Shared planning rules: plan only, never execute, never invent tasks. */
85
+ const PLANNING_RULES = `You are a planning agent working in PLAN MODE. You must NOT execute or implement anything - no code edits, no build commands, no file writes outside the plan note.
86
+
87
+ ## Rules
88
+ - Plan ONLY. There is an execution queue that runs after you; you are the overview.
89
+ - NEVER invent replacement tasks or restate a task that already exists. If a task already covers a step, reference it by name.
90
+ - Use the TeamShare MCP tools to read (get_task, read_document, list_documents, list_checklist_items) and write (update_task, create_checklist_item, create_document, submit_plan).
91
+ - ALWAYS finish by calling the **submit_plan** MCP tool with \`planTaskId\` set to the EXACT task id given below. Without that call your plan is lost.`;
92
+ function buildTaskPrompt(planTaskId, task, projectName, brief) {
93
+ return (`${PLANNING_RULES}\n\n` +
94
+ `## Plan task (this is the task id you MUST pass to submit_plan)\n` +
95
+ `- **planTaskId:** ${planTaskId}\n` +
96
+ `- **Project:** ${projectName}\n` +
97
+ `- **Title:** ${task.title}\n` +
98
+ `- **Status:** ${task.status} · **Priority:** ${task.priority}\n` +
99
+ (task.tags?.length ? `- **Tags:** ${task.tags.join(", ")}\n` : "") +
100
+ `\n## Current description\n${task.description?.trim() || "(none)"}\n` +
101
+ `\n## Current acceptance criteria\n${task.acceptanceCriteria?.trim() || "(none)"}\n` +
102
+ `\n## What to do (plan only)\n` +
103
+ `1. Read the task and its context: use get_task with include comments/subtasks/checklist, and read linked documents with read_document.\n` +
104
+ `2. Research: consult the project context, \`docs/**\` notes and the relevant source files so the plan is grounded in the real codebase.\n` +
105
+ `3. Rewrite the description into a clear specification: goal, context, scope, out-of-scope, technical approach, risks.\n` +
106
+ `4. Write concrete, testable acceptance criteria and persist them with update_task (description, acceptanceCriteria).\n` +
107
+ `5. Add a verification checklist: call create_checklist_item once per acceptance criterion (task-scoped, no subtaskId).\n` +
108
+ `6. Write the plan note: create_document with a note named \`plan.md\` under \`agents/<you>/tasks/${planTaskId}\` explaining the approach, key files and risks.\n` +
109
+ `7. Finish by calling **submit_plan** with:\n` +
110
+ ` - planTaskId: \`${planTaskId}\`\n` +
111
+ ` - mode: "task"\n` +
112
+ ` - acceptanceCriteria: your final criteria (markdown bullets)\n` +
113
+ ` - planMarkdown: the \`plan.md\` body you wrote\n` +
114
+ ` - tasks: [] unless the work genuinely needs extra sub-tasks (only then, 1-5 items with title/description/verification).\n` +
115
+ `8. Post a one-paragraph summary comment, then STOP. Do NOT change the task status, do NOT execute.\n` +
116
+ `\n## TeamShare brief\n${brief || "(no brief available)"}`);
117
+ }
118
+ function buildOverviewPrompt(planTaskId, projectName, queue) {
119
+ const queueBlock = queue
120
+ .map((t, i) => `${i + 1}. id=\`${t.id}\` — **${t.title}**\n` +
121
+ (t.description
122
+ ? ` - ${t.description.replace(/\s+/g, " ").slice(0, 600)}\n`
123
+ : "") +
124
+ (t.acceptanceCriteria
125
+ ? ` - acceptance: ${t.acceptanceCriteria.replace(/\s+/g, " ").slice(0, 400)}\n`
126
+ : ""))
127
+ .join("\n");
128
+ return (`${PLANNING_RULES}\n\n` +
129
+ `## Plan task (pass this exact id to submit_plan)\n` +
130
+ `- **planTaskId:** ${planTaskId}\n` +
131
+ `- **Project:** ${projectName}\n` +
132
+ `- **This is an OVERVIEW plan for a whole batch.** The tasks below are the execution queue and ALREADY EXIST.\n` +
133
+ `\n## Execution queue (${queue.length} tasks — these are the queue, do NOT create replacements)\n${queueBlock}\n` +
134
+ `\n## What to do (plan only)\n` +
135
+ `1. Read every task above (get_task with include comments/subtasks/checklist). Read linked documents and the project context.\n` +
136
+ `2. Research the codebase enough to sequence the work correctly (shared files, ordering constraints, risks).\n` +
137
+ `3. Decide the execution ORDER of the given task ids and any dependency edges between them.\n` +
138
+ `4. Write the shared plan: goal, the ordered steps (referencing the existing tasks by title), cross-task risks, and how each task will be verified.\n` +
139
+ `5. Persist it: create_document with a note named \`plan.md\` under \`agents/<you>/tasks/${planTaskId}\`, and update_task on the plan task with the description + acceptance criteria.\n` +
140
+ `6. Finish by calling **submit_plan** with:\n` +
141
+ ` - planTaskId: \`${planTaskId}\`\n` +
142
+ ` - mode: "overview"\n` +
143
+ ` - orderedTaskIds: the given task ids in execution order (use the EXACT ids above)\n` +
144
+ ` - dependencies: [[taskId, dependsOnTaskId], ...] between the given ids (may be empty)\n` +
145
+ ` - acceptanceCriteria: criteria for the whole plan\n` +
146
+ ` - planMarkdown: the \`plan.md\` body\n` +
147
+ ` - tasks: [] — do NOT propose replacement tasks.\n` +
148
+ `7. Post a one-paragraph summary comment on the plan task, then STOP.\n`);
149
+ }
73
150
  async function cmdPlanner(flags) {
74
- const agentId = (0, utils_1.need)(flags, 'agent');
75
- const projectId = (0, utils_1.need)(flags, 'project');
76
- const parentTaskId = (0, utils_1.need)(flags, 'task');
151
+ const agentId = (0, utils_1.need)(flags, "agent");
152
+ const projectId = (0, utils_1.need)(flags, "project");
153
+ const planTaskId = (0, utils_1.need)(flags, "task");
154
+ const mode = flags.get("mode") === "overview" ? "overview" : "task";
155
+ const queueIds = (flags.get("tasks") ?? "")
156
+ .split(",")
157
+ .map((s) => s.trim())
158
+ .filter(Boolean);
77
159
  const apiKey = (0, utils_1.resolveApiKey)(flags);
78
- const selfMode = flags.get('self') === 'true';
79
- const resultPath = flags.get('result-file') ?? (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'state', `planner-${parentTaskId.slice(0, 8)}.json`);
160
+ const resultPath = flags.get("result-file") ??
161
+ (0, node_path_1.join)((0, node_os_1.homedir)(), ".teamshare", "state", `planner-${planTaskId.slice(0, 8)}.json`);
80
162
  let exitCode = 0;
81
- const sessionId = flags.get('session') ?? (0, session_stream_1.newSessionId)();
82
- const reporter = new daemon_reporter_1.DaemonReporter({ sessionId, agentId, projectId, mode: 'run' });
163
+ const sessionId = flags.get("session") ?? (0, session_stream_1.newSessionId)();
164
+ const reporter = new daemon_reporter_1.DaemonReporter({
165
+ sessionId,
166
+ agentId,
167
+ projectId,
168
+ mode: "run",
169
+ });
83
170
  reporter.connect();
84
171
  const log = (0, utils_1.makeLog)(reporter);
85
172
  const api = new api_1.TeamshareApi(process.env.TEAMSHARE_API_URL, apiKey);
@@ -89,7 +176,7 @@ async function cmdPlanner(flags) {
89
176
  const settings = await (0, utils_1.fetchAgentSettings)(api, agentId, log);
90
177
  const harness = (0, utils_1.harnessOf)(flags, settings);
91
178
  (0, utils_1.applyOpenCodeKey)(agentId, log, settings);
92
- const lock = await (0, lock_1.acquireLock)(agentId, 'orchestrator', { projectId, taskId: parentTaskId }, {
179
+ const lock = await (0, lock_1.acquireLock)(agentId, "orchestrator", { projectId, taskId: planTaskId }, {
93
180
  onWaiting: (busy) => log(`[lock] agent ${agentId} is busy (pid ${busy.pid}, since ${busy.startedAt}, ${busy.kind}) - waiting...`),
94
181
  });
95
182
  if (!lock) {
@@ -98,80 +185,101 @@ async function cmdPlanner(flags) {
98
185
  return;
99
186
  }
100
187
  try {
101
- (0, presence_1.registerPresence)(cwd, { agentId, kind: 'orchestrator', projectId });
102
- await (0, brief_1.reportStatus)(api, agentId, 'working', parentTaskId, sessionId);
103
- log(`[planner] decomposing task ${parentTaskId.slice(0, 8)} in ${cwd}`);
104
- // Fetch the task brief for context.
105
- const brief = await (0, brief_1.fetchTaskBrief)(api, parentTaskId).catch(() => '');
106
- const description = brief || `Task ${parentTaskId}`;
107
- const prompt = PLANNER_PROMPT_TEMPLATE.replace('{description}', description);
188
+ (0, presence_1.registerPresence)(cwd, { agentId, kind: "orchestrator", projectId });
189
+ await (0, brief_1.reportStatus)(api, agentId, "working", planTaskId, sessionId);
190
+ const project = await api.getProject(projectId).catch(() => null);
191
+ const projectName = project?.name ?? projectId;
192
+ // Fetch the plan task for context.
193
+ const task = await api.getTask(planTaskId).catch(() => null);
194
+ if (!task) {
195
+ log(`[planner] cannot fetch plan task ${planTaskId}`, "stderr");
196
+ process.exitCode = 1;
197
+ return;
198
+ }
199
+ // Build the prompt.
200
+ let prompt;
201
+ if (mode === "overview") {
202
+ const queue = await Promise.all(queueIds.map(async (id) => {
203
+ const t = await api.getTask(id).catch(() => null);
204
+ return {
205
+ id,
206
+ title: t?.title ?? id,
207
+ description: t?.description ?? null,
208
+ acceptanceCriteria: t?.acceptanceCriteria ?? null,
209
+ };
210
+ }));
211
+ prompt = buildOverviewPrompt(planTaskId, projectName, queue);
212
+ log(`[planner] overview mode: planning a queue of ${queue.length} tasks`);
213
+ }
214
+ else {
215
+ const brief = await (0, brief_1.fetchTaskBrief)(api, planTaskId).catch(() => "");
216
+ prompt = buildTaskPrompt(planTaskId, task, projectName, brief);
217
+ log(`[planner] task mode: planning task ${planTaskId.slice(0, 8)} in ${cwd}`);
218
+ }
219
+ // Mirror the prompt locally so it can be inspected.
220
+ if (cwd) {
221
+ try {
222
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(cwd, "teamshare", "plans", planTaskId), {
223
+ recursive: true,
224
+ });
225
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(cwd, "teamshare", "plans", planTaskId, "plan-prompt.md"), prompt, "utf8");
226
+ }
227
+ catch {
228
+ /* best-effort */
229
+ }
230
+ }
231
+ // Clear any stale result file so we never read a previous plan.
232
+ try {
233
+ if ((0, node_fs_1.existsSync)(resultPath))
234
+ (0, node_fs_1.unlinkSync)(resultPath);
235
+ }
236
+ catch {
237
+ /* ignore */
238
+ }
108
239
  const heartbeatTimer = setInterval(() => {
109
- void (0, brief_1.reportStatus)(api, agentId, 'working', parentTaskId, sessionId);
240
+ void (0, brief_1.reportStatus)(api, agentId, "working", planTaskId, sessionId);
110
241
  }, 120_000);
111
242
  try {
112
- const endpoint = (0, utils_1.llmEndpoint)(flags, settings);
113
- let plan = null;
114
- if (harness && harness !== 'none') {
115
- log(`[planner] spawning ${harness} harness for decomposition`);
116
- // Spawn the harness. The agent will call submit_plan which writes
117
- // the validated plan to a temp file. We wait for the file to appear.
243
+ if (harness && harness !== "none") {
244
+ log(`[planner] spawning ${harness} harness for ${mode} planning`);
118
245
  const exit = await (0, harness_1.spawnHarness)(harness, prompt, log, {
119
246
  model: (0, utils_1.modelOf)(flags, settings),
120
247
  temperature: (0, utils_1.temperatureOf)(flags, settings),
121
248
  effort: (0, utils_1.effortOf)(flags, settings) ?? (0, utils_1.defaultEffort)(harness),
122
249
  cwd,
123
250
  timeoutMin: (0, utils_1.sessionTimeoutMin)(flags),
124
- title: `Planner decomposition for task ${parentTaskId.slice(0, 8)}`,
251
+ title: `Planner ${mode} for task ${planTaskId.slice(0, 8)}`,
125
252
  agentKey: apiKey,
126
253
  apiBaseUrl: api.baseUrl,
127
254
  providerKey: settings.providerKey ?? undefined,
128
- sessionMode: 'run',
255
+ sessionMode: "run",
129
256
  sessionId,
130
257
  });
131
- // Read the result file written by submit_plan.
132
- plan = readResultFile(resultPath, log);
133
- if (!plan && exit !== 0) {
134
- log(`[planner] WARNING: harness exited with code ${exit} and no plan file found at ${resultPath}`, 'stderr');
135
- }
136
- else if (!plan) {
137
- log(`[planner] WARNING: harness exited successfully but no plan file found at ${resultPath}`, 'stderr');
258
+ if (exit !== 0) {
259
+ log(`[planner] WARNING: harness exited with code ${exit}`, "stderr");
138
260
  }
139
261
  }
140
262
  else {
141
- log(`[planner] WARNING: no harness binary found on PATH — falling back to headless LLM (install opencode or set harness in agent settings)`, 'stderr');
142
- }
143
- // Fall back to headless LLM if harness didn't produce a plan.
144
- if (!plan) {
145
- log(`[planner] falling back to headless LLM (harness did not produce a plan)`);
146
- plan = await runHeadlessPlanner(prompt, {
147
- baseUrl: endpoint.baseUrl,
148
- apiKey: (0, utils_1.sessionLlmApiKey)(settings),
149
- model: endpoint.model,
150
- temperature: (0, utils_1.temperatureOf)(flags, settings),
151
- maxTokens: (0, utils_1.maxTokensOf)(flags, settings),
152
- }, log);
153
- }
154
- if (plan && plan.length > 0) {
155
- // Write the final result for the bridge to read.
156
- (0, node_fs_1.writeFileSync)(resultPath, JSON.stringify({ tasks: plan }), 'utf-8');
157
- log(`[planner] plan ready: ${plan.length} subtasks -> ${resultPath}`);
158
- }
159
- else {
160
- // Single-node fallback.
161
- const fallback = [{ title: description.slice(0, 200), description, dependencies: [], verification: [] }];
162
- (0, node_fs_1.writeFileSync)(resultPath, JSON.stringify({ tasks: fallback }), 'utf-8');
163
- log(`[planner] fallback: single node -> ${resultPath}`);
263
+ log(`[planner] WARNING: no harness binary found on PATH — the planner cannot run (install opencode or set the harness in agent settings)`, "stderr");
164
264
  }
265
+ // Read the plan the agent submitted. If it did not submit one, keep the
266
+ // given queue unchanged — never invent replacement tasks.
267
+ const result = readResultFile(resultPath, mode, queueIds, log);
268
+ (0, node_fs_1.writeFileSync)(resultPath, JSON.stringify(result), "utf-8");
269
+ log(`[planner] plan ready: mode=${mode} tasks=${result.tasks.length} ordered=${result.orderedTaskIds.length} -> ${resultPath}`);
270
+ await (0, brief_1.postClosingComment)(api, planTaskId, mode === "overview"
271
+ ? `[plan] Overview plan ready for ${queueIds.length} queued task(s); execution order set.`
272
+ : `[plan] Planning pass finished; description, acceptance criteria, checklist and plan.md updated.`).catch(() => undefined);
165
273
  }
166
274
  finally {
167
275
  clearInterval(heartbeatTimer);
168
276
  void api.releaseSessionClaims(sessionId).catch(() => { });
169
- await (0, brief_1.reportStatus)(api, agentId, 'online', parentTaskId, sessionId);
277
+ await (0, brief_1.reportStatus)(api, agentId, "online", planTaskId, sessionId);
170
278
  }
171
279
  }
172
280
  catch (err) {
173
- const { msg } = await Promise.resolve().then(() => __importStar(require('../../lib/config')));
174
- log(`[error] ${msg(err)}`, 'stderr');
281
+ const { msg } = await Promise.resolve().then(() => __importStar(require("../../lib/config")));
282
+ log(`[error] ${msg(err)}`, "stderr");
175
283
  exitCode = 1;
176
284
  }
177
285
  finally {
@@ -180,68 +288,65 @@ async function cmdPlanner(flags) {
180
288
  await reporter.end(exitCode);
181
289
  }
182
290
  }
183
- /** Read the plan from the result file written by submit_plan MCP tool. */
184
- function readResultFile(resultPath, log) {
185
- try {
186
- const raw = (0, node_fs_1.readFileSync)(resultPath, 'utf-8');
187
- const parsed = JSON.parse(raw);
188
- const tasks = parsed?.tasks;
189
- if (!Array.isArray(tasks) || tasks.length === 0)
190
- return null;
191
- // Validate and normalize.
192
- return tasks.map((node, i) => ({
193
- title: String(node.title ?? `Step ${i + 1}`).slice(0, 200),
194
- description: String(node.description ?? ''),
195
- dependencies: Array.isArray(node.dependencies)
196
- ? node.dependencies.filter((d) => Number.isInteger(d) && d >= 0 && d < tasks.length && d !== i)
197
- : [],
198
- verification: Array.isArray(node.verification)
199
- ? node.verification.filter((v) => v.trim().length > 0)
200
- : [],
201
- }));
202
- }
203
- catch {
204
- log(`[planner] result file not found or invalid: ${resultPath}`, 'stderr');
205
- return null;
206
- }
207
- }
291
+ /** Resolve the agent API key from flags (mirrors the shared helper). */
208
292
  /**
209
- * Headless planner fallback: uses the complete() LLM call directly (no
210
- * harness, no MCP tools). Returns the parsed plan.
293
+ * Read + normalize the plan the agent submitted via `submit_plan`. When no
294
+ * plan was submitted the given queue is preserved untouched (no invented
295
+ * tasks) so the run still proceeds.
211
296
  */
212
- async function runHeadlessPlanner(prompt, llmOpts, log) {
297
+ function readResultFile(resultPath, mode, queueIds, log) {
298
+ const fallback = {
299
+ tasks: [],
300
+ orderedTaskIds: queueIds,
301
+ dependencies: [],
302
+ };
303
+ if (!(0, node_fs_1.existsSync)(resultPath)) {
304
+ log(`[planner] no plan submitted - keeping the given queue order`, "stderr");
305
+ return fallback;
306
+ }
213
307
  try {
214
- const raw = await (0, llm_1.complete)(prompt, {
215
- baseUrl: llmOpts.baseUrl,
216
- apiKey: llmOpts.apiKey,
217
- model: llmOpts.model,
218
- temperature: llmOpts.temperature ?? 0.2,
219
- maxTokens: llmOpts.maxTokens ?? 2000,
220
- });
221
- const cleaned = raw.replace(/```json/gi, '').replace(/```/g, '').trim();
222
- const start = cleaned.indexOf('[');
223
- const end = cleaned.lastIndexOf(']');
224
- if (start < 0 || end <= start) {
225
- log('[planner] headless LLM returned unparseable response', 'stderr');
226
- return null;
227
- }
228
- const parsed = JSON.parse(cleaned.slice(start, end + 1));
229
- if (!Array.isArray(parsed) || parsed.length === 0)
230
- return null;
231
- return parsed.map((node, i) => ({
232
- title: String(node.title ?? `Step ${i + 1}`).slice(0, 200),
233
- description: String(node.description ?? ''),
308
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(resultPath, "utf-8"));
309
+ const rawTasks = Array.isArray(parsed.tasks)
310
+ ? parsed.tasks
311
+ : [];
312
+ const tasks = rawTasks.map((node, i) => ({
313
+ title: sanitizeTitle(String(node.title ?? ""), `Step ${i + 1}`),
314
+ description: String(node.description ?? ""),
234
315
  dependencies: Array.isArray(node.dependencies)
235
- ? node.dependencies.filter((d) => Number.isInteger(d) && d >= 0 && d < parsed.length && d !== i)
316
+ ? node.dependencies.filter((d) => Number.isInteger(d) && d >= 0 && d < rawTasks.length && d !== i)
236
317
  : [],
237
318
  verification: Array.isArray(node.verification)
238
- ? node.verification.filter((v) => String(v).trim().length > 0).map(String)
319
+ ? node.verification.filter((v) => v.trim().length > 0)
239
320
  : [],
240
321
  }));
322
+ const ordered = Array.isArray(parsed.orderedTaskIds)
323
+ ? parsed.orderedTaskIds
324
+ .map(String)
325
+ .filter((id) => queueIds.includes(id))
326
+ : [];
327
+ const known = new Set(queueIds);
328
+ const dependencies = Array.isArray(parsed.dependencies)
329
+ ? parsed.dependencies.filter((e) => Array.isArray(e) &&
330
+ e.length === 2 &&
331
+ e[0] !== e[1] &&
332
+ known.has(e[0]) &&
333
+ known.has(e[1]))
334
+ : [];
335
+ return {
336
+ tasks: mode === "overview" ? [] : tasks,
337
+ orderedTaskIds: ordered.length ? ordered : queueIds,
338
+ dependencies,
339
+ planMarkdown: typeof parsed.planMarkdown === "string"
340
+ ? parsed.planMarkdown
341
+ : undefined,
342
+ acceptanceCriteria: typeof parsed.acceptanceCriteria === "string"
343
+ ? parsed.acceptanceCriteria
344
+ : undefined,
345
+ };
241
346
  }
242
- catch (err) {
243
- log(`[planner] headless LLM failed: ${err instanceof Error ? err.message : String(err)}`, 'stderr');
244
- return null;
347
+ catch {
348
+ log(`[planner] result file invalid: ${resultPath}`, "stderr");
349
+ return fallback;
245
350
  }
246
351
  }
247
352
  //# sourceMappingURL=planner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/cli/commands/planner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../../src/cli/commands/planner.ts"],"names":[],"mappings":"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"}
@@ -90,10 +90,40 @@ async function cmdRun(flags) {
90
90
  let gitPrepared;
91
91
  let gitPolicy = 'pr';
92
92
  let allowMainCommit = false;
93
+ // Real task context for the safety-net commit (the fetched `task` is
94
+ // block-scoped inside the try, so capture what the `finally` path needs).
95
+ let gitTaskTitle;
96
+ let gitProjectSlug;
93
97
  const sessionId = flags.get('session') ?? (0, session_stream_1.newSessionId)();
94
98
  const reporter = new daemon_reporter_1.DaemonReporter({ sessionId, agentId, mode: 'run' });
95
99
  const log = (0, utils_1.makeLog)(reporter);
96
100
  (0, utils_1.applyOpenCodeKey)(agentId, log, settings);
101
+ /**
102
+ * Safety-net commit: persist any uncommitted AGENT work to the run's task
103
+ * branch. MUST run BEFORE the stash pop so it can never sweep the user's
104
+ * restored dirty files into a commit. The `!s.popped` guard makes it a
105
+ * no-op once a repo's stash has been restored, so repeated calls are safe,
106
+ * and it carries the real task title/slug so work never lands on the
107
+ * placeholder `tasks/task` branch.
108
+ */
109
+ const maybeSafetyCommit = async () => {
110
+ if (!gitPrepared?.active || !presenceCwd)
111
+ return;
112
+ if (!stashResults.some((s) => s.stashed && !s.popped))
113
+ return;
114
+ const safetyResult = await (0, git_pipeline_1.commitRemainingChanges)(presenceCwd, gitPrepared, {
115
+ taskId: flags.get('task') ?? '',
116
+ taskTitle: gitTaskTitle ?? 'task',
117
+ projectSlug: gitProjectSlug,
118
+ agentName: agentId,
119
+ openPr: false,
120
+ policy: gitPolicy,
121
+ allowMainBranchCommit: allowMainCommit,
122
+ log,
123
+ });
124
+ for (const line of safetyResult.lines)
125
+ log(line);
126
+ };
97
127
  try {
98
128
  let taskId = flags.get('task');
99
129
  if (taskId && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(taskId)) {
@@ -120,6 +150,8 @@ async function cmdRun(flags) {
120
150
  reporter.meta.projectId = task.projectId ?? undefined;
121
151
  reporter.meta.taskId = taskId;
122
152
  reporter.connect();
153
+ gitTaskTitle = task.title;
154
+ gitProjectSlug = task.project?.name;
123
155
  const cwd = (0, shared_1.resolveWorkingDir)(flags, task?.projectId ?? null, log);
124
156
  if (!cwd) {
125
157
  exitCode = 1;
@@ -321,6 +353,10 @@ async function cmdRun(flags) {
321
353
  });
322
354
  for (const line of gitResult.lines)
323
355
  log(line);
356
+ // Safety-net commit BEFORE restoring the user's stashed changes, so the
357
+ // commit only captures agent leftovers (usually a no-op once finalize has
358
+ // committed) and never the user's dirty files.
359
+ await maybeSafetyCommit();
324
360
  // Pop stashes (reverse order) — restore user's original changes
325
361
  for (let i = stashResults.length - 1; i >= 0; i--) {
326
362
  stashResults[i] = await (0, git_runner_1.popRepo)(stashResults[i].gitRoot, stashResults[i], cwd, taskId, api, log);
@@ -381,24 +417,10 @@ async function cmdRun(flags) {
381
417
  exitCode = 1;
382
418
  }
383
419
  finally {
384
- // Safety-net commit: if the harness failed or exited early, commit any
385
- // uncommitted agent work to the task branch BEFORE popping stashes. This
386
- // ensures the next run sees a clean tree with the work saved, instead of
387
- // stashing and losing the agent's progress.
388
- if (gitPrepared?.active && presenceCwd && stashResults.some((s) => s.stashed)) {
389
- const taskIdForCommit = flags.get('task') ?? '';
390
- const safetyResult = await (0, git_pipeline_1.commitRemainingChanges)(presenceCwd, gitPrepared, {
391
- taskId: taskIdForCommit,
392
- taskTitle: 'task',
393
- agentName: agentId,
394
- openPr: false,
395
- policy: gitPolicy,
396
- allowMainBranchCommit: allowMainCommit,
397
- log,
398
- });
399
- for (const line of safetyResult.lines)
400
- log(line);
401
- }
420
+ // Safety-net commit (exception path): save uncommitted agent work to the
421
+ // task branch BEFORE restoring the user's stashed changes. No-op if the
422
+ // normal path already committed it, or if a stash has already been popped.
423
+ await maybeSafetyCommit();
402
424
  // Pop stashes — restore user's original changes (only un-popped entries)
403
425
  if (stashResults.length && presenceCwd) {
404
426
  const taskIdForPop = flags.get('task') ?? '';