taskplane 0.24.24 → 0.24.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/taskplane.mjs CHANGED
@@ -1747,9 +1747,9 @@ async function cmdInit(args) {
1747
1747
  } catch {}
1748
1748
  return null;
1749
1749
  })();
1750
- const workspaceTasksRoot = existingWorkspaceJson?.routing?.tasks_root
1750
+ const workspaceTasksRoot = (existingWorkspaceJson?.routing?.tasks_root
1751
1751
  || existingRootYaml?.routing?.tasks_root
1752
- || "taskplane-tasks";
1752
+ || "taskplane-tasks").replace(/\\/g, "/");
1753
1753
  const workspaceDefaultRepo = existingWorkspaceJson?.routing?.default_repo
1754
1754
  || existingRootYaml?.routing?.default_repo
1755
1755
  || configRepo;
@@ -2285,7 +2285,7 @@ function getPresetVars(preset, projectRoot, tasksRootOverride = null) {
2285
2285
  max_lanes: 3,
2286
2286
  worktree_prefix: `${slug}-wt`,
2287
2287
  session_prefix: `${slug}-orch`,
2288
- tasks_root: tasksRootOverride || "taskplane-tasks",
2288
+ tasks_root: (tasksRootOverride || "taskplane-tasks").replace(/\\/g, "/"),
2289
2289
  default_area: "general",
2290
2290
  default_prefix: "TP",
2291
2291
  test_cmd,
@@ -2302,7 +2302,8 @@ async function getInteractiveVars(projectRoot, tasksRootOverride = null) {
2302
2302
  const project_name = await ask("Project name", dirName);
2303
2303
  const maxLanesInput = await ask("Max parallel lanes", "3");
2304
2304
  const max_lanes = parseInt(maxLanesInput, 10) || 3;
2305
- const tasks_root = tasksRootOverride || await ask("Tasks directory", "taskplane-tasks");
2305
+ const tasks_root_raw = tasksRootOverride || await ask("Tasks directory", "taskplane-tasks");
2306
+ const tasks_root = tasks_root_raw.trim().replace(/\\/g, "/").replace(/^\.\//g, "").replace(/\/+$/g, "");
2306
2307
  const default_area = await ask("Default area name", "general");
2307
2308
  const default_prefix = await ask("Task ID prefix", "TP");
2308
2309
  const test_cmd = await ask("Test command (agents run this to verify work — blank to skip)", detected.test || "");
@@ -2908,6 +2908,45 @@ export async function executeOrchBatch(
2908
2908
  return hasSucceeded && hasHardFailure;
2909
2909
  });
2910
2910
 
2911
+ // ── Safety net: auto-commit uncommitted artifacts before merge ──
2912
+ // Workers should commit at step boundaries, but may leave uncommitted
2913
+ // files (especially for level-0 / fast tasks). Check each merge-candidate
2914
+ // lane worktree and auto-commit any remaining changes so they're included
2915
+ // in the merge. Skips lanes with only failed/stalled tasks (no merge).
2916
+ for (const lane of waveResult.allocatedLanes) {
2917
+ if (!lane.worktreePath || !existsSync(lane.worktreePath)) continue;
2918
+ // Only check lanes that have at least one succeeded task (merge candidates)
2919
+ const laneOutcome = laneOutcomeByNumber.get(lane.laneNumber);
2920
+ if (!laneOutcome) continue;
2921
+ const hasSucceeded = laneOutcome.tasks.some(t => t.status === "succeeded");
2922
+ if (!hasSucceeded) continue;
2923
+ try {
2924
+ const addResult = runGit(["add", "-A"], lane.worktreePath);
2925
+ if (!addResult.ok) {
2926
+ execLog("merge", batchState.batchId, `safety-net: git add failed in ${lane.laneId}`, { stderr: addResult.stderr });
2927
+ continue;
2928
+ }
2929
+ const statusResult = runGit(["status", "--porcelain"], lane.worktreePath);
2930
+ if (!statusResult.ok || !statusResult.stdout?.trim()) continue;
2931
+ const taskIds = lane.tasks.map(t => t.taskId).join(", ");
2932
+ const commitResult = runGit(
2933
+ ["commit", "-m", `safety-net: uncommitted artifacts for ${taskIds}`],
2934
+ lane.worktreePath,
2935
+ );
2936
+ if (commitResult.ok) {
2937
+ execLog("merge", batchState.batchId, `safety-net: auto-committed uncommitted files in ${lane.laneId}`, {
2938
+ worktree: lane.worktreePath,
2939
+ taskIds,
2940
+ files: statusResult.stdout.trim(),
2941
+ });
2942
+ } else {
2943
+ execLog("merge", batchState.batchId, `safety-net: commit failed in ${lane.laneId}`, { stderr: commitResult.stderr });
2944
+ }
2945
+ } catch (err: any) {
2946
+ execLog("merge", batchState.batchId, `safety-net: unexpected error in ${lane.laneId}`, { error: err?.message });
2947
+ }
2948
+ }
2949
+
2911
2950
  if (succeededSegmentTaskIdsForMerge.length > 0) {
2912
2951
  const mergeableLaneCount = waveResult.allocatedLanes.filter(lane => {
2913
2952
  const outcome = laneOutcomeByNumber.get(lane.laneNumber);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.24.24",
3
+ "version": "0.24.26",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",