squadrant 0.26.2 → 0.26.3
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 +519 -315
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +164 -2
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5590,9 +5590,13 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
5590
5590
|
await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix}${routerPrefix} ${niceCrewCommand(cliCommand2)}`);
|
|
5591
5591
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
|
|
5592
5592
|
const claudeFirstTurn = spillOversizedFirstTurn(firstTurnTask, rec.id);
|
|
5593
|
-
const
|
|
5593
|
+
const peerBrief = `${firstTurnTask}
|
|
5594
5594
|
|
|
5595
|
-
${buildCompletionProtocol(rec.id, input.project)}
|
|
5595
|
+
${buildCompletionProtocol(rec.id, input.project)}`;
|
|
5596
|
+
const peerResult = deps.sendFirstTurnPeer ? await deps.sendFirstTurnPeer({ pane: pane2, taskId: rec.id, project: input.project, messagingSocketPath, message: peerBrief }).catch(() => ({ kind: "fallback" })) : { kind: "fallback" };
|
|
5597
|
+
const sendPromise = peerResult.kind === "fallback" ? deps.sendFirstTurn(pane2, `${claudeFirstTurn}
|
|
5598
|
+
|
|
5599
|
+
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen) : Promise.resolve({ delivered: peerResult.kind === "delivered" });
|
|
5596
5600
|
let agentNotStarted = false;
|
|
5597
5601
|
const scrapeDelivered = sendPromise.then((r) => {
|
|
5598
5602
|
agentNotStarted = !!r.agentNotStarted;
|
|
@@ -9817,6 +9821,30 @@ async function runCrewAnswer(project, name, option, runtime, workspaceId, deps,
|
|
|
9817
9821
|
if (!crew) {
|
|
9818
9822
|
throw new Error(`Crew '${name}' not found for ${project}. Run 'squadrant crew list ${project}'.`);
|
|
9819
9823
|
}
|
|
9824
|
+
const match = runtime.findDialog && runtime.answerDialog ? await deps.dialogMatch?.() : void 0;
|
|
9825
|
+
if (match) {
|
|
9826
|
+
const dialog = await runtime.findDialog(match).catch(() => void 0);
|
|
9827
|
+
if (dialog) {
|
|
9828
|
+
const log2 = deps.log ?? (() => {
|
|
9829
|
+
});
|
|
9830
|
+
const options2 = dialog.options.map((o) => ({ ...o, highlighted: false }));
|
|
9831
|
+
let target2;
|
|
9832
|
+
try {
|
|
9833
|
+
target2 = resolveOption(options2, option);
|
|
9834
|
+
if (opts?.expect && !target2.label.toLowerCase().includes(opts.expect.toLowerCase()))
|
|
9835
|
+
target2 = void 0;
|
|
9836
|
+
} catch {
|
|
9837
|
+
target2 = void 0;
|
|
9838
|
+
}
|
|
9839
|
+
if (target2) {
|
|
9840
|
+
log2(`\u2192 answering ${target2.index}. "${target2.label}" via the runtime (no keystrokes)`);
|
|
9841
|
+
const ok2 = await runtime.answerDialog(dialog, opts?.text ? { text: opts.text } : { label: target2.label }).catch(() => false);
|
|
9842
|
+
if (ok2)
|
|
9843
|
+
return { selected: target2, closed: true };
|
|
9844
|
+
}
|
|
9845
|
+
log2("runtime answer not applied \u2014 falling back to keystrokes");
|
|
9846
|
+
}
|
|
9847
|
+
}
|
|
9820
9848
|
const modal = await deps.readModalOptions(crew);
|
|
9821
9849
|
if (!modal || modal.options.length === 0) {
|
|
9822
9850
|
throw new Error(`Crew '${name}' has no interactive option prompt visible right now \u2014 nothing to answer. Read its screen with 'squadrant crew read ${project} ${name}' to check its state.`);
|
|
@@ -10196,6 +10224,58 @@ var init_source = __esm({
|
|
|
10196
10224
|
}
|
|
10197
10225
|
});
|
|
10198
10226
|
|
|
10227
|
+
// packages/core/dist/peer-first-turn.js
|
|
10228
|
+
function screenShowsTrustPanel(screen) {
|
|
10229
|
+
return /do you trust the files in this folder|is this a project you created or one you trust|is this a project you trust/i.test(screen);
|
|
10230
|
+
}
|
|
10231
|
+
function trustPanelAcceptKeys(screen) {
|
|
10232
|
+
return /❯\s*(\d\.\s*)?No\b/.test(screen) ? ["Down", "Enter"] : ["Enter"];
|
|
10233
|
+
}
|
|
10234
|
+
async function deliverFirstTurnViaPeer(message, deps) {
|
|
10235
|
+
const pollMs = deps.pollMs ?? DEFAULT_POLL_MS2;
|
|
10236
|
+
const deadline = deps.now() + (deps.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS);
|
|
10237
|
+
let lastTrustAnswer = -Infinity;
|
|
10238
|
+
for (; ; ) {
|
|
10239
|
+
if (deps.statusOf()?.status === "idle")
|
|
10240
|
+
break;
|
|
10241
|
+
if (deps.now() >= deadline)
|
|
10242
|
+
return { kind: "fallback" };
|
|
10243
|
+
if (!deps.statusOf() && deps.now() - lastTrustAnswer >= TRUST_ANSWER_COOLDOWN_MS) {
|
|
10244
|
+
const screen = await deps.readScreen().catch(() => "");
|
|
10245
|
+
if (screenShowsTrustPanel(screen)) {
|
|
10246
|
+
lastTrustAnswer = deps.now();
|
|
10247
|
+
for (const key of trustPanelAcceptKeys(screen))
|
|
10248
|
+
await deps.pressKey(key).catch(() => {
|
|
10249
|
+
});
|
|
10250
|
+
}
|
|
10251
|
+
}
|
|
10252
|
+
await deps.sleep(pollMs);
|
|
10253
|
+
}
|
|
10254
|
+
const outcome = await deps.send(message);
|
|
10255
|
+
if (fallsBackToPane(outcome))
|
|
10256
|
+
return { kind: "fallback" };
|
|
10257
|
+
if (outcome.status === "accepted" && outcome.confirmed)
|
|
10258
|
+
return { kind: "delivered" };
|
|
10259
|
+
const confirmDeadline = deps.now() + (deps.confirmGraceMs ?? DEFAULT_CONFIRM_GRACE_MS);
|
|
10260
|
+
for (; ; ) {
|
|
10261
|
+
if (await deps.isConfirmed().catch(() => false))
|
|
10262
|
+
return { kind: "delivered" };
|
|
10263
|
+
if (deps.now() >= confirmDeadline)
|
|
10264
|
+
return { kind: "unconfirmed" };
|
|
10265
|
+
await deps.sleep(pollMs);
|
|
10266
|
+
}
|
|
10267
|
+
}
|
|
10268
|
+
var DEFAULT_READY_TIMEOUT_MS, DEFAULT_POLL_MS2, DEFAULT_CONFIRM_GRACE_MS, TRUST_ANSWER_COOLDOWN_MS;
|
|
10269
|
+
var init_peer_first_turn = __esm({
|
|
10270
|
+
"packages/core/dist/peer-first-turn.js"() {
|
|
10271
|
+
init_control_channel();
|
|
10272
|
+
DEFAULT_READY_TIMEOUT_MS = 9e4;
|
|
10273
|
+
DEFAULT_POLL_MS2 = 250;
|
|
10274
|
+
DEFAULT_CONFIRM_GRACE_MS = 1e4;
|
|
10275
|
+
TRUST_ANSWER_COOLDOWN_MS = 5e3;
|
|
10276
|
+
}
|
|
10277
|
+
});
|
|
10278
|
+
|
|
10199
10279
|
// packages/core/dist/knowledge/paths.js
|
|
10200
10280
|
import path14 from "path";
|
|
10201
10281
|
function kbDir(hubVault, kb) {
|
|
@@ -13915,6 +13995,7 @@ __export(dist_exports2, {
|
|
|
13915
13995
|
defaultIsPidAlive: () => defaultIsPidAlive,
|
|
13916
13996
|
defaultListenError: () => defaultListenError,
|
|
13917
13997
|
defaultWorkRoot: () => defaultWorkRoot,
|
|
13998
|
+
deliverFirstTurnViaPeer: () => deliverFirstTurnViaPeer,
|
|
13918
13999
|
deliverStartupPrompt: () => deliverStartupPrompt,
|
|
13919
14000
|
deliverToCaptain: () => deliverToCaptain,
|
|
13920
14001
|
deliverable: () => deliverable,
|
|
@@ -14096,6 +14177,7 @@ __export(dist_exports2, {
|
|
|
14096
14177
|
saveState: () => saveState,
|
|
14097
14178
|
screenHasSplashMarker: () => screenHasSplashMarker,
|
|
14098
14179
|
screenHash: () => screenHash,
|
|
14180
|
+
screenShowsTrustPanel: () => screenShowsTrustPanel,
|
|
14099
14181
|
searchRules: () => searchRules,
|
|
14100
14182
|
selectPromptHits: () => selectPromptHits,
|
|
14101
14183
|
sendRequest: () => sendRequest,
|
|
@@ -14121,6 +14203,7 @@ __export(dist_exports2, {
|
|
|
14121
14203
|
toControlEvent: () => toControlEvent,
|
|
14122
14204
|
topicKey: () => topicKey,
|
|
14123
14205
|
topicName: () => topicName,
|
|
14206
|
+
trustPanelAcceptKeys: () => trustPanelAcceptKeys,
|
|
14124
14207
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
14125
14208
|
usageFromJson: () => usageFromJson,
|
|
14126
14209
|
validateKb: () => validateKb,
|
|
@@ -14183,6 +14266,7 @@ var init_dist2 = __esm({
|
|
|
14183
14266
|
init_conformance();
|
|
14184
14267
|
init_source();
|
|
14185
14268
|
init_control_channel();
|
|
14269
|
+
init_peer_first_turn();
|
|
14186
14270
|
init_captain_channel();
|
|
14187
14271
|
init_captain_record();
|
|
14188
14272
|
init_opencode_session();
|
|
@@ -17582,6 +17666,15 @@ async function cmuxHasInputGuards() {
|
|
|
17582
17666
|
inputGuardsCache = major > 0 || minor >= 65;
|
|
17583
17667
|
return inputGuardsCache;
|
|
17584
17668
|
}
|
|
17669
|
+
function feedSessionOf(item) {
|
|
17670
|
+
const parts = item.workstream_id?.split(":");
|
|
17671
|
+
if (parts?.[0] !== "cmux-feed-v1" || !parts[2])
|
|
17672
|
+
return void 0;
|
|
17673
|
+
return Buffer.from(parts[2], "base64").toString("utf-8");
|
|
17674
|
+
}
|
|
17675
|
+
async function feedRpc(method, params) {
|
|
17676
|
+
return JSON.parse(await cmux(["rpc", method, JSON.stringify(params)]));
|
|
17677
|
+
}
|
|
17585
17678
|
function parseList(output) {
|
|
17586
17679
|
let parsed;
|
|
17587
17680
|
try {
|
|
@@ -18005,6 +18098,26 @@ function createCmuxDriver() {
|
|
|
18005
18098
|
await cmux(["send-key", "--workspace", wsId, "--surface", surfaceId, "Enter"]);
|
|
18006
18099
|
return { workspaceId: wsId, surfaceId, title: opts.title };
|
|
18007
18100
|
},
|
|
18101
|
+
// #916: cmux >= 0.65 `surface.input_state` reads the render grid (incl. faint
|
|
18102
|
+
// spans) without touching the pane. The RPC needs a surface UUID, and an
|
|
18103
|
+
// unknown ref would silently fall back to the focused surface, so resolve the
|
|
18104
|
+
// ref through the tree first. Anything unexpected → undefined (old path).
|
|
18105
|
+
async inputState(pane) {
|
|
18106
|
+
try {
|
|
18107
|
+
if (!await cmuxHasInputGuards())
|
|
18108
|
+
return void 0;
|
|
18109
|
+
const tree = JSON.parse(await cmux(["tree", "--workspace", pane.workspaceId, "--json", "--id-format", "both"]));
|
|
18110
|
+
const id = tree.windows?.flatMap((w) => w.workspaces ?? []).flatMap((w) => w.panes ?? []).flatMap((p) => p.surfaces ?? []).find((s) => s.ref === pane.surfaceId)?.id;
|
|
18111
|
+
if (!id)
|
|
18112
|
+
return void 0;
|
|
18113
|
+
const r = JSON.parse(await cmux(["rpc", "surface.input_state", JSON.stringify({ surface_id: id })]));
|
|
18114
|
+
if (r.agent !== true)
|
|
18115
|
+
return "unknown";
|
|
18116
|
+
return r.state === "empty" || r.state === "draft" || r.state === "dialog" ? r.state : "unknown";
|
|
18117
|
+
} catch {
|
|
18118
|
+
return void 0;
|
|
18119
|
+
}
|
|
18120
|
+
},
|
|
18008
18121
|
async sendToSurface(surface, text, opts) {
|
|
18009
18122
|
const ws = surface.workspaceId;
|
|
18010
18123
|
const sf = surface.surfaceId;
|
|
@@ -18031,6 +18144,17 @@ function createCmuxDriver() {
|
|
|
18031
18144
|
`);
|
|
18032
18145
|
}
|
|
18033
18146
|
};
|
|
18147
|
+
if (opts?.agent !== "opencode") {
|
|
18148
|
+
const state = await this.inputState?.(surface);
|
|
18149
|
+
if (state === "draft")
|
|
18150
|
+
throw new DeferDelivery(null, "draft");
|
|
18151
|
+
if (state === "dialog")
|
|
18152
|
+
throw new DeferDelivery(null, "modal");
|
|
18153
|
+
if (state === "empty") {
|
|
18154
|
+
await deliver();
|
|
18155
|
+
return;
|
|
18156
|
+
}
|
|
18157
|
+
}
|
|
18034
18158
|
let screen = "";
|
|
18035
18159
|
try {
|
|
18036
18160
|
screen = await cmux(["read-screen", "--workspace", ws, "--surface", sf]);
|
|
@@ -18109,6 +18233,39 @@ function createCmuxDriver() {
|
|
|
18109
18233
|
args.push("--focus", "true");
|
|
18110
18234
|
await cmux(args);
|
|
18111
18235
|
},
|
|
18236
|
+
// Feed-backed structured answer (#918). cmux >= 0.65 only; any miss
|
|
18237
|
+
// (old cmux, no matching item, unsupported shape) is undefined so the
|
|
18238
|
+
// caller keeps the keystroke path.
|
|
18239
|
+
async findDialog(match) {
|
|
18240
|
+
if (!await cmuxHasInputGuards())
|
|
18241
|
+
return void 0;
|
|
18242
|
+
let items;
|
|
18243
|
+
try {
|
|
18244
|
+
items = (await feedRpc("feed.list", { pending_only: true })).items ?? [];
|
|
18245
|
+
} catch {
|
|
18246
|
+
return void 0;
|
|
18247
|
+
}
|
|
18248
|
+
const mine = items.filter((i) => match.agentSessionId ? feedSessionOf(i) === match.agentSessionId : !!match.cwd && i.cwd === match.cwd);
|
|
18249
|
+
if (mine.length !== 1 || !mine[0].request_id)
|
|
18250
|
+
return void 0;
|
|
18251
|
+
const item = mine[0];
|
|
18252
|
+
if (item.kind !== "question" || item.questions?.length !== 1)
|
|
18253
|
+
return void 0;
|
|
18254
|
+
const labels = (item.questions[0].options ?? []).map((o) => o.label ?? "");
|
|
18255
|
+
if (labels.length === 0)
|
|
18256
|
+
return void 0;
|
|
18257
|
+
return {
|
|
18258
|
+
id: item.request_id,
|
|
18259
|
+
options: labels.map((label, i) => ({ index: i + 1, label }))
|
|
18260
|
+
};
|
|
18261
|
+
},
|
|
18262
|
+
async answerDialog(dialog, answer) {
|
|
18263
|
+
const reply = answer.text ?? answer.label;
|
|
18264
|
+
if (!reply)
|
|
18265
|
+
return false;
|
|
18266
|
+
const res = await feedRpc("feed.question.reply", { request_id: dialog.id, selections: [reply] });
|
|
18267
|
+
return res.delivered === true;
|
|
18268
|
+
},
|
|
18112
18269
|
async showPatch(opts) {
|
|
18113
18270
|
const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
|
|
18114
18271
|
if (opts.title)
|
|
@@ -19423,6 +19580,11 @@ async function readModalOptions(runtime, pane) {
|
|
|
19423
19580
|
return parseModal(screen);
|
|
19424
19581
|
}
|
|
19425
19582
|
async function confirmedSendToPane(runtime, pane, message) {
|
|
19583
|
+
const native = await runtime.inputState?.(pane).catch(() => void 0);
|
|
19584
|
+
if (native === "dialog")
|
|
19585
|
+
return { delivered: false, blockedByModal: true };
|
|
19586
|
+
if (native === "draft")
|
|
19587
|
+
return { delivered: false };
|
|
19426
19588
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
19427
19589
|
if (hasModalOptionList2(preSendScreen)) {
|
|
19428
19590
|
return { delivered: false, blockedByModal: true };
|
|
@@ -21040,9 +21202,9 @@ init_dist4();
|
|
|
21040
21202
|
init_dist3();
|
|
21041
21203
|
init_dist2();
|
|
21042
21204
|
import { Command as Command10 } from "commander";
|
|
21043
|
-
import
|
|
21044
|
-
import { createServer as
|
|
21045
|
-
import { randomUUID as
|
|
21205
|
+
import chalk11 from "chalk";
|
|
21206
|
+
import { createServer as createServer5, connect as netConnect3 } from "net";
|
|
21207
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
21046
21208
|
|
|
21047
21209
|
// packages/cli/src/commands/crew-control.ts
|
|
21048
21210
|
init_dist2();
|
|
@@ -21800,6 +21962,131 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
21800
21962
|
var crewControlCommand = new Command9("crew").description("Dispatch and track crew via the squadrant control plane");
|
|
21801
21963
|
addControlPlaneCrewCommands(crewControlCommand);
|
|
21802
21964
|
|
|
21965
|
+
// packages/cli/src/lib/first-turn-peer.ts
|
|
21966
|
+
init_dist3();
|
|
21967
|
+
init_dist2();
|
|
21968
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
21969
|
+
import { connect as netConnect2 } from "net";
|
|
21970
|
+
|
|
21971
|
+
// packages/cli/src/lib/captain-channel-factory.ts
|
|
21972
|
+
init_dist3();
|
|
21973
|
+
init_dist2();
|
|
21974
|
+
import { createServer as createServer4, connect as netConnect } from "net";
|
|
21975
|
+
import fs28 from "fs";
|
|
21976
|
+
import { join as join28 } from "path";
|
|
21977
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
21978
|
+
import chalk10 from "chalk";
|
|
21979
|
+
var shared;
|
|
21980
|
+
var registryEntryPath = () => join28(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
21981
|
+
function unregisterSenderIdentity() {
|
|
21982
|
+
try {
|
|
21983
|
+
fs28.unlinkSync(registryEntryPath());
|
|
21984
|
+
} catch {
|
|
21985
|
+
}
|
|
21986
|
+
}
|
|
21987
|
+
function registerSenderIdentity(socketPath2) {
|
|
21988
|
+
try {
|
|
21989
|
+
unregisterSenderIdentity();
|
|
21990
|
+
fs28.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
21991
|
+
fs28.writeFileSync(
|
|
21992
|
+
registryEntryPath(),
|
|
21993
|
+
JSON.stringify({
|
|
21994
|
+
pid: process.pid,
|
|
21995
|
+
sessionId: randomUUID6(),
|
|
21996
|
+
name: "squadrantd",
|
|
21997
|
+
messagingSocketPath: socketPath2,
|
|
21998
|
+
kind: "daemon",
|
|
21999
|
+
peerProtocol: 1
|
|
22000
|
+
})
|
|
22001
|
+
);
|
|
22002
|
+
process.on("exit", unregisterSenderIdentity);
|
|
22003
|
+
} catch {
|
|
22004
|
+
}
|
|
22005
|
+
}
|
|
22006
|
+
async function sharedReceiptListener() {
|
|
22007
|
+
if (shared) return shared;
|
|
22008
|
+
ensureSocksDir();
|
|
22009
|
+
const socketPath2 = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
|
|
22010
|
+
const listener = new ClaudeReceiptListener({
|
|
22011
|
+
socketPath: socketPath2,
|
|
22012
|
+
createServer: (h) => createServer4(h),
|
|
22013
|
+
// A UDS path is not cleaned up when a process is killed, so our own leftover
|
|
22014
|
+
// must never be the reason we refuse to start.
|
|
22015
|
+
unlinkStale: (p) => {
|
|
22016
|
+
try {
|
|
22017
|
+
fs28.unlinkSync(p);
|
|
22018
|
+
} catch {
|
|
22019
|
+
}
|
|
22020
|
+
},
|
|
22021
|
+
log: (m) => console.error(chalk10.dim(m))
|
|
22022
|
+
});
|
|
22023
|
+
await listener.start();
|
|
22024
|
+
registerSenderIdentity(socketPath2);
|
|
22025
|
+
shared = listener;
|
|
22026
|
+
return shared;
|
|
22027
|
+
}
|
|
22028
|
+
function captainSessionIdFor(taskId) {
|
|
22029
|
+
return readClaudeStatusBySocketPath(captainSocketPath(taskId))?.sessionId;
|
|
22030
|
+
}
|
|
22031
|
+
async function buildCaptainChannel() {
|
|
22032
|
+
const receipts = await sharedReceiptListener();
|
|
22033
|
+
return new ClaudePeerChannel({
|
|
22034
|
+
// The port's taskId IS the project name for captains.
|
|
22035
|
+
socketPathFor: (taskId) => captainSocketPath(taskId),
|
|
22036
|
+
sessionIdFor: captainSessionIdFor,
|
|
22037
|
+
// Captains are identified in the registry by their cwd (the project path).
|
|
22038
|
+
// Resolve by the socket path squadrant chose at launch, NOT by cwd: a captain
|
|
22039
|
+
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
22040
|
+
// missed and every captain ping reported accepted-unconfirmed.
|
|
22041
|
+
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
22042
|
+
wire: (p, e) => writeLine(p, e, { connect: (path46) => netConnect(path46) }),
|
|
22043
|
+
receipts,
|
|
22044
|
+
newMsgId: () => randomUUID6(),
|
|
22045
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
22046
|
+
log: (m) => console.error(chalk10.dim(m))
|
|
22047
|
+
});
|
|
22048
|
+
}
|
|
22049
|
+
async function buildCaptainChannels(opts) {
|
|
22050
|
+
const claude = await buildCaptainChannel();
|
|
22051
|
+
const opencode = new OpencodeHttpChannel({
|
|
22052
|
+
portFor: (project) => readCaptainAddress(opts.stateRoot, project)?.port,
|
|
22053
|
+
sessionFor: (project) => readCaptainAddress(opts.stateRoot, project)?.sessionId
|
|
22054
|
+
});
|
|
22055
|
+
return {
|
|
22056
|
+
channels: { claude, opencode },
|
|
22057
|
+
agentFor: (project) => resolveCaptainAgent(opts.stateRoot, project, opts.configAgent)
|
|
22058
|
+
};
|
|
22059
|
+
}
|
|
22060
|
+
function resolveCaptainAgent(stateRoot, project, configAgent) {
|
|
22061
|
+
return readCaptainAddress(stateRoot, project)?.agent ?? configAgent;
|
|
22062
|
+
}
|
|
22063
|
+
|
|
22064
|
+
// packages/cli/src/lib/first-turn-peer.ts
|
|
22065
|
+
async function sendClaudeFirstTurnViaPeer(runtime, o, getTaskRecord) {
|
|
22066
|
+
const receipts = await sharedReceiptListener();
|
|
22067
|
+
const channel = new ClaudePeerChannel({
|
|
22068
|
+
socketPathFor: () => o.messagingSocketPath,
|
|
22069
|
+
sessionIdFor: () => readClaudeStatusBySocketPath(o.messagingSocketPath)?.sessionId,
|
|
22070
|
+
statusFor: () => readClaudeStatusBySocketPath(o.messagingSocketPath),
|
|
22071
|
+
wire: (p, e) => writeLine(p, e, { connect: (path46) => netConnect2(path46) }),
|
|
22072
|
+
receipts,
|
|
22073
|
+
newMsgId: () => randomUUID7(),
|
|
22074
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
22075
|
+
// The UserPromptSubmit hook is the real receipt; don't hold spawn for the
|
|
22076
|
+
// default 4s inferred-flip window.
|
|
22077
|
+
confirmWindowMs: 1500
|
|
22078
|
+
});
|
|
22079
|
+
return deliverFirstTurnViaPeer(o.message, {
|
|
22080
|
+
statusOf: () => readClaudeStatusBySocketPath(o.messagingSocketPath),
|
|
22081
|
+
readScreen: async () => await runtime.readPaneScreen(o.pane) ?? "",
|
|
22082
|
+
pressKey: (key) => runtime.sendKeyToPane(o.pane, key),
|
|
22083
|
+
send: (m) => channel.send(o.taskId, m),
|
|
22084
|
+
isConfirmed: async () => !!(await getTaskRecord())?.firstTurnConfirmedAt,
|
|
22085
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
22086
|
+
now: () => Date.now()
|
|
22087
|
+
});
|
|
22088
|
+
}
|
|
22089
|
+
|
|
21803
22090
|
// packages/cli/src/lib/model-guard.ts
|
|
21804
22091
|
var ANTHROPIC_MODEL_PREFIX = "anthropic/";
|
|
21805
22092
|
var BARE_CLAUDE_MODEL_PREFIX = "claude-";
|
|
@@ -21818,8 +22105,8 @@ function anthropicRefusalMessage(role, workspaceName, agentName, model) {
|
|
|
21818
22105
|
|
|
21819
22106
|
// packages/cli/src/commands/crew.ts
|
|
21820
22107
|
async function fetchRouterCredentials(project, backend, deps) {
|
|
21821
|
-
const log = deps.log ?? ((m) => console.log(
|
|
21822
|
-
const warn = deps.warn ?? ((m) => console.error(
|
|
22108
|
+
const log = deps.log ?? ((m) => console.log(chalk11.dim(m)));
|
|
22109
|
+
const warn = deps.warn ?? ((m) => console.error(chalk11.yellow(m)));
|
|
21823
22110
|
const creds = await deps.call(buildRouterCredentialsRequest(project, backend));
|
|
21824
22111
|
if (creds.backend === "direct" && creds.apiKey) {
|
|
21825
22112
|
const approve = deps.approveKey ?? ensureClaudeApiKeyApproved;
|
|
@@ -21855,6 +22142,11 @@ async function runCrewSpawn2(input) {
|
|
|
21855
22142
|
writeOpencodeConfig: writePerCrewOpencodeConfig,
|
|
21856
22143
|
writeRouterSettings,
|
|
21857
22144
|
sendFirstTurn: (pane, firstTurn, preLaunchScreen, opts) => sendFirstTurnWhenReady(runtime, pane, firstTurn, preLaunchScreen, opts),
|
|
22145
|
+
// #917: claude crews get their first turn over the peer socket when the
|
|
22146
|
+
// claude control channel is `on`; anything else keeps the pane path.
|
|
22147
|
+
...resolveControlChannelMode(config.defaults.controlChannel, "claude") === "on" ? {
|
|
22148
|
+
sendFirstTurnPeer: (o) => sendClaudeFirstTurnViaPeer(runtime, o, async () => await squadrantdCall(buildStatusRequest(o.project, o.taskId)).catch(() => void 0))
|
|
22149
|
+
} : {},
|
|
21858
22150
|
getFreePort,
|
|
21859
22151
|
sendCodexFirstTurn,
|
|
21860
22152
|
// #466: wire delivery confirmation so the daemon stamps firstTurnConfirmedAt.
|
|
@@ -21867,25 +22159,25 @@ async function runCrewSpawn2(input) {
|
|
|
21867
22159
|
getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
|
|
21868
22160
|
isAgentInstalled: (name) => commandExists(name),
|
|
21869
22161
|
onRouteSkipped: (route, fallback) => console.log(
|
|
21870
|
-
|
|
22162
|
+
chalk11.dim(`routed: tier=${route.tier} \u2192 ${route.agent} (not installed) \u2192 ${fallback}`)
|
|
21871
22163
|
),
|
|
21872
22164
|
onRouted: (route) => console.log(
|
|
21873
|
-
|
|
22165
|
+
chalk11.dim(
|
|
21874
22166
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
21875
22167
|
)
|
|
21876
22168
|
),
|
|
21877
|
-
onBaseResolved: (base) => console.log(
|
|
22169
|
+
onBaseResolved: (base) => console.log(chalk11.dim(`base: ${base}`)),
|
|
21878
22170
|
// #627 item B: warn (don't block) when a fallback crew silently resolves to
|
|
21879
22171
|
// an Anthropic model — less catastrophic than a captain on the same path,
|
|
21880
22172
|
// and more often intentional, so it just needs to be visible.
|
|
21881
22173
|
onBackendResolved: ({ backend }) => {
|
|
21882
|
-
if (backend !== "native") console.log(
|
|
22174
|
+
if (backend !== "native") console.log(chalk11.dim(`backend: ${backend}`));
|
|
21883
22175
|
},
|
|
21884
22176
|
onModelResolved: ({ agentName, model }) => {
|
|
21885
22177
|
const effectiveModel = model ?? (agentName === "opencode" ? readGlobalOpencodeModel() : void 0);
|
|
21886
22178
|
if (isBlockedFallback(agentName, effectiveModel)) {
|
|
21887
22179
|
console.error(
|
|
21888
|
-
|
|
22180
|
+
chalk11.yellow(
|
|
21889
22181
|
` \u26A0 ${anthropicFallbackMessage(agentName, effectiveModel)} (crew \u2014 not blocked; pass --model to pin a different provider)`
|
|
21890
22182
|
)
|
|
21891
22183
|
);
|
|
@@ -21899,8 +22191,8 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21899
22191
|
let tasks = [];
|
|
21900
22192
|
const receipts = new ClaudeReceiptListener({
|
|
21901
22193
|
socketPath: "/tmp/cc-socks/squadrantd.sock",
|
|
21902
|
-
createServer: (h) =>
|
|
21903
|
-
log: (m) => console.error(
|
|
22194
|
+
createServer: (h) => createServer5(h),
|
|
22195
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21904
22196
|
});
|
|
21905
22197
|
await receipts.start();
|
|
21906
22198
|
const controlChannels = [
|
|
@@ -21914,17 +22206,17 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21914
22206
|
// a prior crew's session. Gate on the crew's createdAt so a stale session
|
|
21915
22207
|
// the live TUI is not showing is never selected.
|
|
21916
22208
|
createdAfterFor: (taskId) => tasks.find((t) => t.id === taskId)?.createdAt,
|
|
21917
|
-
log: (m) => console.error(
|
|
22209
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21918
22210
|
}),
|
|
21919
22211
|
new ClaudePeerChannel({
|
|
21920
22212
|
socketPathFor: (taskId) => tasks.find((t) => t.id === taskId)?.messagingSocketPath,
|
|
21921
22213
|
sessionIdFor: (taskId) => tasks.find((t) => t.id === taskId)?.sessionId,
|
|
21922
22214
|
statusFor: (taskId) => readClaudeStatus(tasks.find((t) => t.id === taskId)),
|
|
21923
|
-
wire: (p, e) => writeLine(p, e, { connect: (path46) =>
|
|
22215
|
+
wire: (p, e) => writeLine(p, e, { connect: (path46) => netConnect3(path46) }),
|
|
21924
22216
|
receipts,
|
|
21925
|
-
newMsgId: () =>
|
|
22217
|
+
newMsgId: () => randomUUID8(),
|
|
21926
22218
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
21927
|
-
log: (m) => console.error(
|
|
22219
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21928
22220
|
})
|
|
21929
22221
|
];
|
|
21930
22222
|
try {
|
|
@@ -21940,7 +22232,7 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21940
22232
|
isBlockedByModal: (pane) => paneHasOpenModal(runtime, pane),
|
|
21941
22233
|
controlChannels,
|
|
21942
22234
|
controlChannelMode: (agent) => resolveControlChannelMode(cfg.defaults.controlChannel, agent),
|
|
21943
|
-
onChannelLog: (m) => console.error(
|
|
22235
|
+
onChannelLog: (m) => console.error(chalk11.dim(m))
|
|
21944
22236
|
}, opts);
|
|
21945
22237
|
} finally {
|
|
21946
22238
|
receipts.stop();
|
|
@@ -21976,7 +22268,14 @@ async function runCrewAnswer2(project, name, option, opts) {
|
|
|
21976
22268
|
workspaceId,
|
|
21977
22269
|
{
|
|
21978
22270
|
readModalOptions: (pane) => readModalOptions(runtime, pane),
|
|
21979
|
-
|
|
22271
|
+
// #918: only claude has a structured answer path; others keep keystrokes.
|
|
22272
|
+
dialogMatch: async () => {
|
|
22273
|
+
const tasks = await squadrantdCall({ kind: "list", project }).catch(() => []);
|
|
22274
|
+
const t = resolveApproveTarget(tasks, name);
|
|
22275
|
+
if (!t || t.provider !== "claude" || !(t.sessionId || t.cwd)) return void 0;
|
|
22276
|
+
return { agentSessionId: t.sessionId, cwd: t.cwd };
|
|
22277
|
+
},
|
|
22278
|
+
log: (m) => console.log(chalk11.dim(m))
|
|
21980
22279
|
},
|
|
21981
22280
|
opts
|
|
21982
22281
|
);
|
|
@@ -22014,9 +22313,9 @@ crewCommand.command("spawn").description(
|
|
|
22014
22313
|
// into the isolated worktree root for relative-path access.
|
|
22015
22314
|
...opts.taskFile && opts.taskFile !== "-" ? { taskFile: opts.taskFile } : {}
|
|
22016
22315
|
});
|
|
22017
|
-
console.log(
|
|
22316
|
+
console.log(chalk11.green(`\u2714 Crew '${pane.title}' spawned (${pane.surfaceId})`));
|
|
22018
22317
|
} catch (err) {
|
|
22019
|
-
console.error(
|
|
22318
|
+
console.error(chalk11.red(err.message));
|
|
22020
22319
|
process.exit(1);
|
|
22021
22320
|
}
|
|
22022
22321
|
}
|
|
@@ -22025,7 +22324,7 @@ crewCommand.command("list").description("List live crew sessions for a project")
|
|
|
22025
22324
|
try {
|
|
22026
22325
|
const crews = await runCrewList2(project);
|
|
22027
22326
|
if (crews.length === 0) {
|
|
22028
|
-
console.log(
|
|
22327
|
+
console.log(chalk11.yellow(`No live crew sessions for ${project}.`));
|
|
22029
22328
|
return;
|
|
22030
22329
|
}
|
|
22031
22330
|
const tasks = await squadrantdCall({ kind: "list", project }).catch(() => []);
|
|
@@ -22056,7 +22355,7 @@ crewCommand.command("list").description("List live crew sessions for a project")
|
|
|
22056
22355
|
}
|
|
22057
22356
|
}
|
|
22058
22357
|
} catch (err) {
|
|
22059
|
-
console.error(
|
|
22358
|
+
console.error(chalk11.red(err.message));
|
|
22060
22359
|
process.exit(1);
|
|
22061
22360
|
}
|
|
22062
22361
|
});
|
|
@@ -22064,10 +22363,10 @@ crewCommand.command("send").description("Send a follow-up message to an existing
|
|
|
22064
22363
|
try {
|
|
22065
22364
|
const resolvedMessage = await resolveTextInput({ positional: message, filePath: opts.messageFile, label: "message" });
|
|
22066
22365
|
const { reopened } = await runCrewSend2(project, name, resolvedMessage, opts);
|
|
22067
|
-
if (reopened) console.log(
|
|
22068
|
-
console.log(
|
|
22366
|
+
if (reopened) console.log(chalk11.cyan(`\u21BB Task was terminal \u2014 reopened to working`));
|
|
22367
|
+
console.log(chalk11.green(`\u2714 Sent to ${project}:${name}`));
|
|
22069
22368
|
} catch (e) {
|
|
22070
|
-
console.error(
|
|
22369
|
+
console.error(chalk11.red(e.message));
|
|
22071
22370
|
process.exit(1);
|
|
22072
22371
|
}
|
|
22073
22372
|
});
|
|
@@ -22077,12 +22376,12 @@ crewCommand.command("answer").description(
|
|
|
22077
22376
|
try {
|
|
22078
22377
|
const { selected, closed } = await runCrewAnswer2(project, name, option, opts);
|
|
22079
22378
|
if (closed) {
|
|
22080
|
-
console.log(
|
|
22379
|
+
console.log(chalk11.green(`\u2714 Answered ${project}:${name} with ${selected.index}. "${selected.label}" \u2014 prompt closed`));
|
|
22081
22380
|
} else {
|
|
22082
|
-
console.log(
|
|
22381
|
+
console.log(chalk11.yellow(`\u26A0 Sent ${selected.index}. "${selected.label}" to ${project}:${name}, but the prompt still appears open \u2014 read it again with 'squadrant crew read ${project} ${name}'`));
|
|
22083
22382
|
}
|
|
22084
22383
|
} catch (err) {
|
|
22085
|
-
console.error(
|
|
22384
|
+
console.error(chalk11.red(err.message));
|
|
22086
22385
|
process.exit(1);
|
|
22087
22386
|
}
|
|
22088
22387
|
});
|
|
@@ -22092,16 +22391,16 @@ crewCommand.command("read").description("Read the current screen of a crew sessi
|
|
|
22092
22391
|
const out = opts.full ? screen : tailLines(screen, Number(opts.lines ?? 40));
|
|
22093
22392
|
console.log(out);
|
|
22094
22393
|
} catch (err) {
|
|
22095
|
-
console.error(
|
|
22394
|
+
console.error(chalk11.red(err.message));
|
|
22096
22395
|
process.exit(1);
|
|
22097
22396
|
}
|
|
22098
22397
|
});
|
|
22099
22398
|
crewCommand.command("close").description("Shutdown a crew session (closes its tab)").argument("<project>", "Project name").argument("<name>", "Crew name").option("--force", "override an operator takeover (only when the operator told you to)", false).action(async (project, name, opts) => {
|
|
22100
22399
|
try {
|
|
22101
22400
|
await runCrewClose2(project, name, opts);
|
|
22102
|
-
console.log(
|
|
22401
|
+
console.log(chalk11.green(`\u2714 Closed ${project}:${name}`));
|
|
22103
22402
|
} catch (err) {
|
|
22104
|
-
console.error(
|
|
22403
|
+
console.error(chalk11.red(err.message));
|
|
22105
22404
|
process.exit(1);
|
|
22106
22405
|
}
|
|
22107
22406
|
});
|
|
@@ -22110,7 +22409,7 @@ crewCommand.command("close").description("Shutdown a crew session (closes its ta
|
|
|
22110
22409
|
init_dist();
|
|
22111
22410
|
init_dist4();
|
|
22112
22411
|
import { Command as Command11 } from "commander";
|
|
22113
|
-
import
|
|
22412
|
+
import chalk12 from "chalk";
|
|
22114
22413
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
22115
22414
|
import readline2 from "readline";
|
|
22116
22415
|
function resolveDiffTarget(tasks, crew, projectPath) {
|
|
@@ -22227,7 +22526,7 @@ async function openCrewDiff(project, proj, crew, opts, runtime, workspaceId) {
|
|
|
22227
22526
|
const label = sources.length > 1 ? "staged or unstaged" : sources[0];
|
|
22228
22527
|
console.log(`No ${label} changes on ${branchLabel}.`);
|
|
22229
22528
|
} else {
|
|
22230
|
-
console.log(
|
|
22529
|
+
console.log(chalk12.dim(`Opened ${opened} working-tree diff(s) (${sources.join(", ")}) for ${branchLabel}.`));
|
|
22231
22530
|
}
|
|
22232
22531
|
return;
|
|
22233
22532
|
}
|
|
@@ -22250,7 +22549,7 @@ async function openCrewDiff(project, proj, crew, opts, runtime, workspaceId) {
|
|
|
22250
22549
|
lastTurn: opts.lastTurn,
|
|
22251
22550
|
source: "branch"
|
|
22252
22551
|
});
|
|
22253
|
-
console.log(
|
|
22552
|
+
console.log(chalk12.dim(`Opened ${branchLabel} vs ${base} in cmux diff.`));
|
|
22254
22553
|
}
|
|
22255
22554
|
async function runDiff(project, crewArg, opts) {
|
|
22256
22555
|
const config = loadConfig();
|
|
@@ -22271,7 +22570,7 @@ async function runDiff(project, crewArg, opts) {
|
|
|
22271
22570
|
return;
|
|
22272
22571
|
}
|
|
22273
22572
|
await runtime.showPatch({ workspaceId, patch, title, layout: opts.layout, focus: opts.focus });
|
|
22274
|
-
console.log(
|
|
22573
|
+
console.log(chalk12.dim(`Opened ${title} in cmux diff.`));
|
|
22275
22574
|
return;
|
|
22276
22575
|
}
|
|
22277
22576
|
let crew;
|
|
@@ -22307,10 +22606,10 @@ init_dist4();
|
|
|
22307
22606
|
init_dist();
|
|
22308
22607
|
init_dist2();
|
|
22309
22608
|
import { Command as Command12 } from "commander";
|
|
22310
|
-
import
|
|
22609
|
+
import fs29 from "fs";
|
|
22311
22610
|
import path32 from "path";
|
|
22312
22611
|
import os17 from "os";
|
|
22313
|
-
import
|
|
22612
|
+
import chalk13 from "chalk";
|
|
22314
22613
|
var TEMPLATES_DIR3 = path32.join(os17.homedir(), ".config", "squadrant", "templates");
|
|
22315
22614
|
async function runSideSpawn2(input) {
|
|
22316
22615
|
const config = loadConfig();
|
|
@@ -22344,7 +22643,7 @@ async function runSideSpawn2(input) {
|
|
|
22344
22643
|
prompt: input.topic,
|
|
22345
22644
|
workdir: spawnCwd,
|
|
22346
22645
|
role: "side",
|
|
22347
|
-
promptFile:
|
|
22646
|
+
promptFile: fs29.existsSync(promptFile) ? promptFile : void 0,
|
|
22348
22647
|
interactive: true,
|
|
22349
22648
|
permissionMode: config.defaults.permissions?.crew ?? "auto",
|
|
22350
22649
|
...sideModel ? { model: sideModel } : {},
|
|
@@ -22399,9 +22698,9 @@ sideCommand.command("spawn").description(
|
|
|
22399
22698
|
direction: opts.direction,
|
|
22400
22699
|
agent: opts.agent
|
|
22401
22700
|
});
|
|
22402
|
-
console.log(
|
|
22701
|
+
console.log(chalk13.green(`\u2714 Side session '${pane.title}' spawned (${pane.surfaceId})`));
|
|
22403
22702
|
} catch (err) {
|
|
22404
|
-
console.error(
|
|
22703
|
+
console.error(chalk13.red(err.message));
|
|
22405
22704
|
process.exit(1);
|
|
22406
22705
|
}
|
|
22407
22706
|
}
|
|
@@ -22410,14 +22709,14 @@ sideCommand.command("list").description("List live side-sessions for a project")
|
|
|
22410
22709
|
try {
|
|
22411
22710
|
const sessions = await runSideList2(project);
|
|
22412
22711
|
if (sessions.length === 0) {
|
|
22413
|
-
console.log(
|
|
22712
|
+
console.log(chalk13.yellow(`No live side-sessions for ${project}.`));
|
|
22414
22713
|
return;
|
|
22415
22714
|
}
|
|
22416
22715
|
for (const s of sessions) {
|
|
22417
22716
|
console.log(` ${s.name} (${s.surfaceId})`);
|
|
22418
22717
|
}
|
|
22419
22718
|
} catch (err) {
|
|
22420
|
-
console.error(
|
|
22719
|
+
console.error(chalk13.red(err.message));
|
|
22421
22720
|
process.exit(1);
|
|
22422
22721
|
}
|
|
22423
22722
|
});
|
|
@@ -22430,9 +22729,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
|
|
|
22430
22729
|
label: "message"
|
|
22431
22730
|
});
|
|
22432
22731
|
await runSideSend2(project, name, resolvedMessage);
|
|
22433
|
-
console.log(
|
|
22732
|
+
console.log(chalk13.green(`\u2714 Sent to ${project}:${name}`));
|
|
22434
22733
|
} catch (err) {
|
|
22435
|
-
console.error(
|
|
22734
|
+
console.error(chalk13.red(err.message));
|
|
22436
22735
|
process.exit(1);
|
|
22437
22736
|
}
|
|
22438
22737
|
}
|
|
@@ -22440,9 +22739,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
|
|
|
22440
22739
|
sideCommand.command("close").description("Close a side-session (closes its tab)").argument("<project>", "Project name").argument("<name>", "Session name").action(async (project, name) => {
|
|
22441
22740
|
try {
|
|
22442
22741
|
await runSideClose2(project, name);
|
|
22443
|
-
console.log(
|
|
22742
|
+
console.log(chalk13.green(`\u2714 Closed ${project}:${name}`));
|
|
22444
22743
|
} catch (err) {
|
|
22445
|
-
console.error(
|
|
22744
|
+
console.error(chalk13.red(err.message));
|
|
22446
22745
|
process.exit(1);
|
|
22447
22746
|
}
|
|
22448
22747
|
});
|
|
@@ -22452,7 +22751,7 @@ init_dist();
|
|
|
22452
22751
|
init_dist4();
|
|
22453
22752
|
import { Command as Command13 } from "commander";
|
|
22454
22753
|
import { execSync as execSync10 } from "child_process";
|
|
22455
|
-
import
|
|
22754
|
+
import chalk15 from "chalk";
|
|
22456
22755
|
|
|
22457
22756
|
// packages/web/dist/read-status.js
|
|
22458
22757
|
function deriveState(tasks) {
|
|
@@ -22529,14 +22828,14 @@ async function readAllStatuses(deps) {
|
|
|
22529
22828
|
}
|
|
22530
22829
|
|
|
22531
22830
|
// packages/web/dist/render.js
|
|
22532
|
-
import
|
|
22831
|
+
import chalk14 from "chalk";
|
|
22533
22832
|
var ICON = {
|
|
22534
|
-
idle:
|
|
22535
|
-
busy:
|
|
22536
|
-
blocked:
|
|
22537
|
-
errored:
|
|
22538
|
-
offline:
|
|
22539
|
-
unknown:
|
|
22833
|
+
idle: chalk14.green,
|
|
22834
|
+
busy: chalk14.cyan,
|
|
22835
|
+
blocked: chalk14.yellow,
|
|
22836
|
+
errored: chalk14.red,
|
|
22837
|
+
offline: chalk14.dim,
|
|
22838
|
+
unknown: chalk14.gray
|
|
22540
22839
|
};
|
|
22541
22840
|
var ICON_CHAR = {
|
|
22542
22841
|
idle: "\u25CF",
|
|
@@ -22579,10 +22878,10 @@ function renderDashboard(rows, opts) {
|
|
|
22579
22878
|
const width = opts.width ?? 100;
|
|
22580
22879
|
const lines = [];
|
|
22581
22880
|
lines.push("");
|
|
22582
|
-
lines.push(" " +
|
|
22881
|
+
lines.push(" " + chalk14.bold("\u{1F4CA} Squadrant Dashboard") + " " + chalk14.dim(opts.now));
|
|
22583
22882
|
lines.push("");
|
|
22584
22883
|
if (rows.length === 0) {
|
|
22585
|
-
lines.push(" " +
|
|
22884
|
+
lines.push(" " + chalk14.yellow("No projects registered. Add one with: squadrant projects add <name> <path>"));
|
|
22586
22885
|
lines.push("");
|
|
22587
22886
|
return lines.join("\n");
|
|
22588
22887
|
}
|
|
@@ -22593,21 +22892,21 @@ function renderDashboard(rows, opts) {
|
|
|
22593
22892
|
const excerptW = Math.max(20, width - FIXED);
|
|
22594
22893
|
for (const r of rows) {
|
|
22595
22894
|
const icon = ICON[r.state](ICON_CHAR[r.state]);
|
|
22596
|
-
const name =
|
|
22895
|
+
const name = chalk14.cyan(pad(r.project, NAME_W));
|
|
22597
22896
|
const state = ICON[r.state](pad(r.state, STATE_W));
|
|
22598
22897
|
const age = pad(formatAge(r.lastChecked, opts.now), AGE_W);
|
|
22599
|
-
const excerpt =
|
|
22898
|
+
const excerpt = chalk14.dim(truncate(firstLine(r.excerpt), excerptW));
|
|
22600
22899
|
lines.push(` ${icon} ${name} ${state} ${age} \u2502 ${excerpt}`);
|
|
22601
22900
|
}
|
|
22602
22901
|
lines.push("");
|
|
22603
|
-
lines.push(
|
|
22902
|
+
lines.push(chalk14.dim(" Refreshes every 10s \xB7 Ctrl+C to exit"));
|
|
22604
22903
|
lines.push("");
|
|
22605
22904
|
return lines.join("\n");
|
|
22606
22905
|
}
|
|
22607
22906
|
|
|
22608
22907
|
// packages/web/dist/sync-hub.js
|
|
22609
22908
|
init_dist();
|
|
22610
|
-
import
|
|
22909
|
+
import fs30 from "fs";
|
|
22611
22910
|
import path33 from "path";
|
|
22612
22911
|
function buildMirrorMarkdown(s) {
|
|
22613
22912
|
const fenced = "```";
|
|
@@ -22634,8 +22933,8 @@ function buildMirrorMarkdown(s) {
|
|
|
22634
22933
|
function syncHub(deps) {
|
|
22635
22934
|
if (!deps.config.hubVault)
|
|
22636
22935
|
return [];
|
|
22637
|
-
const writeFile6 = deps.writeFile ?? ((p, c) =>
|
|
22638
|
-
const mkdir6 = deps.mkdir ?? ((p) =>
|
|
22936
|
+
const writeFile6 = deps.writeFile ?? ((p, c) => fs30.writeFileSync(p, c));
|
|
22937
|
+
const mkdir6 = deps.mkdir ?? ((p) => fs30.mkdirSync(p, { recursive: true }));
|
|
22639
22938
|
const projectsDir = path33.join(resolveHome(deps.config.hubVault), "projects");
|
|
22640
22939
|
mkdir6(projectsDir);
|
|
22641
22940
|
const out = [];
|
|
@@ -22654,8 +22953,8 @@ function syncHub(deps) {
|
|
|
22654
22953
|
|
|
22655
22954
|
// packages/web/dist/web-server.js
|
|
22656
22955
|
init_dist2();
|
|
22657
|
-
import { createServer as
|
|
22658
|
-
import { dirname as dirname7, join as
|
|
22956
|
+
import { createServer as createServer6 } from "http";
|
|
22957
|
+
import { dirname as dirname7, join as join30 } from "path";
|
|
22659
22958
|
|
|
22660
22959
|
// packages/web/dist/snapshot-merge.js
|
|
22661
22960
|
function mergeSnapshot(daemon, external, now) {
|
|
@@ -22665,7 +22964,7 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
22665
22964
|
// packages/web/dist/probes.js
|
|
22666
22965
|
init_dist();
|
|
22667
22966
|
init_dist();
|
|
22668
|
-
import { join as
|
|
22967
|
+
import { join as join29 } from "path";
|
|
22669
22968
|
import { homedir as homedir16 } from "os";
|
|
22670
22969
|
import { existsSync as existsSync13, readFileSync as readFileSync20 } from "fs";
|
|
22671
22970
|
import { execFile as execFile4 } from "child_process";
|
|
@@ -22703,7 +23002,7 @@ function vaultProbe(run, dir) {
|
|
|
22703
23002
|
return { state: "unknown", detail: "no vault configured" };
|
|
22704
23003
|
if (!run.pathExists(dir))
|
|
22705
23004
|
return { state: "gone", detail: "vault directory missing" };
|
|
22706
|
-
if (!run.pathExists(
|
|
23005
|
+
if (!run.pathExists(join29(dir, ".obsidian")))
|
|
22707
23006
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
22708
23007
|
return { state: "alive" };
|
|
22709
23008
|
} catch {
|
|
@@ -22771,10 +23070,10 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
|
22771
23070
|
const sessions = probeSessions(run);
|
|
22772
23071
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
22773
23072
|
}
|
|
22774
|
-
var SESSIONS_PATH =
|
|
23073
|
+
var SESSIONS_PATH = join29(homedir16(), ".config", "squadrant", "sessions.json");
|
|
22775
23074
|
function onPath(cli) {
|
|
22776
23075
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
22777
|
-
return dirs.some((d) => existsSync13(
|
|
23076
|
+
return dirs.some((d) => existsSync13(join29(d, cli)));
|
|
22778
23077
|
}
|
|
22779
23078
|
function readSessionsHashes() {
|
|
22780
23079
|
const raw = JSON.parse(readFileSync20(SESSIONS_PATH, "utf-8"));
|
|
@@ -23751,7 +24050,7 @@ async function startWebServer(opts) {
|
|
|
23751
24050
|
const now = opts.now ?? (() => Date.now());
|
|
23752
24051
|
const clients = /* @__PURE__ */ new Set();
|
|
23753
24052
|
const logClients = /* @__PURE__ */ new Set();
|
|
23754
|
-
const logPath2 = opts.logPath ??
|
|
24053
|
+
const logPath2 = opts.logPath ?? join30(dirname7(opts.sockPath), "squadrantd.log");
|
|
23755
24054
|
const tailer = new LogTailer(logPath2);
|
|
23756
24055
|
let latest = null;
|
|
23757
24056
|
async function tick() {
|
|
@@ -23781,7 +24080,7 @@ async function startWebServer(opts) {
|
|
|
23781
24080
|
}
|
|
23782
24081
|
}
|
|
23783
24082
|
}
|
|
23784
|
-
const server =
|
|
24083
|
+
const server = createServer6((req, res) => {
|
|
23785
24084
|
if (req.method !== "GET") {
|
|
23786
24085
|
res.writeHead(405).end();
|
|
23787
24086
|
return;
|
|
@@ -23912,8 +24211,8 @@ async function runDashboardWeb(input) {
|
|
|
23912
24211
|
sockPath: SOCK3,
|
|
23913
24212
|
runners: defaultProbeRunners()
|
|
23914
24213
|
});
|
|
23915
|
-
console.log(
|
|
23916
|
-
console.log(
|
|
24214
|
+
console.log(chalk15.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
|
|
24215
|
+
console.log(chalk15.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
|
|
23917
24216
|
}
|
|
23918
24217
|
var dashboardCommand = new Command13("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v2) => parseInt(v2, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v2) => parseInt(v2, 10)).action(async (opts) => {
|
|
23919
24218
|
try {
|
|
@@ -23923,12 +24222,12 @@ var dashboardCommand = new Command13("dashboard").description("Live status grid
|
|
|
23923
24222
|
}
|
|
23924
24223
|
if (opts.pane) {
|
|
23925
24224
|
const pane = await runDashboardPane({ direction: opts.direction, interval: opts.interval ?? 10 });
|
|
23926
|
-
console.log(
|
|
24225
|
+
console.log(chalk15.green(`\u2714 Dashboard pane opened in ${pane.workspaceId} ${pane.surfaceId}`));
|
|
23927
24226
|
return;
|
|
23928
24227
|
}
|
|
23929
24228
|
await runDashboardOnce();
|
|
23930
24229
|
} catch (err) {
|
|
23931
|
-
console.error(
|
|
24230
|
+
console.error(chalk15.red(err.message));
|
|
23932
24231
|
process.exit(1);
|
|
23933
24232
|
}
|
|
23934
24233
|
});
|
|
@@ -23939,12 +24238,12 @@ dashboardCommand.command("sync-hub").description("Mirror each spoke status.md in
|
|
|
23939
24238
|
return;
|
|
23940
24239
|
}
|
|
23941
24240
|
if (results.length === 0) {
|
|
23942
|
-
console.log(
|
|
24241
|
+
console.log(chalk15.dim("\n No mirrors written (no projects with usable status.md, or hubVault unset).\n"));
|
|
23943
24242
|
return;
|
|
23944
24243
|
}
|
|
23945
|
-
console.log(
|
|
24244
|
+
console.log(chalk15.bold("\n \u{1F4CA} Hub mirror sync\n"));
|
|
23946
24245
|
for (const r of results) {
|
|
23947
|
-
console.log(` ${
|
|
24246
|
+
console.log(` ${chalk15.green("\u2714")} ${chalk15.cyan(r.project.padEnd(16))} \u2192 ${chalk15.dim(r.hubPath)}`);
|
|
23948
24247
|
}
|
|
23949
24248
|
console.log("");
|
|
23950
24249
|
});
|
|
@@ -23956,10 +24255,10 @@ init_dist4();
|
|
|
23956
24255
|
init_dist2();
|
|
23957
24256
|
import { Command as Command14 } from "commander";
|
|
23958
24257
|
import { execSync as execSync11, execFileSync as execFileSync11 } from "child_process";
|
|
23959
|
-
import
|
|
24258
|
+
import fs31 from "fs";
|
|
23960
24259
|
import path34 from "path";
|
|
23961
24260
|
import os18 from "os";
|
|
23962
|
-
import
|
|
24261
|
+
import chalk16 from "chalk";
|
|
23963
24262
|
|
|
23964
24263
|
// packages/cli/src/commands/launch-interactive.ts
|
|
23965
24264
|
import checkbox, { Separator } from "@inquirer/checkbox";
|
|
@@ -24086,20 +24385,20 @@ function resolveAnthropicRefusal(role, workspaceName, agentName, model, readOpen
|
|
|
24086
24385
|
}
|
|
24087
24386
|
function ensureCmuxReady(headless) {
|
|
24088
24387
|
if (headless || isInsideCmux()) return;
|
|
24089
|
-
console.log(
|
|
24388
|
+
console.log(chalk16.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
24090
24389
|
execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
|
|
24091
|
-
console.log(
|
|
24390
|
+
console.log(chalk16.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
|
|
24092
24391
|
process.exit(0);
|
|
24093
24392
|
}
|
|
24094
24393
|
var launchCommand = new Command14("launch").description(
|
|
24095
24394
|
"Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
|
|
24096
24395
|
).argument("[project]", "Project name to launch captain for").option("--fresh", "Start a new session instead of resuming the last one").option("--keep", "Resume the latest session even on a new day / after a template change").option("--all", "Launch all captain workspaces").option("--headless", "Skip the interactive cmux-app requirement (used by the daemon to boot captains without a terminal)").option("--agent <name>", "Override captain agent for this launch (claude|codex|gemini|opencode); takes precedence over defaults.roles.captain.agent").option("--model <name>", "Override captain model for this launch; takes precedence over defaults.roles.captain.model").option("--thinking <level>", `Override captain thinking level for this launch (${THINKING_LEVELS.join("|")}) \u2192 claude --effort; takes precedence over defaults.roles.captain.thinking`).option("--backend <mode>", "Override captain backend for this launch (native|direct|proxy); takes precedence over defaults.roles.captain.backend").action(async (project, opts) => {
|
|
24097
24396
|
if (opts.fresh && opts.keep) {
|
|
24098
|
-
console.error(
|
|
24397
|
+
console.error(chalk16.red("\n \u2718 --fresh and --keep are mutually exclusive\n"));
|
|
24099
24398
|
process.exit(1);
|
|
24100
24399
|
}
|
|
24101
24400
|
if (opts.backend !== void 0 && !isBackendMode(opts.backend)) {
|
|
24102
|
-
console.error(
|
|
24401
|
+
console.error(chalk16.red(`
|
|
24103
24402
|
\u2718 Invalid --backend value '${opts.backend}'. Valid values: native, direct, proxy
|
|
24104
24403
|
`));
|
|
24105
24404
|
process.exit(1);
|
|
@@ -24109,7 +24408,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24109
24408
|
try {
|
|
24110
24409
|
thinkingOverride = opts.thinking ? parseThinkingLevel(opts.thinking) : void 0;
|
|
24111
24410
|
} catch (err) {
|
|
24112
|
-
console.error(
|
|
24411
|
+
console.error(chalk16.red(`
|
|
24113
24412
|
\u2718 ${err.message}
|
|
24114
24413
|
`));
|
|
24115
24414
|
process.exit(1);
|
|
@@ -24134,7 +24433,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24134
24433
|
const backend = resolveLaunchBackend({ backendOverride, agentName, roleConfig });
|
|
24135
24434
|
const refusal = resolveAnthropicRefusal(role, workspaceName, agentName, model);
|
|
24136
24435
|
if (refusal) {
|
|
24137
|
-
console.error(
|
|
24436
|
+
console.error(chalk16.red(`
|
|
24138
24437
|
\u2718 ${refusal}
|
|
24139
24438
|
`));
|
|
24140
24439
|
hadFailure = true;
|
|
@@ -24151,7 +24450,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24151
24450
|
const stateRoot = path34.join(os18.homedir(), ".config", "squadrant", "state");
|
|
24152
24451
|
const isOpencodeCaptain = role === "captain" && agentName === "opencode" && !!projectName;
|
|
24153
24452
|
if (isOpencodeCaptain && !isOpencodeCaptainDir(cwd)) {
|
|
24154
|
-
console.error(
|
|
24453
|
+
console.error(chalk16.red(`
|
|
24155
24454
|
\u2718 '${projectName}' is not a git repo with a commit \u2014 an opencode captain needs a stable project identity. Run 'git commit' first.
|
|
24156
24455
|
`));
|
|
24157
24456
|
hadFailure = true;
|
|
@@ -24182,10 +24481,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
24182
24481
|
routerCredentials: (o) => fetchRouterCredentials(o.project, o.backend, { call: squadrantdCall }),
|
|
24183
24482
|
writeRouterSettings
|
|
24184
24483
|
},
|
|
24185
|
-
warn: (m) => console.error(
|
|
24484
|
+
warn: (m) => console.error(chalk16.yellow(` \u26A0 ${m}`))
|
|
24186
24485
|
});
|
|
24187
24486
|
} catch (err) {
|
|
24188
|
-
console.error(
|
|
24487
|
+
console.error(chalk16.red(` \u2718 ${err.message}`));
|
|
24189
24488
|
hadFailure = true;
|
|
24190
24489
|
return;
|
|
24191
24490
|
}
|
|
@@ -24225,7 +24524,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24225
24524
|
route?.settingsPath
|
|
24226
24525
|
);
|
|
24227
24526
|
if (gateDecision?.warn) {
|
|
24228
|
-
console.error(
|
|
24527
|
+
console.error(chalk16.yellow(` \u26A0 gate: ${gateDecision.reason} \u2014 launching opencode captain directly (P6-C safety fallback)`));
|
|
24229
24528
|
}
|
|
24230
24529
|
const effectiveCmd = gateDecision?.wrap && captainBoot?.port ? buildOpencodeGateRunCommand({ port: captainBoot.port, sessionId: captainBoot.sessionId }) : baseCmd;
|
|
24231
24530
|
const gatePrefix = route && Object.keys(route.env).length > 0 ? `${renderEnvAssignments(route.env)} ` : "";
|
|
@@ -24245,10 +24544,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
24245
24544
|
return null;
|
|
24246
24545
|
}
|
|
24247
24546
|
},
|
|
24248
|
-
onFreshReason: (reason) => console.log(
|
|
24249
|
-
onStoppingStale: (name) => console.log(
|
|
24547
|
+
onFreshReason: (reason) => console.log(chalk16.cyan(` \u21BB ${reason}`)),
|
|
24548
|
+
onStoppingStale: (name) => console.log(chalk16.yellow(` Closing stale workspace '${name}' for fresh start`)),
|
|
24250
24549
|
onAlreadyExists: (name) => {
|
|
24251
|
-
console.log(
|
|
24550
|
+
console.log(chalk16.yellow(` Workspace '${name}' already exists \u2014 switching to it`));
|
|
24252
24551
|
if (!projectName || !isOpencodeCaptain) return;
|
|
24253
24552
|
const live = discoverLiveOpencodeServer({
|
|
24254
24553
|
directory: realpathOrSelf(cwd),
|
|
@@ -24266,7 +24565,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24266
24565
|
});
|
|
24267
24566
|
},
|
|
24268
24567
|
onCreated: (name) => {
|
|
24269
|
-
console.log(
|
|
24568
|
+
console.log(chalk16.green(` \u2714 Workspace '${name}' created`));
|
|
24270
24569
|
if (!projectName) return;
|
|
24271
24570
|
if (isOpencodeCaptain && captainPort) {
|
|
24272
24571
|
writeCaptainAddress(stateRoot, projectName, {
|
|
@@ -24282,7 +24581,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24282
24581
|
directory: realpathOrSelf(cwd),
|
|
24283
24582
|
launchedAt,
|
|
24284
24583
|
sessionId: opencodeResumeSessionId,
|
|
24285
|
-
log: (m) => console.error(
|
|
24584
|
+
log: (m) => console.error(chalk16.yellow(` \u26A0 ${m}`))
|
|
24286
24585
|
}).catch(() => {
|
|
24287
24586
|
});
|
|
24288
24587
|
} else if (role === "captain") {
|
|
@@ -24295,23 +24594,23 @@ var launchCommand = new Command14("launch").description(
|
|
|
24295
24594
|
}
|
|
24296
24595
|
});
|
|
24297
24596
|
} catch (err) {
|
|
24298
|
-
console.error(
|
|
24597
|
+
console.error(chalk16.red(` \u2718 Failed: ${err.message}`));
|
|
24299
24598
|
hadFailure = true;
|
|
24300
24599
|
}
|
|
24301
24600
|
}
|
|
24302
24601
|
if (opts.all) {
|
|
24303
24602
|
const hubPath = resolveHome(config.hubVault);
|
|
24304
|
-
|
|
24305
|
-
console.log(
|
|
24603
|
+
fs31.mkdirSync(hubPath, { recursive: true });
|
|
24604
|
+
console.log(chalk16.bold("\nLaunching all captain workspaces\n"));
|
|
24306
24605
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
24307
24606
|
const projPath = resolveHome(proj.path);
|
|
24308
24607
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24309
|
-
if (!
|
|
24608
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24310
24609
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
24311
24610
|
await ensureSpokeLayout(spokeDriver);
|
|
24312
|
-
console.log(
|
|
24611
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24313
24612
|
}
|
|
24314
|
-
console.log(
|
|
24613
|
+
console.log(chalk16.bold(`
|
|
24315
24614
|
Captain: ${proj.captainName} (${name})`));
|
|
24316
24615
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
24317
24616
|
}
|
|
@@ -24319,7 +24618,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24319
24618
|
} else if (!project) {
|
|
24320
24619
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
24321
24620
|
console.error(
|
|
24322
|
-
|
|
24621
|
+
chalk16.red(
|
|
24323
24622
|
"\n \u2718 Specify a project name, or pass --all to launch every captain.\n For one-shot Command tasks, use `squadrant command --task <briefing|learnings-review|wiki-aggregate>`.\n"
|
|
24324
24623
|
)
|
|
24325
24624
|
);
|
|
@@ -24334,22 +24633,22 @@ var launchCommand = new Command14("launch").description(
|
|
|
24334
24633
|
}));
|
|
24335
24634
|
const selected = await selectCaptainsInteractive(entries);
|
|
24336
24635
|
if (selected.length === 0) {
|
|
24337
|
-
console.log(
|
|
24636
|
+
console.log(chalk16.yellow("\n No captains selected.\n"));
|
|
24338
24637
|
return;
|
|
24339
24638
|
}
|
|
24340
|
-
console.log(
|
|
24639
|
+
console.log(chalk16.bold(`
|
|
24341
24640
|
Launching ${selected.length} captain workspace(s) in parallel
|
|
24342
24641
|
`));
|
|
24343
24642
|
await Promise.all(selected.map(async (name) => {
|
|
24344
24643
|
const proj = config.projects[name];
|
|
24345
24644
|
const projPath = resolveHome(proj.path);
|
|
24346
24645
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24347
|
-
if (!
|
|
24646
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24348
24647
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
24349
24648
|
await ensureSpokeLayout(spokeDriver);
|
|
24350
|
-
console.log(
|
|
24649
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24351
24650
|
}
|
|
24352
|
-
console.log(
|
|
24651
|
+
console.log(chalk16.bold(`
|
|
24353
24652
|
Captain: ${proj.captainName} (${name})`));
|
|
24354
24653
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
24355
24654
|
}));
|
|
@@ -24357,7 +24656,7 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
24357
24656
|
} else {
|
|
24358
24657
|
if (!config.projects[project]) {
|
|
24359
24658
|
console.error(
|
|
24360
|
-
|
|
24659
|
+
chalk16.red(
|
|
24361
24660
|
`
|
|
24362
24661
|
\u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
|
|
24363
24662
|
`
|
|
@@ -24368,13 +24667,13 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
24368
24667
|
const proj = config.projects[project];
|
|
24369
24668
|
const projPath = resolveHome(proj.path);
|
|
24370
24669
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24371
|
-
if (!
|
|
24670
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24372
24671
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
|
|
24373
24672
|
await ensureSpokeLayout(spokeDriver);
|
|
24374
|
-
console.log(
|
|
24673
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24375
24674
|
}
|
|
24376
24675
|
console.log(
|
|
24377
|
-
|
|
24676
|
+
chalk16.bold(
|
|
24378
24677
|
`
|
|
24379
24678
|
Launching captain workspace for '${project}' (${proj.captainName})
|
|
24380
24679
|
`
|
|
@@ -24389,7 +24688,7 @@ Launching captain workspace for '${project}' (${proj.captainName})
|
|
|
24389
24688
|
init_dist();
|
|
24390
24689
|
init_dist4();
|
|
24391
24690
|
import { Command as Command15 } from "commander";
|
|
24392
|
-
import
|
|
24691
|
+
import chalk17 from "chalk";
|
|
24393
24692
|
init_dist();
|
|
24394
24693
|
function nameVariants(name) {
|
|
24395
24694
|
const stripped = name.replace(/^⚓\s+/, "").trim();
|
|
@@ -24402,17 +24701,17 @@ async function closeMatching(runtime, variants, label) {
|
|
|
24402
24701
|
const failed = [];
|
|
24403
24702
|
if (matches.length === 0) {
|
|
24404
24703
|
console.log(
|
|
24405
|
-
|
|
24704
|
+
chalk17.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
|
|
24406
24705
|
);
|
|
24407
24706
|
return { closed, failed };
|
|
24408
24707
|
}
|
|
24409
24708
|
for (const ws of matches) {
|
|
24410
24709
|
try {
|
|
24411
24710
|
await runtime.stop(ws.id);
|
|
24412
|
-
console.log(
|
|
24711
|
+
console.log(chalk17.green(` \u2714 Closed: ${ws.name}`));
|
|
24413
24712
|
closed.push(ws.name);
|
|
24414
24713
|
} catch {
|
|
24415
|
-
console.log(
|
|
24714
|
+
console.log(chalk17.red(` \u2718 Failed to close: ${ws.name}`));
|
|
24416
24715
|
failed.push(ws.name);
|
|
24417
24716
|
}
|
|
24418
24717
|
}
|
|
@@ -24434,11 +24733,11 @@ var shutdownCommand = new Command15("shutdown").description(
|
|
|
24434
24733
|
const allVariants = /* @__PURE__ */ new Set([...captainVariants, ...commandVariants]);
|
|
24435
24734
|
const squadrantWorkspaces = workspaces.filter((w) => allVariants.has(w.name));
|
|
24436
24735
|
if (squadrantWorkspaces.length === 0) {
|
|
24437
|
-
console.log(
|
|
24736
|
+
console.log(chalk17.yellow("\nNo squadrant workspaces found to close.\n"));
|
|
24438
24737
|
return;
|
|
24439
24738
|
}
|
|
24440
24739
|
console.log(
|
|
24441
|
-
|
|
24740
|
+
chalk17.bold(
|
|
24442
24741
|
`
|
|
24443
24742
|
Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
24444
24743
|
`
|
|
@@ -24458,9 +24757,9 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24458
24757
|
for (const ws of squadrantWorkspaces) {
|
|
24459
24758
|
try {
|
|
24460
24759
|
await globalRuntime.stop(ws.id);
|
|
24461
|
-
console.log(
|
|
24760
|
+
console.log(chalk17.green(` \u2714 Closed: ${ws.name}`));
|
|
24462
24761
|
} catch {
|
|
24463
|
-
console.log(
|
|
24762
|
+
console.log(chalk17.red(` \u2718 Failed to close: ${ws.name}`));
|
|
24464
24763
|
}
|
|
24465
24764
|
}
|
|
24466
24765
|
console.log("");
|
|
@@ -24468,7 +24767,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24468
24767
|
}
|
|
24469
24768
|
if (!config.projects[project]) {
|
|
24470
24769
|
console.error(
|
|
24471
|
-
|
|
24770
|
+
chalk17.red(
|
|
24472
24771
|
`
|
|
24473
24772
|
\u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
|
|
24474
24773
|
`
|
|
@@ -24479,7 +24778,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24479
24778
|
const captainName = config.projects[project].captainName;
|
|
24480
24779
|
const runtime = runtimes.forProject(project, config);
|
|
24481
24780
|
console.log(
|
|
24482
|
-
|
|
24781
|
+
chalk17.bold(`
|
|
24483
24782
|
Shutting down captain workspace for '${project}'...
|
|
24484
24783
|
`)
|
|
24485
24784
|
);
|
|
@@ -24504,24 +24803,24 @@ Shutting down captain workspace for '${project}'...
|
|
|
24504
24803
|
// packages/cli/src/commands/feedback.ts
|
|
24505
24804
|
init_dist();
|
|
24506
24805
|
import { Command as Command16 } from "commander";
|
|
24507
|
-
import
|
|
24806
|
+
import fs32 from "fs";
|
|
24508
24807
|
import os19 from "os";
|
|
24509
24808
|
import path35 from "path";
|
|
24510
24809
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24511
24810
|
import { execSync as execSync12 } from "child_process";
|
|
24512
|
-
import
|
|
24811
|
+
import chalk18 from "chalk";
|
|
24513
24812
|
var REPO_URL = "https://github.com/tu11aa/squadrant";
|
|
24514
24813
|
function readPkgVersion() {
|
|
24515
24814
|
try {
|
|
24516
24815
|
const pkgPath = path35.join(path35.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
|
|
24517
|
-
return JSON.parse(
|
|
24816
|
+
return JSON.parse(fs32.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
24518
24817
|
} catch {
|
|
24519
24818
|
return "unknown";
|
|
24520
24819
|
}
|
|
24521
24820
|
}
|
|
24522
24821
|
function readMetrics(metricsPath) {
|
|
24523
24822
|
try {
|
|
24524
|
-
return JSON.parse(
|
|
24823
|
+
return JSON.parse(fs32.readFileSync(metricsPath, "utf-8"));
|
|
24525
24824
|
} catch {
|
|
24526
24825
|
return {};
|
|
24527
24826
|
}
|
|
@@ -24563,15 +24862,15 @@ var feedbackCommand = new Command16("feedback").description("Open a pre-filled G
|
|
|
24563
24862
|
const metrics = readMetrics(metricsPath);
|
|
24564
24863
|
const version = readStamp(config) ?? readPkgVersion();
|
|
24565
24864
|
const issueUrl = buildIssueUrl(metrics, version);
|
|
24566
|
-
console.log(
|
|
24567
|
-
console.log(
|
|
24865
|
+
console.log(chalk18.bold("\nOpening feedback issue in browser...\n"));
|
|
24866
|
+
console.log(chalk18.dim(` URL: ${issueUrl.substring(0, 80)}...
|
|
24568
24867
|
`));
|
|
24569
24868
|
try {
|
|
24570
24869
|
execSync12(`open "${issueUrl}"`, { stdio: "ignore" });
|
|
24571
|
-
console.log(
|
|
24870
|
+
console.log(chalk18.green(" \u2714 Browser opened\n"));
|
|
24572
24871
|
} catch {
|
|
24573
|
-
console.log(
|
|
24574
|
-
console.log(` Open manually: ${
|
|
24872
|
+
console.log(chalk18.yellow(" \u26A0 Could not open browser automatically."));
|
|
24873
|
+
console.log(` Open manually: ${chalk18.cyan(issueUrl)}
|
|
24575
24874
|
`);
|
|
24576
24875
|
}
|
|
24577
24876
|
});
|
|
@@ -24581,7 +24880,7 @@ init_dist();
|
|
|
24581
24880
|
init_dist();
|
|
24582
24881
|
init_dist4();
|
|
24583
24882
|
import { Command as Command17 } from "commander";
|
|
24584
|
-
import
|
|
24883
|
+
import chalk19 from "chalk";
|
|
24585
24884
|
function getDateStr(yesterday) {
|
|
24586
24885
|
return iso(daysAgo(yesterday ? 1 : 0));
|
|
24587
24886
|
}
|
|
@@ -24600,7 +24899,7 @@ function formatStandup(standups, dateStr, raw) {
|
|
|
24600
24899
|
const lines = [];
|
|
24601
24900
|
const header = `Standup \u2014 ${dateStr}`;
|
|
24602
24901
|
if (!raw) {
|
|
24603
|
-
lines.push(
|
|
24902
|
+
lines.push(chalk19.bold(`
|
|
24604
24903
|
${header}
|
|
24605
24904
|
`));
|
|
24606
24905
|
} else {
|
|
@@ -24610,12 +24909,12 @@ ${header}
|
|
|
24610
24909
|
let hasBlockers = false;
|
|
24611
24910
|
for (const s of standups) {
|
|
24612
24911
|
if (!raw) {
|
|
24613
|
-
lines.push(
|
|
24912
|
+
lines.push(chalk19.cyan.bold(`## ${s.name}`));
|
|
24614
24913
|
} else {
|
|
24615
24914
|
lines.push(`## ${s.name}`);
|
|
24616
24915
|
}
|
|
24617
24916
|
if (s.gitCommits.length > 0) {
|
|
24618
|
-
lines.push(!raw ?
|
|
24917
|
+
lines.push(!raw ? chalk19.green("Done:") : "**Done:**");
|
|
24619
24918
|
for (const commit of s.gitCommits) {
|
|
24620
24919
|
lines.push(` - ${commit}`);
|
|
24621
24920
|
}
|
|
@@ -24627,7 +24926,7 @@ ${header}
|
|
|
24627
24926
|
if (match) {
|
|
24628
24927
|
const items = match[1].trim().split("\n").filter((l) => l.trim().startsWith("-"));
|
|
24629
24928
|
if (items.length > 0 && section === "Tomorrow") {
|
|
24630
|
-
lines.push(!raw ?
|
|
24929
|
+
lines.push(!raw ? chalk19.blue("Next:") : "**Next:**");
|
|
24631
24930
|
for (const item of items) lines.push(` ${item.trim()}`);
|
|
24632
24931
|
}
|
|
24633
24932
|
}
|
|
@@ -24635,19 +24934,19 @@ ${header}
|
|
|
24635
24934
|
}
|
|
24636
24935
|
if (s.blockers.length > 0) {
|
|
24637
24936
|
hasBlockers = true;
|
|
24638
|
-
lines.push(!raw ?
|
|
24937
|
+
lines.push(!raw ? chalk19.red("Blocked:") : "**Blocked:**");
|
|
24639
24938
|
for (const b of s.blockers) {
|
|
24640
24939
|
lines.push(` - ${b}`);
|
|
24641
24940
|
}
|
|
24642
24941
|
}
|
|
24643
24942
|
if (s.gitCommits.length === 0 && !s.dailyLog) {
|
|
24644
|
-
lines.push(!raw ?
|
|
24943
|
+
lines.push(!raw ? chalk19.dim(" (no activity)") : " (no activity)");
|
|
24645
24944
|
}
|
|
24646
24945
|
lines.push("");
|
|
24647
24946
|
}
|
|
24648
24947
|
const totalCommits = standups.reduce((sum, s) => sum + s.gitCommits.length, 0);
|
|
24649
24948
|
if (!raw) {
|
|
24650
|
-
lines.push(
|
|
24949
|
+
lines.push(chalk19.dim(`--- ${totalCommits} commits${hasBlockers ? ", HAS BLOCKERS" : ""} (task tracking: no data source \u2014 #630) ---
|
|
24651
24950
|
`));
|
|
24652
24951
|
} else {
|
|
24653
24952
|
lines.push(`---
|
|
@@ -24661,7 +24960,7 @@ var standupCommand = new Command17("standup").description("Generate daily standu
|
|
|
24661
24960
|
const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
24662
24961
|
const projects = Object.entries(config.projects);
|
|
24663
24962
|
if (projects.length === 0) {
|
|
24664
|
-
console.log(
|
|
24963
|
+
console.log(chalk19.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
|
|
24665
24964
|
return;
|
|
24666
24965
|
}
|
|
24667
24966
|
const dateStr = getDateStr(!!opts.yesterday);
|
|
@@ -24670,7 +24969,7 @@ var standupCommand = new Command17("standup").description("Generate daily standu
|
|
|
24670
24969
|
if (opts.project) {
|
|
24671
24970
|
const match = projects.find(([name]) => name === opts.project);
|
|
24672
24971
|
if (!match) {
|
|
24673
|
-
console.error(
|
|
24972
|
+
console.error(chalk19.red(`Project "${opts.project}" not found.`));
|
|
24674
24973
|
process.exit(1);
|
|
24675
24974
|
}
|
|
24676
24975
|
targets = [match];
|
|
@@ -24689,7 +24988,7 @@ init_dist();
|
|
|
24689
24988
|
init_dist();
|
|
24690
24989
|
init_dist4();
|
|
24691
24990
|
import { Command as Command18 } from "commander";
|
|
24692
|
-
import
|
|
24991
|
+
import chalk20 from "chalk";
|
|
24693
24992
|
function dedupe(items) {
|
|
24694
24993
|
const seen = /* @__PURE__ */ new Set();
|
|
24695
24994
|
const out = [];
|
|
@@ -24745,7 +25044,7 @@ function formatRetro(retros, fromStr, toStr, raw) {
|
|
|
24745
25044
|
const lines = [];
|
|
24746
25045
|
const header = `Retro \u2014 ${fromStr} \u2192 ${toStr}`;
|
|
24747
25046
|
lines.push(raw ? `# ${header}
|
|
24748
|
-
` :
|
|
25047
|
+
` : chalk20.bold(`
|
|
24749
25048
|
${header}
|
|
24750
25049
|
`));
|
|
24751
25050
|
let totalCommits = 0;
|
|
@@ -24755,30 +25054,30 @@ ${header}
|
|
|
24755
25054
|
totalCommits += r.commits.length;
|
|
24756
25055
|
totalPRs += r.mergedPRs.length;
|
|
24757
25056
|
totalShipped += r.shipped.length;
|
|
24758
|
-
lines.push(raw ? `## ${r.name}` :
|
|
24759
|
-
renderList(lines, r.shipped, raw, "Shipped",
|
|
25057
|
+
lines.push(raw ? `## ${r.name}` : chalk20.cyan.bold(`## ${r.name}`));
|
|
25058
|
+
renderList(lines, r.shipped, raw, "Shipped", chalk20.green);
|
|
24760
25059
|
if (r.mergedPRs.length > 0) {
|
|
24761
|
-
lines.push(raw ? `**PRs merged:**` :
|
|
25060
|
+
lines.push(raw ? `**PRs merged:**` : chalk20.green("PRs merged:"));
|
|
24762
25061
|
for (const pr of r.mergedPRs) lines.push(` - ${pr}`);
|
|
24763
25062
|
}
|
|
24764
|
-
renderList(lines, r.inProgress, raw, "In Progress",
|
|
24765
|
-
renderList(lines, r.blocked, raw, "Blocked",
|
|
24766
|
-
renderList(lines, r.decisions, raw, "Key Decisions",
|
|
25063
|
+
renderList(lines, r.inProgress, raw, "In Progress", chalk20.yellow);
|
|
25064
|
+
renderList(lines, r.blocked, raw, "Blocked", chalk20.red);
|
|
25065
|
+
renderList(lines, r.decisions, raw, "Key Decisions", chalk20.magenta);
|
|
24767
25066
|
const metricBits = [
|
|
24768
25067
|
`${r.commits.length} commits`,
|
|
24769
25068
|
`${r.mergedPRs.length} PRs merged`,
|
|
24770
25069
|
`${r.shipped.length} shipped`
|
|
24771
25070
|
];
|
|
24772
|
-
lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` :
|
|
25071
|
+
lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk20.dim(` ${metricBits.join(" \xB7 ")}`));
|
|
24773
25072
|
if (r.shipped.length === 0 && r.commits.length === 0 && r.mergedPRs.length === 0 && r.inProgress.length === 0 && r.blocked.length === 0) {
|
|
24774
|
-
lines.push(raw ? "_(no activity in this window)_" :
|
|
25073
|
+
lines.push(raw ? "_(no activity in this window)_" : chalk20.dim(" (no activity in this window)"));
|
|
24775
25074
|
}
|
|
24776
25075
|
lines.push("");
|
|
24777
25076
|
}
|
|
24778
25077
|
const summary = `${totalShipped} items shipped \xB7 ${totalCommits} commits \xB7 ${totalPRs} PRs merged`;
|
|
24779
25078
|
lines.push(raw ? `---
|
|
24780
25079
|
*${summary}*
|
|
24781
|
-
` :
|
|
25080
|
+
` : chalk20.dim(`--- ${summary} ---
|
|
24782
25081
|
`));
|
|
24783
25082
|
return lines.join("\n");
|
|
24784
25083
|
}
|
|
@@ -24787,7 +25086,7 @@ var retroCommand = new Command18("retro").description("Generate a retro (weekly/
|
|
|
24787
25086
|
const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
24788
25087
|
const projects = Object.entries(config.projects);
|
|
24789
25088
|
if (projects.length === 0) {
|
|
24790
|
-
console.log(
|
|
25089
|
+
console.log(chalk20.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
|
|
24791
25090
|
return;
|
|
24792
25091
|
}
|
|
24793
25092
|
let windowDays = 7;
|
|
@@ -24804,7 +25103,7 @@ var retroCommand = new Command18("retro").description("Generate a retro (weekly/
|
|
|
24804
25103
|
if (opts.project) {
|
|
24805
25104
|
const match = projects.find(([name]) => name === opts.project);
|
|
24806
25105
|
if (!match) {
|
|
24807
|
-
console.error(
|
|
25106
|
+
console.error(chalk20.red(`Project "${opts.project}" not found.`));
|
|
24808
25107
|
process.exit(1);
|
|
24809
25108
|
}
|
|
24810
25109
|
targets = [match];
|
|
@@ -24824,7 +25123,7 @@ init_runtime2();
|
|
|
24824
25123
|
init_dist();
|
|
24825
25124
|
init_dist4();
|
|
24826
25125
|
import { Command as Command19 } from "commander";
|
|
24827
|
-
import
|
|
25126
|
+
import chalk21 from "chalk";
|
|
24828
25127
|
function buildRegistry2() {
|
|
24829
25128
|
return new WorkspaceRegistry({
|
|
24830
25129
|
obsidian: createObsidianDriver
|
|
@@ -24864,7 +25163,7 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
|
|
|
24864
25163
|
const content = await driver.read(path46);
|
|
24865
25164
|
process.stdout.write(content);
|
|
24866
25165
|
} catch (err) {
|
|
24867
|
-
console.error(
|
|
25166
|
+
console.error(chalk21.red(err.message));
|
|
24868
25167
|
process.exit(1);
|
|
24869
25168
|
}
|
|
24870
25169
|
});
|
|
@@ -24894,7 +25193,7 @@ workspaceCommand.command("write").description("Write content to a scope-relative
|
|
|
24894
25193
|
const payload = rawContent === "-" ? await readStdin() : rawContent;
|
|
24895
25194
|
await driver.write(path46, payload);
|
|
24896
25195
|
} catch (err) {
|
|
24897
|
-
console.error(
|
|
25196
|
+
console.error(chalk21.red(err.message));
|
|
24898
25197
|
process.exit(1);
|
|
24899
25198
|
}
|
|
24900
25199
|
});
|
|
@@ -24907,7 +25206,7 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
|
|
|
24907
25206
|
const entries = await driver.list(path46);
|
|
24908
25207
|
for (const entry of entries) console.log(entry);
|
|
24909
25208
|
} catch (err) {
|
|
24910
|
-
console.error(
|
|
25209
|
+
console.error(chalk21.red(err.message));
|
|
24911
25210
|
process.exit(1);
|
|
24912
25211
|
}
|
|
24913
25212
|
});
|
|
@@ -24920,7 +25219,7 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
|
|
|
24920
25219
|
const ok2 = await driver.exists(path46);
|
|
24921
25220
|
process.exit(ok2 ? 0 : 1);
|
|
24922
25221
|
} catch (err) {
|
|
24923
|
-
console.error(
|
|
25222
|
+
console.error(chalk21.red(err.message));
|
|
24924
25223
|
process.exit(2);
|
|
24925
25224
|
}
|
|
24926
25225
|
});
|
|
@@ -24932,7 +25231,7 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
|
|
|
24932
25231
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24933
25232
|
await driver.mkdir(path46);
|
|
24934
25233
|
} catch (err) {
|
|
24935
|
-
console.error(
|
|
25234
|
+
console.error(chalk21.red(err.message));
|
|
24936
25235
|
process.exit(1);
|
|
24937
25236
|
}
|
|
24938
25237
|
});
|
|
@@ -24941,7 +25240,7 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
|
|
|
24941
25240
|
init_dist();
|
|
24942
25241
|
init_dist4();
|
|
24943
25242
|
import { Command as Command20 } from "commander";
|
|
24944
|
-
import
|
|
25243
|
+
import chalk22 from "chalk";
|
|
24945
25244
|
async function readStdin2() {
|
|
24946
25245
|
const chunks = [];
|
|
24947
25246
|
for await (const chunk of process.stdin) {
|
|
@@ -24957,7 +25256,7 @@ var notifyCommand = new Command20("notify").description("Send a message to the u
|
|
|
24957
25256
|
if (!payload) throw new Error("Empty message");
|
|
24958
25257
|
await registry.get(config).notify(payload);
|
|
24959
25258
|
} catch (err) {
|
|
24960
|
-
console.error(
|
|
25259
|
+
console.error(chalk22.red(err.message));
|
|
24961
25260
|
process.exit(1);
|
|
24962
25261
|
}
|
|
24963
25262
|
});
|
|
@@ -24968,8 +25267,8 @@ init_dist3();
|
|
|
24968
25267
|
init_dist4();
|
|
24969
25268
|
init_dist();
|
|
24970
25269
|
import { Command as Command21 } from "commander";
|
|
24971
|
-
import
|
|
24972
|
-
import
|
|
25270
|
+
import chalk23 from "chalk";
|
|
25271
|
+
import fs33 from "fs";
|
|
24973
25272
|
import path36 from "path";
|
|
24974
25273
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
24975
25274
|
function parseScope(v2) {
|
|
@@ -24981,7 +25280,7 @@ function parseScope(v2) {
|
|
|
24981
25280
|
function findPackageRoot3() {
|
|
24982
25281
|
let dir = path36.dirname(fileURLToPath4(import.meta.url));
|
|
24983
25282
|
while (dir !== "/" && dir !== "") {
|
|
24984
|
-
if (
|
|
25283
|
+
if (fs33.existsSync(path36.join(dir, "package.json"))) return dir;
|
|
24985
25284
|
dir = path36.dirname(dir);
|
|
24986
25285
|
}
|
|
24987
25286
|
return process.cwd();
|
|
@@ -25016,17 +25315,17 @@ async function runEmit(opts) {
|
|
|
25016
25315
|
for (const dest of emitter.destinations(scope, projectRoot)) {
|
|
25017
25316
|
const result = await emitter.emit(source, dest, { dryRun: opts.dryRun });
|
|
25018
25317
|
if (opts.dryRun) {
|
|
25019
|
-
console.log(
|
|
25318
|
+
console.log(chalk23.cyan(`[${emitter.name}] ${dest.path}`));
|
|
25020
25319
|
console.log(result.diff ?? "(no diff)");
|
|
25021
25320
|
} else if (result.written) {
|
|
25022
25321
|
console.log(
|
|
25023
|
-
|
|
25322
|
+
chalk23.green(
|
|
25024
25323
|
`\u2714 ${emitter.name} \u2192 ${dest.path} (${result.bytesWritten} bytes)`
|
|
25025
25324
|
)
|
|
25026
25325
|
);
|
|
25027
25326
|
emittedCount.written++;
|
|
25028
25327
|
} else {
|
|
25029
|
-
console.log(
|
|
25328
|
+
console.log(chalk23.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
|
|
25030
25329
|
emittedCount.skipped++;
|
|
25031
25330
|
}
|
|
25032
25331
|
}
|
|
@@ -25049,14 +25348,14 @@ async function runEmit(opts) {
|
|
|
25049
25348
|
`Unknown project '${projectName}'. Available: ${Object.keys(cfg.projects).join(", ") || "(none)"}`
|
|
25050
25349
|
);
|
|
25051
25350
|
}
|
|
25052
|
-
console.error(
|
|
25351
|
+
console.error(chalk23.yellow(`\u26A0 unknown project: ${projectName}`));
|
|
25053
25352
|
continue;
|
|
25054
25353
|
}
|
|
25055
25354
|
const source = await readProjectLevelSource(
|
|
25056
25355
|
createObsidianDriver({ root: proj.path })
|
|
25057
25356
|
);
|
|
25058
25357
|
if (!source) {
|
|
25059
|
-
console.log(
|
|
25358
|
+
console.log(chalk23.gray(`- ${projectName}: no AGENTS.md, skipping`));
|
|
25060
25359
|
continue;
|
|
25061
25360
|
}
|
|
25062
25361
|
for (const name of targets) {
|
|
@@ -25066,7 +25365,7 @@ async function runEmit(opts) {
|
|
|
25066
25365
|
}
|
|
25067
25366
|
if (!opts.dryRun) {
|
|
25068
25367
|
console.log(
|
|
25069
|
-
|
|
25368
|
+
chalk23.bold(
|
|
25070
25369
|
`
|
|
25071
25370
|
Projection complete \u2014 ${emittedCount.written} written, ${emittedCount.skipped} skipped.`
|
|
25072
25371
|
)
|
|
@@ -25080,7 +25379,7 @@ projectionCommand.command("emit").description("Emit projections to disk").option
|
|
|
25080
25379
|
try {
|
|
25081
25380
|
await runEmit({ ...opts, dryRun: false });
|
|
25082
25381
|
} catch (err) {
|
|
25083
|
-
console.error(
|
|
25382
|
+
console.error(chalk23.red(err.message));
|
|
25084
25383
|
process.exit(1);
|
|
25085
25384
|
}
|
|
25086
25385
|
});
|
|
@@ -25088,7 +25387,7 @@ projectionCommand.command("diff").description("Preview changes without writing")
|
|
|
25088
25387
|
try {
|
|
25089
25388
|
await runEmit({ ...opts, dryRun: true });
|
|
25090
25389
|
} catch (err) {
|
|
25091
|
-
console.error(
|
|
25390
|
+
console.error(chalk23.red(err.message));
|
|
25092
25391
|
process.exit(1);
|
|
25093
25392
|
}
|
|
25094
25393
|
});
|
|
@@ -25098,7 +25397,7 @@ projectionCommand.command("list").description("List registered projection target
|
|
|
25098
25397
|
const emitter = registry.get(name);
|
|
25099
25398
|
const userDests = emitter.destinations("user").map((d) => d.path);
|
|
25100
25399
|
const projectDests = emitter.destinations("project", "<project>").map((d) => d.path);
|
|
25101
|
-
console.log(
|
|
25400
|
+
console.log(chalk23.bold(name));
|
|
25102
25401
|
console.log(` user: ${userDests.join(", ") || "(none)"}`);
|
|
25103
25402
|
console.log(` project: ${projectDests.join(", ") || "(none)"}`);
|
|
25104
25403
|
}
|
|
@@ -25178,12 +25477,12 @@ init_dist();
|
|
|
25178
25477
|
init_dist();
|
|
25179
25478
|
init_dist2();
|
|
25180
25479
|
import { Command as Command23 } from "commander";
|
|
25181
|
-
import
|
|
25480
|
+
import fs34 from "fs";
|
|
25182
25481
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
25183
|
-
import { dirname as dirname8, join as
|
|
25184
|
-
import
|
|
25482
|
+
import { dirname as dirname8, join as join31 } from "path";
|
|
25483
|
+
import chalk24 from "chalk";
|
|
25185
25484
|
function runConfigCheck(opts) {
|
|
25186
|
-
const raw = JSON.parse(
|
|
25485
|
+
const raw = JSON.parse(fs34.readFileSync(opts.configPath, "utf-8"));
|
|
25187
25486
|
const def = getDefaultConfig();
|
|
25188
25487
|
const items = detectDrift(raw, def);
|
|
25189
25488
|
let working = raw;
|
|
@@ -25236,14 +25535,14 @@ function runConfigSet(key, value, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
25236
25535
|
}
|
|
25237
25536
|
function printRestartOutcome(outcome) {
|
|
25238
25537
|
if (outcome === "skipped-not-running") {
|
|
25239
|
-
console.log(
|
|
25538
|
+
console.log(chalk24.dim("(daemon not running \u2014 change applies on next start)"));
|
|
25240
25539
|
} else if (outcome === "skipped-opt-out") {
|
|
25241
|
-
console.log(
|
|
25540
|
+
console.log(chalk24.dim("(run 'squadrant heal daemon' to apply)"));
|
|
25242
25541
|
}
|
|
25243
25542
|
}
|
|
25244
25543
|
function runConfigSetAction(opts) {
|
|
25245
25544
|
runConfigSet(opts.key, opts.value, opts.configPath);
|
|
25246
|
-
console.log(
|
|
25545
|
+
console.log(chalk24.green(`\u2714 set ${opts.key} = ${opts.value}`));
|
|
25247
25546
|
if (isDaemonCachedKey(opts.key)) {
|
|
25248
25547
|
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
25249
25548
|
const outcome = doRestart({ reason: `config ${opts.key}`, noRestart: opts.noRestart });
|
|
@@ -25251,9 +25550,9 @@ function runConfigSetAction(opts) {
|
|
|
25251
25550
|
}
|
|
25252
25551
|
}
|
|
25253
25552
|
var SEV_COLOR = {
|
|
25254
|
-
info:
|
|
25255
|
-
advisory:
|
|
25256
|
-
warn:
|
|
25553
|
+
info: chalk24.green,
|
|
25554
|
+
advisory: chalk24.yellow,
|
|
25555
|
+
warn: chalk24.red
|
|
25257
25556
|
};
|
|
25258
25557
|
var KIND_GLYPH = {
|
|
25259
25558
|
missing: "+",
|
|
@@ -25265,14 +25564,14 @@ function printItems(items) {
|
|
|
25265
25564
|
for (const i of items) {
|
|
25266
25565
|
const color = SEV_COLOR[i.severity] ?? ((s) => s);
|
|
25267
25566
|
const detail = i.note ? ` (${i.note})` : i.suggested !== void 0 ? ` \u2192 ${JSON.stringify(i.suggested)}` : "";
|
|
25268
|
-
console.log(" " + color(`${KIND_GLYPH[i.kind]} ${i.kind}: ${i.path}`) +
|
|
25567
|
+
console.log(" " + color(`${KIND_GLYPH[i.kind]} ${i.kind}: ${i.path}`) + chalk24.dim(detail));
|
|
25269
25568
|
}
|
|
25270
25569
|
}
|
|
25271
25570
|
var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
|
|
25272
25571
|
configCommand.command("check").description("Detect config drift vs the current default schema").option("--fix", "Apply the safe tier (add missing, remove deprecated)", false).option("--accept", "Stamp the current version without changing config (dismiss advisories)", false).option("--json", "Output drift items as JSON", false).action((opts) => {
|
|
25273
25572
|
const pkgVersion = readPkgVersion2();
|
|
25274
|
-
if (!
|
|
25275
|
-
console.log(
|
|
25573
|
+
if (!fs34.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
25574
|
+
console.log(chalk24.yellow("No config found \u2014 run `squadrant init` first."));
|
|
25276
25575
|
return;
|
|
25277
25576
|
}
|
|
25278
25577
|
const res = runConfigCheck({ configPath: DEFAULT_CONFIG_PATH, pkgVersion, fix: opts.fix, accept: opts.accept });
|
|
@@ -25281,21 +25580,21 @@ configCommand.command("check").description("Detect config drift vs the current d
|
|
|
25281
25580
|
return;
|
|
25282
25581
|
}
|
|
25283
25582
|
if (res.items.length === 0) {
|
|
25284
|
-
console.log(
|
|
25583
|
+
console.log(chalk24.green("\u2714 Config is in sync with the current schema."));
|
|
25285
25584
|
return;
|
|
25286
25585
|
}
|
|
25287
|
-
console.log(
|
|
25586
|
+
console.log(chalk24.bold("\nConfig drift:\n"));
|
|
25288
25587
|
printItems(res.items);
|
|
25289
25588
|
if (opts.fix && res.applied.length) {
|
|
25290
|
-
console.log(
|
|
25589
|
+
console.log(chalk24.green(`
|
|
25291
25590
|
\u2714 Applied ${res.applied.length} safe item(s): ${res.applied.join(", ")}`));
|
|
25292
25591
|
}
|
|
25293
25592
|
const judgment = res.remaining.filter((i) => i.kind === "changed-default" || i.kind === "invalid");
|
|
25294
25593
|
if (judgment.length) {
|
|
25295
|
-
console.log(
|
|
25594
|
+
console.log(chalk24.yellow(`
|
|
25296
25595
|
${judgment.length} item(s) need review \u2014 run the config-doctor skill, or \`squadrant config check --accept\` to keep your values.`));
|
|
25297
25596
|
} else if (res.stamped) {
|
|
25298
|
-
console.log(
|
|
25597
|
+
console.log(chalk24.green("\n\u2714 Config reconciled and stamped."));
|
|
25299
25598
|
}
|
|
25300
25599
|
});
|
|
25301
25600
|
configCommand.command("get").description("Read a config value by dotted key (e.g. defaults.effort)").argument("<key>", "dotted config key").action((key) => {
|
|
@@ -25303,7 +25602,7 @@ configCommand.command("get").description("Read a config value by dotted key (e.g
|
|
|
25303
25602
|
const value = runConfigGet(key);
|
|
25304
25603
|
console.log(typeof value === "string" ? value : JSON.stringify(value));
|
|
25305
25604
|
} catch (e) {
|
|
25306
|
-
console.error(
|
|
25605
|
+
console.error(chalk24.red(e.message));
|
|
25307
25606
|
process.exit(1);
|
|
25308
25607
|
}
|
|
25309
25608
|
});
|
|
@@ -25311,22 +25610,22 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
25311
25610
|
try {
|
|
25312
25611
|
runConfigSetAction({ key, value, noRestart: opts.restart === false });
|
|
25313
25612
|
} catch (e) {
|
|
25314
|
-
console.error(
|
|
25613
|
+
console.error(chalk24.red(e.message));
|
|
25315
25614
|
process.exit(1);
|
|
25316
25615
|
}
|
|
25317
25616
|
});
|
|
25318
25617
|
function readPkgVersion2() {
|
|
25319
|
-
const pkgPath =
|
|
25320
|
-
return JSON.parse(
|
|
25618
|
+
const pkgPath = join31(dirname8(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
25619
|
+
return JSON.parse(fs34.readFileSync(pkgPath, "utf-8")).version;
|
|
25321
25620
|
}
|
|
25322
25621
|
|
|
25323
25622
|
// packages/cli/src/commands/heal.ts
|
|
25324
25623
|
init_dist();
|
|
25325
25624
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
25326
|
-
import { join as
|
|
25625
|
+
import { join as join32 } from "path";
|
|
25327
25626
|
import { homedir as homedir17 } from "os";
|
|
25328
25627
|
import { Command as Command24 } from "commander";
|
|
25329
|
-
import
|
|
25628
|
+
import chalk25 from "chalk";
|
|
25330
25629
|
init_dist2();
|
|
25331
25630
|
init_dist2();
|
|
25332
25631
|
init_dist4();
|
|
@@ -25384,11 +25683,11 @@ async function runHealStatus(opts) {
|
|
|
25384
25683
|
return result.healthy ? 0 : 2;
|
|
25385
25684
|
}
|
|
25386
25685
|
if (result.healthy) {
|
|
25387
|
-
stdout.write(
|
|
25686
|
+
stdout.write(chalk25.green("\u2714 all components healthy\n"));
|
|
25388
25687
|
for (const c of result.components) {
|
|
25389
25688
|
if (c.kind === "delivery" && c.state === "stale") {
|
|
25390
|
-
const glyph =
|
|
25391
|
-
const stateColor2 =
|
|
25689
|
+
const glyph = chalk25.yellow("\u2022");
|
|
25690
|
+
const stateColor2 = chalk25.yellow;
|
|
25392
25691
|
stdout.write(` ${glyph} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${stateColor2(c.state.padEnd(8))} ${c.project}
|
|
25393
25692
|
`);
|
|
25394
25693
|
if (c.detail) {
|
|
@@ -25399,13 +25698,13 @@ async function runHealStatus(opts) {
|
|
|
25399
25698
|
}
|
|
25400
25699
|
return 0;
|
|
25401
25700
|
}
|
|
25402
|
-
stdout.write(
|
|
25701
|
+
stdout.write(chalk25.bold("Unhealthy components:\n\n"));
|
|
25403
25702
|
for (const c of result.components) {
|
|
25404
25703
|
const isUnhealthy = c.kind === "delivery" ? c.stuck === true : c.healCmd !== null;
|
|
25405
25704
|
const isAdvisoryDelivery = c.kind === "delivery" && c.state === "stale" && c.stuck !== true;
|
|
25406
25705
|
if (isUnhealthy || isAdvisoryDelivery) {
|
|
25407
|
-
const glyph = c.state === "stale" || isAdvisoryDelivery ?
|
|
25408
|
-
const stateColor2 = c.state === "stale" || isAdvisoryDelivery ?
|
|
25706
|
+
const glyph = c.state === "stale" || isAdvisoryDelivery ? chalk25.yellow("\u2022") : chalk25.red("\u2718");
|
|
25707
|
+
const stateColor2 = c.state === "stale" || isAdvisoryDelivery ? chalk25.yellow : chalk25.red;
|
|
25409
25708
|
stdout.write(` ${glyph} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${stateColor2(c.state.padEnd(8))} ${c.project}
|
|
25410
25709
|
`);
|
|
25411
25710
|
if (c.detail) {
|
|
@@ -25413,7 +25712,7 @@ async function runHealStatus(opts) {
|
|
|
25413
25712
|
`);
|
|
25414
25713
|
}
|
|
25415
25714
|
if (c.healCmd) {
|
|
25416
|
-
stdout.write(` heal: ${
|
|
25715
|
+
stdout.write(` heal: ${chalk25.cyan(c.healCmd)}
|
|
25417
25716
|
`);
|
|
25418
25717
|
}
|
|
25419
25718
|
}
|
|
@@ -25426,7 +25725,7 @@ async function runHealDaemon(opts) {
|
|
|
25426
25725
|
try {
|
|
25427
25726
|
const result = opts.ensureDaemon();
|
|
25428
25727
|
const noteSuffix = result?.note ? ` (${result.note})` : "";
|
|
25429
|
-
stdout.write(
|
|
25728
|
+
stdout.write(chalk25.green(`\u2714 daemon kickstart complete${noteSuffix}
|
|
25430
25729
|
`));
|
|
25431
25730
|
return 0;
|
|
25432
25731
|
} catch (e) {
|
|
@@ -25481,12 +25780,12 @@ async function runHealCaptain(projects, deps) {
|
|
|
25481
25780
|
for (const project of projects) {
|
|
25482
25781
|
const resolved = resolveLiveCaptain(records, project, deps.isPidAlive);
|
|
25483
25782
|
if (!resolved) {
|
|
25484
|
-
stdout.write(` ${
|
|
25783
|
+
stdout.write(` ${chalk25.yellow("\u2013")} ${project}: no live captain found \u2014 nothing to heal
|
|
25485
25784
|
`);
|
|
25486
25785
|
continue;
|
|
25487
25786
|
}
|
|
25488
25787
|
if (entryMatchesResolved(deps.getEntry(project), resolved)) {
|
|
25489
|
-
stdout.write(` ${
|
|
25788
|
+
stdout.write(` ${chalk25.green("\u2714")} ${project}: already healthy (pid=${resolved.pid})
|
|
25490
25789
|
`);
|
|
25491
25790
|
continue;
|
|
25492
25791
|
}
|
|
@@ -25516,16 +25815,16 @@ async function runHealCaptain(projects, deps) {
|
|
|
25516
25815
|
for (const { project, resolved } of targets) {
|
|
25517
25816
|
if (stillMissingSet.has(project)) {
|
|
25518
25817
|
stderr.write(
|
|
25519
|
-
` ${
|
|
25818
|
+
` ${chalk25.red("\u2718")} ${project}: heal did not survive the daemon restart after one retry \u2014 pid=${resolved.pid} sessionId=${resolved.sessionId} may be racing the daemon's own periodic persist
|
|
25520
25819
|
`
|
|
25521
25820
|
);
|
|
25522
25821
|
} else {
|
|
25523
|
-
stdout.write(` ${
|
|
25822
|
+
stdout.write(` ${chalk25.green("\u2714")} ${project}: re-adopted captain pid=${resolved.pid} sessionId=${resolved.sessionId}
|
|
25524
25823
|
`);
|
|
25525
25824
|
}
|
|
25526
25825
|
}
|
|
25527
25826
|
if (stillMissingSet.size > 0) return 1;
|
|
25528
|
-
stdout.write(
|
|
25827
|
+
stdout.write(chalk25.green(`\u2714 healed ${targets.length} captain(s); daemon kickstarted
|
|
25529
25828
|
`));
|
|
25530
25829
|
return 0;
|
|
25531
25830
|
}
|
|
@@ -25563,8 +25862,8 @@ var healCommand = new Command24("heal").description("Targeted, idempotent remedi
|
|
|
25563
25862
|
}
|
|
25564
25863
|
const config = loadConfig();
|
|
25565
25864
|
const projects = opts.all ? Object.keys(config.projects) : [project];
|
|
25566
|
-
const stateRoot =
|
|
25567
|
-
const registry = new LivenessRegistry({ path:
|
|
25865
|
+
const stateRoot = join32(homedir17(), ".config", "squadrant", "state");
|
|
25866
|
+
const registry = new LivenessRegistry({ path: join32(stateRoot, "liveness.json") });
|
|
25568
25867
|
registry.load();
|
|
25569
25868
|
const code = await runHealCaptain(projects, {
|
|
25570
25869
|
liveness: readCmuxLiveness,
|
|
@@ -25590,14 +25889,14 @@ var healCommand = new Command24("heal").description("Targeted, idempotent remedi
|
|
|
25590
25889
|
|
|
25591
25890
|
// packages/cli/src/commands/group.ts
|
|
25592
25891
|
import { Command as Command26 } from "commander";
|
|
25593
|
-
import
|
|
25892
|
+
import chalk27 from "chalk";
|
|
25594
25893
|
|
|
25595
25894
|
// packages/cli/src/commands/dispatch.ts
|
|
25596
25895
|
init_dist();
|
|
25597
25896
|
init_dist2();
|
|
25598
25897
|
import { Command as Command25 } from "commander";
|
|
25599
25898
|
import { execSync as execSync13 } from "child_process";
|
|
25600
|
-
import
|
|
25899
|
+
import chalk26 from "chalk";
|
|
25601
25900
|
async function runDispatch(toProject, task, opts) {
|
|
25602
25901
|
const fromProject = resolveCurrentProject(loadConfig());
|
|
25603
25902
|
if (!fromProject) {
|
|
@@ -25622,11 +25921,11 @@ async function runDispatch(toProject, task, opts) {
|
|
|
25622
25921
|
async function dispatchAction(toProject, task, opts) {
|
|
25623
25922
|
try {
|
|
25624
25923
|
const result = await runDispatch(toProject, task, opts);
|
|
25625
|
-
console.log(
|
|
25626
|
-
console.log(
|
|
25627
|
-
console.log(
|
|
25924
|
+
console.log(chalk26.green(`\u2714 Dispatched to '${toProject}' (task ${result.id.slice(0, 8)})`));
|
|
25925
|
+
console.log(chalk26.dim(` originProject: ${result.originProject ?? "none"}`));
|
|
25926
|
+
console.log(chalk26.dim(" You will be notified when the task settles (done/blocked/failed)."));
|
|
25628
25927
|
} catch (e) {
|
|
25629
|
-
console.error(
|
|
25928
|
+
console.error(chalk26.red(`\u2718 ${e.message}`));
|
|
25630
25929
|
process.exit(1);
|
|
25631
25930
|
}
|
|
25632
25931
|
}
|
|
@@ -25636,7 +25935,7 @@ var dispatchCommand = new Command25("dispatch").description("Dispatch a task to
|
|
|
25636
25935
|
init_dist2();
|
|
25637
25936
|
var groupCommand = new Command26("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
|
|
25638
25937
|
new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v2) => parseInt(v2, 10) * 1e3).action(async (toProject, task, opts) => {
|
|
25639
|
-
console.error(
|
|
25938
|
+
console.error(chalk27.yellow(
|
|
25640
25939
|
`\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
|
|
25641
25940
|
));
|
|
25642
25941
|
await dispatchAction(toProject, task, opts);
|
|
@@ -25652,101 +25951,6 @@ import { join as join33, dirname as dirname9 } from "path";
|
|
|
25652
25951
|
import { homedir as homedir18 } from "os";
|
|
25653
25952
|
import { Command as Command27 } from "commander";
|
|
25654
25953
|
import chalk28 from "chalk";
|
|
25655
|
-
|
|
25656
|
-
// packages/cli/src/lib/captain-channel-factory.ts
|
|
25657
|
-
init_dist3();
|
|
25658
|
-
init_dist2();
|
|
25659
|
-
import { createServer as createServer6, connect as netConnect2 } from "net";
|
|
25660
|
-
import fs34 from "fs";
|
|
25661
|
-
import { join as join32 } from "path";
|
|
25662
|
-
import { randomUUID as randomUUID7 } from "crypto";
|
|
25663
|
-
import chalk27 from "chalk";
|
|
25664
|
-
var shared;
|
|
25665
|
-
var registryEntryPath = () => join32(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
25666
|
-
function unregisterSenderIdentity() {
|
|
25667
|
-
try {
|
|
25668
|
-
fs34.unlinkSync(registryEntryPath());
|
|
25669
|
-
} catch {
|
|
25670
|
-
}
|
|
25671
|
-
}
|
|
25672
|
-
function registerSenderIdentity(socketPath2) {
|
|
25673
|
-
try {
|
|
25674
|
-
unregisterSenderIdentity();
|
|
25675
|
-
fs34.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
25676
|
-
fs34.writeFileSync(
|
|
25677
|
-
registryEntryPath(),
|
|
25678
|
-
JSON.stringify({
|
|
25679
|
-
pid: process.pid,
|
|
25680
|
-
sessionId: randomUUID7(),
|
|
25681
|
-
name: "squadrantd",
|
|
25682
|
-
messagingSocketPath: socketPath2,
|
|
25683
|
-
kind: "daemon",
|
|
25684
|
-
peerProtocol: 1
|
|
25685
|
-
})
|
|
25686
|
-
);
|
|
25687
|
-
process.on("exit", unregisterSenderIdentity);
|
|
25688
|
-
} catch {
|
|
25689
|
-
}
|
|
25690
|
-
}
|
|
25691
|
-
async function sharedReceiptListener() {
|
|
25692
|
-
if (shared) return shared;
|
|
25693
|
-
ensureSocksDir();
|
|
25694
|
-
const socketPath2 = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
|
|
25695
|
-
const listener = new ClaudeReceiptListener({
|
|
25696
|
-
socketPath: socketPath2,
|
|
25697
|
-
createServer: (h) => createServer6(h),
|
|
25698
|
-
// A UDS path is not cleaned up when a process is killed, so our own leftover
|
|
25699
|
-
// must never be the reason we refuse to start.
|
|
25700
|
-
unlinkStale: (p) => {
|
|
25701
|
-
try {
|
|
25702
|
-
fs34.unlinkSync(p);
|
|
25703
|
-
} catch {
|
|
25704
|
-
}
|
|
25705
|
-
},
|
|
25706
|
-
log: (m) => console.error(chalk27.dim(m))
|
|
25707
|
-
});
|
|
25708
|
-
await listener.start();
|
|
25709
|
-
registerSenderIdentity(socketPath2);
|
|
25710
|
-
shared = listener;
|
|
25711
|
-
return shared;
|
|
25712
|
-
}
|
|
25713
|
-
function captainSessionIdFor(taskId) {
|
|
25714
|
-
return readClaudeStatusBySocketPath(captainSocketPath(taskId))?.sessionId;
|
|
25715
|
-
}
|
|
25716
|
-
async function buildCaptainChannel() {
|
|
25717
|
-
const receipts = await sharedReceiptListener();
|
|
25718
|
-
return new ClaudePeerChannel({
|
|
25719
|
-
// The port's taskId IS the project name for captains.
|
|
25720
|
-
socketPathFor: (taskId) => captainSocketPath(taskId),
|
|
25721
|
-
sessionIdFor: captainSessionIdFor,
|
|
25722
|
-
// Captains are identified in the registry by their cwd (the project path).
|
|
25723
|
-
// Resolve by the socket path squadrant chose at launch, NOT by cwd: a captain
|
|
25724
|
-
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
25725
|
-
// missed and every captain ping reported accepted-unconfirmed.
|
|
25726
|
-
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
25727
|
-
wire: (p, e) => writeLine(p, e, { connect: (path46) => netConnect2(path46) }),
|
|
25728
|
-
receipts,
|
|
25729
|
-
newMsgId: () => randomUUID7(),
|
|
25730
|
-
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
25731
|
-
log: (m) => console.error(chalk27.dim(m))
|
|
25732
|
-
});
|
|
25733
|
-
}
|
|
25734
|
-
async function buildCaptainChannels(opts) {
|
|
25735
|
-
const claude = await buildCaptainChannel();
|
|
25736
|
-
const opencode = new OpencodeHttpChannel({
|
|
25737
|
-
portFor: (project) => readCaptainAddress(opts.stateRoot, project)?.port,
|
|
25738
|
-
sessionFor: (project) => readCaptainAddress(opts.stateRoot, project)?.sessionId
|
|
25739
|
-
});
|
|
25740
|
-
return {
|
|
25741
|
-
channels: { claude, opencode },
|
|
25742
|
-
agentFor: (project) => resolveCaptainAgent(opts.stateRoot, project, opts.configAgent)
|
|
25743
|
-
};
|
|
25744
|
-
}
|
|
25745
|
-
function resolveCaptainAgent(stateRoot, project, configAgent) {
|
|
25746
|
-
return readCaptainAddress(stateRoot, project)?.agent ?? configAgent;
|
|
25747
|
-
}
|
|
25748
|
-
|
|
25749
|
-
// packages/cli/src/commands/ping.ts
|
|
25750
25954
|
function formatPingResult(project, outcome) {
|
|
25751
25955
|
if (!outcome) return `\u2714 Pinged '${project}'`;
|
|
25752
25956
|
switch (outcome.status) {
|