squadrant 0.13.0 → 0.13.2
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/dist/index.js +145 -27
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +27 -4
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -622,8 +622,41 @@ function resolveWorktreeBase(repoRoot, fallback = "develop") {
|
|
|
622
622
|
return fallback;
|
|
623
623
|
}
|
|
624
624
|
function addWorktree(spec) {
|
|
625
|
-
const
|
|
626
|
-
|
|
625
|
+
const originalBranch = crewBranch(spec.name);
|
|
626
|
+
let targetName = spec.name;
|
|
627
|
+
let targetBranch = originalBranch;
|
|
628
|
+
let branchExists = false;
|
|
629
|
+
try {
|
|
630
|
+
execFileSync2("git", ["-C", spec.repoRoot, "show-ref", "--verify", "--quiet", `refs/heads/${originalBranch}`], { stdio: "pipe" });
|
|
631
|
+
branchExists = true;
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
if (branchExists) {
|
|
635
|
+
const log = execFileSync2("git", ["-C", spec.repoRoot, "log", "--oneline", `${spec.base}..${originalBranch}`], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
636
|
+
if (!log) {
|
|
637
|
+
execFileSync2("git", ["-C", spec.repoRoot, "branch", "-D", originalBranch], { stdio: "pipe" });
|
|
638
|
+
} else {
|
|
639
|
+
let suffix = 2;
|
|
640
|
+
while (true) {
|
|
641
|
+
const candidate = `${spec.name}-${suffix}`;
|
|
642
|
+
const candidateBranch = crewBranch(candidate);
|
|
643
|
+
let candidateExists = false;
|
|
644
|
+
try {
|
|
645
|
+
execFileSync2("git", ["-C", spec.repoRoot, "show-ref", "--verify", "--quiet", `refs/heads/${candidateBranch}`], { stdio: "pipe" });
|
|
646
|
+
candidateExists = true;
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
if (!candidateExists) {
|
|
650
|
+
targetName = candidate;
|
|
651
|
+
targetBranch = candidateBranch;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
suffix++;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const wt = worktreePath(spec.repoRoot, spec.worktreeDir, spec.project, targetName);
|
|
659
|
+
execFileSync2("git", ["-C", spec.repoRoot, "worktree", "add", wt, "-b", targetBranch, spec.base], { stdio: "pipe" });
|
|
627
660
|
return wt;
|
|
628
661
|
}
|
|
629
662
|
function removeWorktree(repoRoot, wtPath) {
|
|
@@ -2454,8 +2487,9 @@ async function runCodexInteractiveSpawn(o) {
|
|
|
2454
2487
|
title
|
|
2455
2488
|
});
|
|
2456
2489
|
await o.runtime.sendToPane(pane, `squadrant crew attach ${rec.id}`);
|
|
2457
|
-
|
|
2458
|
-
|
|
2490
|
+
const firstTurnText = o.firstTurn ?? o.task;
|
|
2491
|
+
if (firstTurnText && firstTurnText !== "(interactive)") {
|
|
2492
|
+
void o.sendCodexFirstTurn(rec.id, firstTurnText).catch((e) => {
|
|
2459
2493
|
process.stderr.write(`(first-turn delivery failed: ${e.message})
|
|
2460
2494
|
`);
|
|
2461
2495
|
});
|
|
@@ -2487,6 +2521,13 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2487
2521
|
name,
|
|
2488
2522
|
base: resolveWorktreeBase(proj.path)
|
|
2489
2523
|
}) : proj.path;
|
|
2524
|
+
let firstTurnTask = input.task;
|
|
2525
|
+
if (input.taskFile && input.taskFile !== "-" && !input.shared) {
|
|
2526
|
+
const absTaskFile = path10.resolve(input.taskFile);
|
|
2527
|
+
const basename = path10.basename(absTaskFile);
|
|
2528
|
+
fs12.copyFileSync(absTaskFile, path10.join(spawnCwd, basename));
|
|
2529
|
+
firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
|
|
2530
|
+
}
|
|
2490
2531
|
const route = !input.agentExplicit && !input.model ? resolveCrewRoute(input.task, config) : null;
|
|
2491
2532
|
if (route) {
|
|
2492
2533
|
deps.onRouted?.(route);
|
|
@@ -2502,6 +2543,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2502
2543
|
return runCodexInteractiveSpawn({
|
|
2503
2544
|
project: input.project,
|
|
2504
2545
|
task: input.task,
|
|
2546
|
+
firstTurn: firstTurnTask !== input.task ? firstTurnTask : void 0,
|
|
2505
2547
|
cwd: spawnCwd,
|
|
2506
2548
|
runtime: deps.runtime,
|
|
2507
2549
|
workspaceId: captain.id,
|
|
@@ -2545,9 +2587,15 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2545
2587
|
const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
|
|
2546
2588
|
await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} ${cliCommand2}`);
|
|
2547
2589
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
|
|
2548
|
-
await deps.sendFirstTurn(pane2, `${
|
|
2590
|
+
const claudeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
|
|
2549
2591
|
|
|
2550
2592
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
2593
|
+
if (!claudeResult.delivered) {
|
|
2594
|
+
process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
|
|
2595
|
+
`);
|
|
2596
|
+
} else {
|
|
2597
|
+
await deps.emitEvent?.(input.project, { type: "task.first-turn.confirmed", id: rec.id });
|
|
2598
|
+
}
|
|
2551
2599
|
return { ...pane2, title: title2 };
|
|
2552
2600
|
}
|
|
2553
2601
|
if (agentName === "opencode") {
|
|
@@ -2587,7 +2635,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
|
2587
2635
|
const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
|
|
2588
2636
|
await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} OPENCODE_CONFIG=${opencodeConfigPath} ${cliCommand2}`);
|
|
2589
2637
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
|
|
2590
|
-
await deps.sendFirstTurn(pane2, `${
|
|
2638
|
+
const opencodeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
|
|
2591
2639
|
|
|
2592
2640
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
2593
2641
|
// #235: confirm-on-delivery — sendFirstTurnWhenReady polls until "Ask
|
|
@@ -2595,6 +2643,12 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
|
2595
2643
|
// without duplicating the task. See crew-pane.ts SPLASH_MAX_CHECKS/EVERY_N.
|
|
2596
2644
|
splashMarker: "Ask anything\u2026"
|
|
2597
2645
|
});
|
|
2646
|
+
if (!opencodeResult.delivered) {
|
|
2647
|
+
process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
|
|
2648
|
+
`);
|
|
2649
|
+
} else {
|
|
2650
|
+
await deps.emitEvent?.(input.project, { type: "task.first-turn.confirmed", id: rec.id });
|
|
2651
|
+
}
|
|
2598
2652
|
return { ...pane2, title: title2 };
|
|
2599
2653
|
}
|
|
2600
2654
|
const cliCommand = agent.buildCommand({
|
|
@@ -2611,7 +2665,11 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
|
2611
2665
|
await deps.runtime.sendToPane(pane, cliCommand);
|
|
2612
2666
|
if (interactive) {
|
|
2613
2667
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane) ?? "";
|
|
2614
|
-
await deps.sendFirstTurn(pane,
|
|
2668
|
+
const genericResult = await deps.sendFirstTurn(pane, firstTurnTask, preLaunchScreen);
|
|
2669
|
+
if (!genericResult.delivered) {
|
|
2670
|
+
process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
|
|
2671
|
+
`);
|
|
2672
|
+
}
|
|
2615
2673
|
}
|
|
2616
2674
|
return { ...pane, title };
|
|
2617
2675
|
}
|
|
@@ -2632,8 +2690,12 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
|
2632
2690
|
}
|
|
2633
2691
|
} catch {
|
|
2634
2692
|
}
|
|
2635
|
-
const deliver = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg));
|
|
2636
|
-
await deliver(crew, message);
|
|
2693
|
+
const deliver = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg).then(() => ({ delivered: true })));
|
|
2694
|
+
const { delivered } = await deliver(crew, message);
|
|
2695
|
+
if (!delivered) {
|
|
2696
|
+
process.stderr.write(`\u26A0\uFE0F Message not delivered to crew '${name}' \u2014 use 'squadrant crew send ${project} ${name}' to re-send.
|
|
2697
|
+
`);
|
|
2698
|
+
}
|
|
2637
2699
|
}
|
|
2638
2700
|
async function runCrewRead(project, name, runtime, workspaceId) {
|
|
2639
2701
|
const crew = await findCrewPane(runtime, workspaceId, project, name);
|
|
@@ -3911,13 +3973,18 @@ var init_native_hook_source = __esm({
|
|
|
3911
3973
|
import net from "net";
|
|
3912
3974
|
async function settleInputBox(runtime, pane) {
|
|
3913
3975
|
let prev = await runtime.readPaneScreen(pane) ?? "";
|
|
3976
|
+
let sawContent = parseDraftFromScreen(prev) !== "" && parseDraftFromScreen(prev) !== null;
|
|
3914
3977
|
for (let i = 0; i < SETTLE_MAX_POLLS; i++) {
|
|
3915
3978
|
await new Promise((r) => setTimeout(r, SETTLE_POLL_MS));
|
|
3916
3979
|
const cur = await runtime.readPaneScreen(pane) ?? "";
|
|
3980
|
+
const draft = parseDraftFromScreen(cur);
|
|
3981
|
+
if (draft !== "" && draft !== null)
|
|
3982
|
+
sawContent = true;
|
|
3917
3983
|
if (cur === prev)
|
|
3918
|
-
return;
|
|
3984
|
+
return sawContent;
|
|
3919
3985
|
prev = cur;
|
|
3920
3986
|
}
|
|
3987
|
+
return sawContent;
|
|
3921
3988
|
}
|
|
3922
3989
|
function getFreePort() {
|
|
3923
3990
|
return new Promise((resolve3, reject) => {
|
|
@@ -3955,19 +4022,29 @@ async function resolveCaptainWorkspace(project) {
|
|
|
3955
4022
|
async function confirmedSendToPane(runtime, pane, message) {
|
|
3956
4023
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3957
4024
|
await runtime.pasteToPane(pane, message);
|
|
3958
|
-
await settleInputBox(runtime, pane);
|
|
4025
|
+
let sawDraft = await settleInputBox(runtime, pane);
|
|
3959
4026
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
4027
|
+
let repasted = false;
|
|
3960
4028
|
for (let attempt = 0; attempt < SUBMIT_RETRY_LIMIT; attempt++) {
|
|
3961
4029
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3962
4030
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3963
4031
|
const draft = parseDraftFromScreen(afterScreen);
|
|
3964
|
-
if (draft
|
|
3965
|
-
|
|
4032
|
+
if (draft !== "" && draft !== null)
|
|
4033
|
+
sawDraft = true;
|
|
4034
|
+
if (draft === "" && sawDraft)
|
|
4035
|
+
return { delivered: true };
|
|
3966
4036
|
if (draft === null && afterScreen !== preSendScreen)
|
|
3967
|
-
return;
|
|
3968
|
-
await settleInputBox(runtime, pane);
|
|
4037
|
+
return { delivered: true };
|
|
4038
|
+
const settled = await settleInputBox(runtime, pane);
|
|
4039
|
+
if (settled)
|
|
4040
|
+
sawDraft = true;
|
|
4041
|
+
if (!sawDraft && !repasted) {
|
|
4042
|
+
repasted = true;
|
|
4043
|
+
await runtime.pasteToPane(pane, message);
|
|
4044
|
+
}
|
|
3969
4045
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
3970
4046
|
}
|
|
4047
|
+
return { delivered: false };
|
|
3971
4048
|
}
|
|
3972
4049
|
async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acceptanceConfig) {
|
|
3973
4050
|
await new Promise((r) => setTimeout(r, SEND_FIRST_TURN_FLOOR_MS));
|
|
@@ -3976,7 +4053,8 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
3976
4053
|
let stable = false;
|
|
3977
4054
|
for (let i = 0; i < maxPolls && !stable; i++) {
|
|
3978
4055
|
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
3979
|
-
|
|
4056
|
+
const hasInputBox = !!acceptanceConfig?.splashMarker || parseDraftFromScreen(screen) !== null;
|
|
4057
|
+
if (screen.length > 0 && screen === previousScreen && screen !== preLaunchScreen && hasInputBox) {
|
|
3980
4058
|
stable = true;
|
|
3981
4059
|
} else {
|
|
3982
4060
|
previousScreen = screen;
|
|
@@ -3990,29 +4068,45 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
3990
4068
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3991
4069
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3992
4070
|
if (isTurnAccepted(preSendScreen, afterScreen, acceptanceConfig)) {
|
|
3993
|
-
return;
|
|
4071
|
+
return { delivered: true };
|
|
3994
4072
|
}
|
|
3995
4073
|
if ((check2 + 1) % SPLASH_RESEND_EVERY_N === 0 && check2 < SPLASH_MAX_CHECKS - 1) {
|
|
3996
4074
|
await runtime.sendToPane(pane, task);
|
|
3997
4075
|
}
|
|
3998
4076
|
}
|
|
3999
|
-
return;
|
|
4077
|
+
return { delivered: false };
|
|
4078
|
+
}
|
|
4079
|
+
if (!stable) {
|
|
4080
|
+
return confirmedSendToPane(runtime, pane, task);
|
|
4000
4081
|
}
|
|
4001
4082
|
await runtime.pasteToPane(pane, task);
|
|
4002
|
-
await settleInputBox(runtime, pane);
|
|
4083
|
+
let sawDraft = await settleInputBox(runtime, pane);
|
|
4003
4084
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
4004
4085
|
const retryLimit = acceptanceConfig?.retryLimit ?? SUBMIT_RETRY_LIMIT;
|
|
4086
|
+
let repasted = false;
|
|
4005
4087
|
for (let attempt = 0; attempt < retryLimit; attempt++) {
|
|
4006
4088
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
4007
4089
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
4008
4090
|
const draft = parseDraftFromScreen(afterScreen);
|
|
4009
|
-
if (draft
|
|
4010
|
-
|
|
4091
|
+
if (draft !== "" && draft !== null)
|
|
4092
|
+
sawDraft = true;
|
|
4093
|
+
if (draft === "" && sawDraft)
|
|
4094
|
+
return { delivered: true };
|
|
4011
4095
|
if (draft === null && afterScreen !== preSendScreen)
|
|
4012
|
-
return;
|
|
4013
|
-
await settleInputBox(runtime, pane);
|
|
4096
|
+
return { delivered: true };
|
|
4097
|
+
const settled = await settleInputBox(runtime, pane);
|
|
4098
|
+
if (settled)
|
|
4099
|
+
sawDraft = true;
|
|
4100
|
+
if (!sawDraft && !repasted) {
|
|
4101
|
+
repasted = true;
|
|
4102
|
+
await runtime.pasteToPane(pane, task);
|
|
4103
|
+
}
|
|
4014
4104
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
4015
4105
|
}
|
|
4106
|
+
if (!sawDraft) {
|
|
4107
|
+
return confirmedSendToPane(runtime, pane, task);
|
|
4108
|
+
}
|
|
4109
|
+
return { delivered: false };
|
|
4016
4110
|
}
|
|
4017
4111
|
var SEND_FIRST_TURN_FLOOR_MS, POLL_INTERVAL_MS, SEND_FIRST_TURN_TIMEOUT_MS, POST_SEND_CHECK_MS, SPLASH_MAX_CHECKS, SPLASH_RESEND_EVERY_N, SETTLE_POLL_MS, SETTLE_MAX_POLLS, SUBMIT_RETRY_LIMIT;
|
|
4018
4112
|
var init_crew_pane = __esm({
|
|
@@ -5806,8 +5900,13 @@ function classifyPaneTail(tail) {
|
|
|
5806
5900
|
if (c != null && ERROR_BANNER_RE.some((re) => re.test(c)))
|
|
5807
5901
|
errLine = c;
|
|
5808
5902
|
}
|
|
5809
|
-
if (errLine)
|
|
5903
|
+
if (errLine) {
|
|
5904
|
+
const isRetrying = cleaned.some((c) => c != null && RETRYING_RE.test(c));
|
|
5905
|
+
const isExhausted = cleaned.some((c) => c != null && EXHAUSTED_RE.test(c));
|
|
5906
|
+
if (isRetrying && !isExhausted)
|
|
5907
|
+
return null;
|
|
5810
5908
|
return { kind: "error", text: errLine.slice(0, 200) };
|
|
5909
|
+
}
|
|
5811
5910
|
return null;
|
|
5812
5911
|
}
|
|
5813
5912
|
function stripChrome(raw) {
|
|
@@ -5824,7 +5923,7 @@ function stripChrome(raw) {
|
|
|
5824
5923
|
return null;
|
|
5825
5924
|
return trimmed;
|
|
5826
5925
|
}
|
|
5827
|
-
var ERROR_BANNER_RE, OPTION_RE, PICKER_FOOTER_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5926
|
+
var ERROR_BANNER_RE, RETRYING_RE, EXHAUSTED_RE, OPTION_RE, PICKER_FOOTER_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5828
5927
|
var init_pane_classifier = __esm({
|
|
5829
5928
|
"packages/agents/dist/interactive/pane-classifier.js"() {
|
|
5830
5929
|
init_claude2();
|
|
@@ -5837,6 +5936,8 @@ var init_pane_classifier = __esm({
|
|
|
5837
5936
|
/\bretr(?:y|ies)\s+(?:exhausted|limit\s+(?:reached|exceeded))\b/i,
|
|
5838
5937
|
/\bmaximum\s+retries\b/i
|
|
5839
5938
|
];
|
|
5939
|
+
RETRYING_RE = /\bRetrying\b|\battempt\s+\d+\s*\/\s*\d+/i;
|
|
5940
|
+
EXHAUSTED_RE = /\bretr(?:y|ies)\s+(?:exhausted|limit\s+(?:reached|exceeded))\b|\bmaximum\s+retries\b/i;
|
|
5840
5941
|
OPTION_RE = /^[❯>›]?\s*(\d+)\.\s+(.*\S)\s*$/;
|
|
5841
5942
|
PICKER_FOOTER_RE = /↑↓\s*select|enter\s+submit|esc\s+dismiss/i;
|
|
5842
5943
|
PURE_CHROME_RE = /^[\s─━│┃╭╮╰╯┌┐└┘├┤┬┴┼═║╔╗╚╝╠╣╦╩╬▁▂▃▄▅▆▇█▔▏▕]+$/;
|
|
@@ -6979,6 +7080,7 @@ import chalk9 from "chalk";
|
|
|
6979
7080
|
// packages/cli/src/commands/crew-control.ts
|
|
6980
7081
|
init_dist2();
|
|
6981
7082
|
init_dist2();
|
|
7083
|
+
init_dist();
|
|
6982
7084
|
init_dist4();
|
|
6983
7085
|
import { Command as Command8 } from "commander";
|
|
6984
7086
|
import { createConnection as createConnection3 } from "net";
|
|
@@ -7404,7 +7506,16 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
7404
7506
|
const r = await squadrantdCall(buildStatusRequest(project, id));
|
|
7405
7507
|
process.stdout.write(JSON.stringify(r) + "\n");
|
|
7406
7508
|
});
|
|
7407
|
-
crew.command("tasks <project>").description("List control-plane tasks for a project (control-plane analogue of legacy `list`), or purge a task with --purge").option("--json", "Full JSON output (one or more records)").option("--id <taskId>", "Show only tasks matching this id prefix").option("--state <state>", "Filter by task state").option("--state-only <taskId>", "Print just the state string for a single task").option("--purge <taskId>", "Purge a task record from the store (default: only terminal records)").option("--force", "Force-purge a non-terminal record (use with --purge)").action(async (project, opts) => {
|
|
7509
|
+
crew.command("tasks <project>").description("List control-plane tasks for a project (control-plane analogue of legacy `list`), or purge a task with --purge").option("--json", "Full JSON output (one or more records)").option("--id <taskId>", "Show only tasks matching this id prefix").option("--state <state>", "Filter by task state").option("--state-only <taskId>", "Print just the state string for a single task").option("--purge <taskId>", "Purge a task record from the store (default: only terminal records)").option("--force", "Force-purge a non-terminal record (use with --purge)").option("--all-terminal", "Purge all terminal (done/cancelled/failed) records for the project").action(async (project, opts) => {
|
|
7510
|
+
if (opts.allTerminal) {
|
|
7511
|
+
const tasks = await squadrantdCall({ kind: "list", project });
|
|
7512
|
+
const terminal = tasks.filter((t) => TERMINAL_STATES.has(t.state));
|
|
7513
|
+
for (const t of terminal) {
|
|
7514
|
+
await squadrantdCall({ kind: "purge", project, id: t.id, force: false });
|
|
7515
|
+
}
|
|
7516
|
+
console.log(`purged ${terminal.length} terminal record(s)`);
|
|
7517
|
+
return;
|
|
7518
|
+
}
|
|
7408
7519
|
if (opts.purge) {
|
|
7409
7520
|
const r = await squadrantdCall({ kind: "purge", project, id: opts.purge, force: opts.force ?? false });
|
|
7410
7521
|
console.log(`purged ${r.provider}/${r.id} (was ${r.state})`);
|
|
@@ -7642,6 +7753,10 @@ async function runCrewSpawn2(input) {
|
|
|
7642
7753
|
sendFirstTurn: (pane, firstTurn, preLaunchScreen, opts) => sendFirstTurnWhenReady(runtime, pane, firstTurn, preLaunchScreen, opts),
|
|
7643
7754
|
getFreePort,
|
|
7644
7755
|
sendCodexFirstTurn,
|
|
7756
|
+
// #466: wire delivery confirmation so the daemon stamps firstTurnConfirmedAt.
|
|
7757
|
+
emitEvent: async (p, event) => {
|
|
7758
|
+
await squadrantdCall({ kind: "event", project: p, event });
|
|
7759
|
+
},
|
|
7645
7760
|
onRouted: (route) => console.log(
|
|
7646
7761
|
chalk9.dim(
|
|
7647
7762
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
@@ -7702,7 +7817,10 @@ crewCommand.command("spawn").description(
|
|
|
7702
7817
|
// opencode consumes the `approval` flag (→ bash:"ask" per-crew config).
|
|
7703
7818
|
...opts.approval ? { approvalPolicy: "untrusted", approval: true } : {},
|
|
7704
7819
|
...opts.shared ? { shared: true } : {},
|
|
7705
|
-
...opts.model ? { model: opts.model } : {}
|
|
7820
|
+
...opts.model ? { model: opts.model } : {},
|
|
7821
|
+
// #458: pass the raw file path (not stdin) so runCrewSpawn can copy it
|
|
7822
|
+
// into the isolated worktree root for relative-path access.
|
|
7823
|
+
...opts.taskFile && opts.taskFile !== "-" ? { taskFile: opts.taskFile } : {}
|
|
7706
7824
|
});
|
|
7707
7825
|
console.log(chalk9.green(`\u2714 Crew '${pane.title}' spawned (${pane.surfaceId})`));
|
|
7708
7826
|
} catch (err) {
|