taskplane 0.5.2 → 0.5.4
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.
|
@@ -155,20 +155,30 @@ export function buildLaneEnvVars(
|
|
|
155
155
|
repoRoot: string,
|
|
156
156
|
workspaceRoot?: string,
|
|
157
157
|
): Record<string, string> {
|
|
158
|
-
// TASK_AUTOSTART
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
158
|
+
// TASK_AUTOSTART: resolve the prompt path for the lane session.
|
|
159
|
+
//
|
|
160
|
+
// In workspace mode, tasks may live in a different repo than the lane's
|
|
161
|
+
// worktree (e.g., task PROMPT.md in shared-libs, worker runs in api-service).
|
|
162
|
+
// Always use the absolute path — task-runner's resolve(cwd, autoPath) handles
|
|
163
|
+
// absolute paths correctly, and this avoids broken relative paths when the
|
|
164
|
+
// task folder is outside the lane's repo.
|
|
165
|
+
//
|
|
166
|
+
// In repo mode (no workspace), we still use relative paths from repoRoot
|
|
167
|
+
// because the worktree mirrors the repo structure and the task folder is
|
|
168
|
+
// inside the repo.
|
|
162
169
|
const repoRootNorm = resolve(repoRoot).replace(/\\/g, "/");
|
|
163
170
|
const promptNorm = resolve(promptPath).replace(/\\/g, "/");
|
|
164
171
|
|
|
165
172
|
let relativePath: string;
|
|
166
|
-
if (
|
|
173
|
+
if (workspaceRoot) {
|
|
174
|
+
// Workspace mode: always use absolute path for cross-repo safety
|
|
175
|
+
relativePath = resolve(promptPath);
|
|
176
|
+
} else if (promptNorm.startsWith(repoRootNorm + "/")) {
|
|
177
|
+
// Repo mode: relative path from repo root (mirrors into worktree)
|
|
167
178
|
relativePath = promptNorm.slice(repoRootNorm.length + 1);
|
|
168
179
|
} else {
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
relativePath = promptPath;
|
|
180
|
+
// Fallback: absolute path
|
|
181
|
+
relativePath = resolve(promptPath);
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
const nodePathEntries: string[] = [join(repoRoot, "node_modules")];
|
|
@@ -403,21 +413,26 @@ export function resolveCanonicalTaskPaths(
|
|
|
403
413
|
taskFolder: string,
|
|
404
414
|
worktreePath: string,
|
|
405
415
|
repoRoot: string,
|
|
416
|
+
isWorkspaceMode?: boolean,
|
|
406
417
|
): ResolvedTaskPaths {
|
|
407
418
|
const repoRootNorm = resolve(repoRoot).replace(/\\/g, "/");
|
|
408
419
|
const folderNorm = resolve(taskFolder).replace(/\\/g, "/");
|
|
409
420
|
|
|
410
421
|
let resolvedFolder: string;
|
|
411
422
|
|
|
412
|
-
if (
|
|
413
|
-
//
|
|
423
|
+
if (isWorkspaceMode) {
|
|
424
|
+
// Workspace mode: task folder may live in a different repo than
|
|
425
|
+
// the lane's worktree. Always use the absolute canonical path —
|
|
426
|
+
// .DONE and STATUS.md are written by workers to the original
|
|
427
|
+
// task folder (via absolute TASK_AUTOSTART path), not to the worktree.
|
|
428
|
+
resolvedFolder = resolve(taskFolder);
|
|
429
|
+
} else if (folderNorm.startsWith(repoRootNorm + "/")) {
|
|
430
|
+
// Repo mode: task folder is inside the repo root.
|
|
414
431
|
// Translate to equivalent path in the worktree.
|
|
415
432
|
const relativePath = folderNorm.slice(repoRootNorm.length + 1);
|
|
416
433
|
resolvedFolder = join(worktreePath, relativePath);
|
|
417
434
|
} else {
|
|
418
|
-
//
|
|
419
|
-
// Use the absolute path directly — task state lives in the
|
|
420
|
-
// canonical task folder, not mirrored in any worktree.
|
|
435
|
+
// Fallback: use absolute path directly.
|
|
421
436
|
resolvedFolder = resolve(taskFolder);
|
|
422
437
|
}
|
|
423
438
|
|
|
@@ -474,8 +489,9 @@ export function resolveTaskDonePath(
|
|
|
474
489
|
taskFolder: string,
|
|
475
490
|
worktreePath: string,
|
|
476
491
|
repoRoot: string,
|
|
492
|
+
isWorkspaceMode?: boolean,
|
|
477
493
|
): string {
|
|
478
|
-
return resolveCanonicalTaskPaths(taskFolder, worktreePath, repoRoot).donePath;
|
|
494
|
+
return resolveCanonicalTaskPaths(taskFolder, worktreePath, repoRoot, isWorkspaceMode).donePath;
|
|
479
495
|
}
|
|
480
496
|
|
|
481
497
|
/**
|
|
@@ -603,10 +619,11 @@ export async function pollUntilTaskComplete(
|
|
|
603
619
|
config: OrchestratorConfig,
|
|
604
620
|
repoRoot: string,
|
|
605
621
|
pauseSignal: { paused: boolean },
|
|
622
|
+
isWorkspaceMode?: boolean,
|
|
606
623
|
): Promise<{ status: LaneTaskStatus; exitReason: string; doneFileFound: boolean }> {
|
|
607
624
|
const sessionName = lane.tmuxSessionName;
|
|
608
625
|
const laneId = lane.laneId;
|
|
609
|
-
const resolved = resolveCanonicalTaskPaths(task.task.taskFolder, lane.worktreePath, repoRoot);
|
|
626
|
+
const resolved = resolveCanonicalTaskPaths(task.task.taskFolder, lane.worktreePath, repoRoot, isWorkspaceMode);
|
|
610
627
|
const donePath = resolved.donePath;
|
|
611
628
|
const statusPath = resolved.statusPath;
|
|
612
629
|
const laneLogPath = resolveLaneLogPath(lane, task);
|
|
@@ -822,6 +839,7 @@ export async function executeLane(
|
|
|
822
839
|
repoRoot: string,
|
|
823
840
|
pauseSignal: { paused: boolean },
|
|
824
841
|
workspaceRoot?: string,
|
|
842
|
+
isWorkspaceMode?: boolean,
|
|
825
843
|
): Promise<LaneExecutionResult> {
|
|
826
844
|
const laneId = lane.laneId;
|
|
827
845
|
const laneStartTime = Date.now();
|
|
@@ -867,6 +885,7 @@ export async function executeLane(
|
|
|
867
885
|
config,
|
|
868
886
|
repoRoot,
|
|
869
887
|
pauseSignal,
|
|
888
|
+
isWorkspaceMode,
|
|
870
889
|
);
|
|
871
890
|
|
|
872
891
|
taskOutcome = {
|
|
@@ -993,9 +1012,10 @@ export function parseWorktreeStatusMd(
|
|
|
993
1012
|
taskFolder: string,
|
|
994
1013
|
worktreePath: string,
|
|
995
1014
|
repoRoot: string,
|
|
1015
|
+
isWorkspaceMode?: boolean,
|
|
996
1016
|
): { parsed: ParsedWorktreeStatus | null; error: string | null } {
|
|
997
1017
|
// Use canonical resolver for consistent path translation
|
|
998
|
-
const resolved = resolveCanonicalTaskPaths(taskFolder, worktreePath, repoRoot);
|
|
1018
|
+
const resolved = resolveCanonicalTaskPaths(taskFolder, worktreePath, repoRoot, isWorkspaceMode);
|
|
999
1019
|
const statusPath = resolved.statusPath;
|
|
1000
1020
|
|
|
1001
1021
|
if (!existsSync(statusPath)) {
|
|
@@ -1313,6 +1333,7 @@ export async function monitorLanes(
|
|
|
1313
1333
|
pauseSignal: { paused: boolean },
|
|
1314
1334
|
waveNumber: number = 1,
|
|
1315
1335
|
onUpdate?: MonitorUpdateCallback,
|
|
1336
|
+
isWorkspaceMode?: boolean,
|
|
1316
1337
|
): Promise<MonitorState> {
|
|
1317
1338
|
const pollIntervalMs = (config.monitoring.poll_interval || 5) * 1000;
|
|
1318
1339
|
const stallTimeoutMs = (config.failure.stall_timeout || 30) * 60_000;
|
|
@@ -1402,8 +1423,8 @@ export async function monitorLanes(
|
|
|
1402
1423
|
currentTaskId = task.taskId;
|
|
1403
1424
|
|
|
1404
1425
|
const tracker = getOrCreateTracker(task.taskId, now);
|
|
1405
|
-
const donePath = resolveTaskDonePath(task.task.taskFolder, lane.worktreePath, repoRoot);
|
|
1406
|
-
const statusResult = parseWorktreeStatusMd(task.task.taskFolder, lane.worktreePath, repoRoot);
|
|
1426
|
+
const donePath = resolveTaskDonePath(task.task.taskFolder, lane.worktreePath, repoRoot, isWorkspaceMode);
|
|
1427
|
+
const statusResult = parseWorktreeStatusMd(task.task.taskFolder, lane.worktreePath, repoRoot, isWorkspaceMode);
|
|
1407
1428
|
|
|
1408
1429
|
const snapshot = resolveTaskMonitorState(
|
|
1409
1430
|
task.taskId,
|
|
@@ -1809,8 +1830,9 @@ export async function executeWave(
|
|
|
1809
1830
|
// In workspace mode, pass the workspace root so lane sessions can find .pi/ config.
|
|
1810
1831
|
// configPath is .pi/taskplane-workspace.yaml → parent of parent is workspace root.
|
|
1811
1832
|
const wsRoot = workspaceConfig ? dirname(dirname(workspaceConfig.configPath)) : undefined;
|
|
1833
|
+
const isWsMode = !!workspaceConfig;
|
|
1812
1834
|
const lanePromises = lanes.map(lane =>
|
|
1813
|
-
executeLane(lane, config, repoRoot, wavePauseSignal, wsRoot),
|
|
1835
|
+
executeLane(lane, config, repoRoot, wavePauseSignal, wsRoot, isWsMode),
|
|
1814
1836
|
);
|
|
1815
1837
|
|
|
1816
1838
|
// Start monitoring as a sibling async loop
|
|
@@ -1822,6 +1844,7 @@ export async function executeWave(
|
|
|
1822
1844
|
wavePauseSignal,
|
|
1823
1845
|
waveIndex,
|
|
1824
1846
|
onMonitorUpdate,
|
|
1847
|
+
isWsMode,
|
|
1825
1848
|
);
|
|
1826
1849
|
|
|
1827
1850
|
// ── Stage 4: Wait for all lanes + apply policy ───────────────
|