squadrant 0.26.1 → 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 +1186 -730
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +566 -171
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/rules-ops/SKILL.md +2 -0
package/dist/index.js
CHANGED
|
@@ -594,22 +594,22 @@ function defaultCmuxConfigPath() {
|
|
|
594
594
|
return join(homedir(), ".config", "cmux", "cmux.json");
|
|
595
595
|
}
|
|
596
596
|
function ensureSocketAutomation(opts = {}) {
|
|
597
|
-
const
|
|
598
|
-
if (!existsSync(
|
|
599
|
-
mkdirSync(dirname(
|
|
600
|
-
writeFileSync(
|
|
601
|
-
return { path:
|
|
597
|
+
const path46 = opts.path ?? defaultCmuxConfigPath();
|
|
598
|
+
if (!existsSync(path46)) {
|
|
599
|
+
mkdirSync(dirname(path46), { recursive: true });
|
|
600
|
+
writeFileSync(path46, MINIMAL_TEMPLATE);
|
|
601
|
+
return { path: path46, changed: true, alreadySet: false };
|
|
602
602
|
}
|
|
603
|
-
const text = readFileSync(
|
|
603
|
+
const text = readFileSync(path46, "utf-8");
|
|
604
604
|
const current = parse(text)?.automation?.socketControlMode;
|
|
605
605
|
if (current === AUTOMATION_MODE) {
|
|
606
|
-
return { path:
|
|
606
|
+
return { path: path46, changed: false, alreadySet: true };
|
|
607
607
|
}
|
|
608
608
|
const edits = modify(text, [...SOCKET_CONTROL_MODE_PATH], AUTOMATION_MODE, {
|
|
609
609
|
formattingOptions: { insertSpaces: true, tabSize: 2 }
|
|
610
610
|
});
|
|
611
|
-
writeFileSync(
|
|
612
|
-
return { path:
|
|
611
|
+
writeFileSync(path46, applyEdits(text, edits));
|
|
612
|
+
return { path: path46, changed: true, alreadySet: false };
|
|
613
613
|
}
|
|
614
614
|
var SOCKET_CONTROL_MODE_PATH, AUTOMATION_MODE, MINIMAL_TEMPLATE;
|
|
615
615
|
var init_cmux_config = __esm({
|
|
@@ -762,9 +762,9 @@ import { join as join4 } from "path";
|
|
|
762
762
|
function defaultStatePath() {
|
|
763
763
|
return join4(homedir3(), ".config", "squadrant", "state", "cmux-autoconfig.json");
|
|
764
764
|
}
|
|
765
|
-
function readState(
|
|
765
|
+
function readState(path46) {
|
|
766
766
|
try {
|
|
767
|
-
return JSON.parse(readConfigFileSync(
|
|
767
|
+
return JSON.parse(readConfigFileSync(path46));
|
|
768
768
|
} catch {
|
|
769
769
|
return {};
|
|
770
770
|
}
|
|
@@ -925,18 +925,18 @@ function detectDrift(user, def) {
|
|
|
925
925
|
}
|
|
926
926
|
}
|
|
927
927
|
}
|
|
928
|
-
const checkBackend = (
|
|
928
|
+
const checkBackend = (path46, backend, agentName) => {
|
|
929
929
|
if (backend === void 0)
|
|
930
930
|
return;
|
|
931
931
|
if (!isBackendMode(backend)) {
|
|
932
|
-
items.push({ path:
|
|
932
|
+
items.push({ path: path46, kind: "invalid", severity: "warn", current: backend, note: "unknown backend; expected native|direct|proxy" });
|
|
933
933
|
return;
|
|
934
934
|
}
|
|
935
935
|
if (backend !== "native") {
|
|
936
936
|
if (agentName !== "claude") {
|
|
937
|
-
items.push({ path:
|
|
937
|
+
items.push({ path: path46, kind: "invalid", severity: "warn", current: backend, note: `backend '${backend}' is claude-only (agent '${agentName ?? "?"}')` });
|
|
938
938
|
} else if (!router) {
|
|
939
|
-
items.push({ path:
|
|
939
|
+
items.push({ path: path46, kind: "invalid", severity: "warn", current: backend, note: `backend '${backend}' requires defaults.router` });
|
|
940
940
|
}
|
|
941
941
|
}
|
|
942
942
|
};
|
|
@@ -1124,8 +1124,8 @@ async function notifyIfUpdateAvailable(opts) {
|
|
|
1124
1124
|
const statePath2 = opts.statePath ?? UPDATE_CHECK_STATE_PATH;
|
|
1125
1125
|
const readState2 = opts.readState ?? readUpdateCheckState;
|
|
1126
1126
|
const writeState = opts.writeState ?? writeUpdateCheckState;
|
|
1127
|
-
const write = opts.write ?? ((
|
|
1128
|
-
${
|
|
1127
|
+
const write = opts.write ?? ((line2) => process.stderr.write(`
|
|
1128
|
+
${line2}
|
|
1129
1129
|
`));
|
|
1130
1130
|
const outcome = await checkForUpdate({
|
|
1131
1131
|
currentVersion: opts.currentVersion,
|
|
@@ -1480,8 +1480,8 @@ function parseSkill(raw) {
|
|
|
1480
1480
|
return null;
|
|
1481
1481
|
const [, fmBlock, body] = match;
|
|
1482
1482
|
const fm = {};
|
|
1483
|
-
for (const
|
|
1484
|
-
const kv =
|
|
1483
|
+
for (const line2 of fmBlock.split("\n")) {
|
|
1484
|
+
const kv = line2.match(/^(\w+):\s*(.+)$/);
|
|
1485
1485
|
if (kv)
|
|
1486
1486
|
fm[kv[1]] = kv[2].trim();
|
|
1487
1487
|
}
|
|
@@ -1595,8 +1595,8 @@ async function readDailyLog(workspace, dateStr) {
|
|
|
1595
1595
|
const blockerMatch = content.match(/## Blocked\n([\s\S]*?)(?=\n##|$)/);
|
|
1596
1596
|
if (blockerMatch) {
|
|
1597
1597
|
const lines = blockerMatch[1].trim().split("\n");
|
|
1598
|
-
for (const
|
|
1599
|
-
const trimmed =
|
|
1598
|
+
for (const line2 of lines) {
|
|
1599
|
+
const trimmed = line2.replace(/^[-*]\s*/, "").trim();
|
|
1600
1600
|
if (trimmed && trimmed !== "(none)" && trimmed !== "None") {
|
|
1601
1601
|
blockers.push(trimmed);
|
|
1602
1602
|
}
|
|
@@ -2702,12 +2702,12 @@ async function* readFromCursor(opts) {
|
|
|
2702
2702
|
continue;
|
|
2703
2703
|
throw e;
|
|
2704
2704
|
}
|
|
2705
|
-
for (const
|
|
2706
|
-
if (!
|
|
2705
|
+
for (const line2 of buf.split("\n")) {
|
|
2706
|
+
if (!line2.trim())
|
|
2707
2707
|
continue;
|
|
2708
2708
|
let entry;
|
|
2709
2709
|
try {
|
|
2710
|
-
entry = JSON.parse(
|
|
2710
|
+
entry = JSON.parse(line2);
|
|
2711
2711
|
} catch {
|
|
2712
2712
|
continue;
|
|
2713
2713
|
}
|
|
@@ -2809,17 +2809,17 @@ function createDecoder(onParseError) {
|
|
|
2809
2809
|
const out = [];
|
|
2810
2810
|
let idx;
|
|
2811
2811
|
while ((idx = buf.indexOf("\n")) >= 0) {
|
|
2812
|
-
const
|
|
2812
|
+
const line2 = buf.slice(0, idx);
|
|
2813
2813
|
buf = buf.slice(idx + 1);
|
|
2814
|
-
if (!
|
|
2814
|
+
if (!line2.trim())
|
|
2815
2815
|
continue;
|
|
2816
2816
|
try {
|
|
2817
|
-
const parsed = JSON.parse(
|
|
2817
|
+
const parsed = JSON.parse(line2);
|
|
2818
2818
|
if (typeof parsed === "object" && parsed !== null && parsed.type === "_keepalive")
|
|
2819
2819
|
continue;
|
|
2820
2820
|
out.push(parsed);
|
|
2821
2821
|
} catch {
|
|
2822
|
-
onParseError?.(
|
|
2822
|
+
onParseError?.(line2);
|
|
2823
2823
|
}
|
|
2824
2824
|
}
|
|
2825
2825
|
return out;
|
|
@@ -2979,11 +2979,11 @@ function encodeFrame(f) {
|
|
|
2979
2979
|
}
|
|
2980
2980
|
function decodeFrames(wire) {
|
|
2981
2981
|
const out = [];
|
|
2982
|
-
for (const
|
|
2983
|
-
if (!
|
|
2982
|
+
for (const line2 of wire.split("\n")) {
|
|
2983
|
+
if (!line2.trim())
|
|
2984
2984
|
continue;
|
|
2985
2985
|
try {
|
|
2986
|
-
const parsed = JSON.parse(
|
|
2986
|
+
const parsed = JSON.parse(line2);
|
|
2987
2987
|
if (parsed.type === "_keepalive")
|
|
2988
2988
|
continue;
|
|
2989
2989
|
out.push(parsed);
|
|
@@ -3465,10 +3465,10 @@ function detectForeignInstall(parsed, thisEntry, registeredEntryExists) {
|
|
|
3465
3465
|
isUpgrade: isVersionUpgrade(parsed.daemonEntry, thisEntry)
|
|
3466
3466
|
};
|
|
3467
3467
|
}
|
|
3468
|
-
function sanitizePathForPlist(
|
|
3468
|
+
function sanitizePathForPlist(path46) {
|
|
3469
3469
|
const seen = /* @__PURE__ */ new Set();
|
|
3470
3470
|
const stable = [];
|
|
3471
|
-
for (const p of
|
|
3471
|
+
for (const p of path46.split(":")) {
|
|
3472
3472
|
if (!p)
|
|
3473
3473
|
continue;
|
|
3474
3474
|
if (p.includes("/.claude/plugins/"))
|
|
@@ -4171,14 +4171,14 @@ function detectTrailingQuestion(text) {
|
|
|
4171
4171
|
let inFence = false;
|
|
4172
4172
|
let lastLine = null;
|
|
4173
4173
|
for (const raw of text.split(/\r?\n/)) {
|
|
4174
|
-
const
|
|
4175
|
-
if (
|
|
4174
|
+
const line2 = raw.trim();
|
|
4175
|
+
if (line2.startsWith("```")) {
|
|
4176
4176
|
inFence = !inFence;
|
|
4177
4177
|
continue;
|
|
4178
4178
|
}
|
|
4179
|
-
if (inFence ||
|
|
4179
|
+
if (inFence || line2 === "")
|
|
4180
4180
|
continue;
|
|
4181
|
-
lastLine =
|
|
4181
|
+
lastLine = line2;
|
|
4182
4182
|
}
|
|
4183
4183
|
if (lastLine && lastLine.endsWith("?"))
|
|
4184
4184
|
return lastLine;
|
|
@@ -4189,9 +4189,9 @@ function isQuotedLine(raw) {
|
|
|
4189
4189
|
return QUOTED_PREFIX_RE.test(noAnsi);
|
|
4190
4190
|
}
|
|
4191
4191
|
function stripChrome(raw) {
|
|
4192
|
-
let
|
|
4193
|
-
|
|
4194
|
-
const trimmed =
|
|
4192
|
+
let line2 = raw.replace(/\[[0-9;]*m/g, "");
|
|
4193
|
+
line2 = line2.replace(/^[\s│┃▏▕|]+/, "").replace(/[\s│┃▏▕|]+$/, "");
|
|
4194
|
+
const trimmed = line2.trim();
|
|
4195
4195
|
if (trimmed === "")
|
|
4196
4196
|
return null;
|
|
4197
4197
|
if (PURE_CHROME_RE.test(trimmed))
|
|
@@ -4852,12 +4852,12 @@ function extractUserIntentFromTranscript(jsonl) {
|
|
|
4852
4852
|
return null;
|
|
4853
4853
|
const lines = jsonl.split(/\r?\n/);
|
|
4854
4854
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
4855
|
-
const
|
|
4856
|
-
if (!
|
|
4855
|
+
const line2 = lines[i].trim();
|
|
4856
|
+
if (!line2)
|
|
4857
4857
|
continue;
|
|
4858
4858
|
let entry;
|
|
4859
4859
|
try {
|
|
4860
|
-
entry = JSON.parse(
|
|
4860
|
+
entry = JSON.parse(line2);
|
|
4861
4861
|
} catch {
|
|
4862
4862
|
continue;
|
|
4863
4863
|
}
|
|
@@ -5306,10 +5306,10 @@ async function reapCrewChildren(taskId, graceMs = 2e3, execFn = nodeExec) {
|
|
|
5306
5306
|
execFn("ps auxE", { maxBuffer: 64 * 1024 * 1024 }, (err, out) => err ? reject(err) : resolve4(out));
|
|
5307
5307
|
});
|
|
5308
5308
|
const pids = [];
|
|
5309
|
-
for (const
|
|
5310
|
-
if (!
|
|
5309
|
+
for (const line2 of stdout.split("\n").slice(1)) {
|
|
5310
|
+
if (!line2.includes(marker))
|
|
5311
5311
|
continue;
|
|
5312
|
-
const pid = parseInt(
|
|
5312
|
+
const pid = parseInt(line2.trim().split(/\s+/)[1], 10);
|
|
5313
5313
|
if (!isNaN(pid) && pid !== process.pid)
|
|
5314
5314
|
pids.push(pid);
|
|
5315
5315
|
}
|
|
@@ -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;
|
|
@@ -6004,11 +6008,11 @@ var init_captain_record = __esm({
|
|
|
6004
6008
|
// packages/core/dist/opencode-session.js
|
|
6005
6009
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
6006
6010
|
async function listSessions(port, fetchImpl = fetch, timeoutMs = 5e3) {
|
|
6007
|
-
for (const
|
|
6011
|
+
for (const path46 of ["/session", "/api/session"]) {
|
|
6008
6012
|
const ac = new AbortController();
|
|
6009
6013
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
6010
6014
|
try {
|
|
6011
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
6015
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path46}`, { signal: ac.signal });
|
|
6012
6016
|
if (!res.ok)
|
|
6013
6017
|
continue;
|
|
6014
6018
|
const rows = await res.json();
|
|
@@ -6023,8 +6027,8 @@ async function listSessions(port, fetchImpl = fetch, timeoutMs = 5e3) {
|
|
|
6023
6027
|
}
|
|
6024
6028
|
function parseLiveOpencodeServers(psOutput) {
|
|
6025
6029
|
const out = [];
|
|
6026
|
-
for (const
|
|
6027
|
-
const m =
|
|
6030
|
+
for (const line2 of psOutput.split("\n")) {
|
|
6031
|
+
const m = line2.match(/^\s*(\d+)\s+(.+)$/);
|
|
6028
6032
|
if (!m)
|
|
6029
6033
|
continue;
|
|
6030
6034
|
const pid = Number(m[1]);
|
|
@@ -6050,8 +6054,8 @@ function defaultPsOutput() {
|
|
|
6050
6054
|
function defaultCwdOf(pid) {
|
|
6051
6055
|
try {
|
|
6052
6056
|
const out = execFileSync4("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], { encoding: "utf-8", timeout: 2e3 });
|
|
6053
|
-
const
|
|
6054
|
-
return
|
|
6057
|
+
const line2 = out.split("\n").find((l) => l.startsWith("n"));
|
|
6058
|
+
return line2 ? line2.slice(1) : null;
|
|
6055
6059
|
} catch {
|
|
6056
6060
|
return null;
|
|
6057
6061
|
}
|
|
@@ -6701,10 +6705,10 @@ function distBuiltAt() {
|
|
|
6701
6705
|
return 0;
|
|
6702
6706
|
}
|
|
6703
6707
|
}
|
|
6704
|
-
function gatherLogStats(
|
|
6708
|
+
function gatherLogStats(path46, now, windowMs) {
|
|
6705
6709
|
let sizeBytes = 0;
|
|
6706
6710
|
try {
|
|
6707
|
-
sizeBytes = statSync3(
|
|
6711
|
+
sizeBytes = statSync3(path46).size;
|
|
6708
6712
|
} catch {
|
|
6709
6713
|
return { errorCount: 0, sizeBytes: 0, windowMs };
|
|
6710
6714
|
}
|
|
@@ -6715,7 +6719,7 @@ function gatherLogStats(path45, now, windowMs) {
|
|
|
6715
6719
|
const len = sizeBytes - start;
|
|
6716
6720
|
let text = "";
|
|
6717
6721
|
try {
|
|
6718
|
-
const fd = openSync2(
|
|
6722
|
+
const fd = openSync2(path46, "r");
|
|
6719
6723
|
try {
|
|
6720
6724
|
const buf = Buffer.alloc(len);
|
|
6721
6725
|
readSync(fd, buf, 0, len, start);
|
|
@@ -6728,10 +6732,10 @@ function gatherLogStats(path45, now, windowMs) {
|
|
|
6728
6732
|
}
|
|
6729
6733
|
const cutoff = now - windowMs;
|
|
6730
6734
|
let errorCount = 0;
|
|
6731
|
-
for (const
|
|
6732
|
-
if (!/error|failed/i.test(
|
|
6735
|
+
for (const line2 of text.split("\n")) {
|
|
6736
|
+
if (!/error|failed/i.test(line2))
|
|
6733
6737
|
continue;
|
|
6734
|
-
const m =
|
|
6738
|
+
const m = line2.match(/\d{4}-\d{2}-\d{2}T[\d:.]+Z/);
|
|
6735
6739
|
if (m) {
|
|
6736
6740
|
const ts = Date.parse(m[0]);
|
|
6737
6741
|
if (!Number.isNaN(ts) && ts < cutoff)
|
|
@@ -8906,10 +8910,10 @@ function scan(bufRef, acc) {
|
|
|
8906
8910
|
let saw = false;
|
|
8907
8911
|
let idx;
|
|
8908
8912
|
while ((idx = bufRef.value.indexOf("\n")) !== -1) {
|
|
8909
|
-
const
|
|
8913
|
+
const line2 = bufRef.value.slice(0, idx).trim();
|
|
8910
8914
|
bufRef.value = bufRef.value.slice(idx + 1);
|
|
8911
|
-
if (
|
|
8912
|
-
const payload =
|
|
8915
|
+
if (line2.startsWith("data:")) {
|
|
8916
|
+
const payload = line2.slice(5).trim();
|
|
8913
8917
|
if (payload && payload !== "[DONE]") {
|
|
8914
8918
|
try {
|
|
8915
8919
|
if (absorb(JSON.parse(payload), acc))
|
|
@@ -8960,8 +8964,8 @@ var init_stream = __esm({
|
|
|
8960
8964
|
// packages/core/dist/router/shim.js
|
|
8961
8965
|
import { createServer as createServer3 } from "http";
|
|
8962
8966
|
import { Readable } from "stream";
|
|
8963
|
-
function joinUrl(base,
|
|
8964
|
-
return base.replace(/\/+$/, "") +
|
|
8967
|
+
function joinUrl(base, path46) {
|
|
8968
|
+
return base.replace(/\/+$/, "") + path46;
|
|
8965
8969
|
}
|
|
8966
8970
|
async function readBody(req) {
|
|
8967
8971
|
const chunks = [];
|
|
@@ -9137,8 +9141,8 @@ function createRouterShim(opts) {
|
|
|
9137
9141
|
writeJson(res, 200, { input_tokens: Math.max(1, Math.ceil(text.length / 4)) });
|
|
9138
9142
|
}
|
|
9139
9143
|
function handler(req, res) {
|
|
9140
|
-
const
|
|
9141
|
-
if (req.method === "POST" &&
|
|
9144
|
+
const path46 = pathnameOf(req.url);
|
|
9145
|
+
if (req.method === "POST" && path46 === "/v1/messages") {
|
|
9142
9146
|
void handleMessages(req, res).catch((err) => {
|
|
9143
9147
|
log(`router internal error: ${err instanceof Error ? err.message : String(err)}`);
|
|
9144
9148
|
if (!res.headersSent) {
|
|
@@ -9150,18 +9154,18 @@ function createRouterShim(opts) {
|
|
|
9150
9154
|
});
|
|
9151
9155
|
return;
|
|
9152
9156
|
}
|
|
9153
|
-
if (req.method === "POST" &&
|
|
9157
|
+
if (req.method === "POST" && path46 === "/v1/messages/count_tokens") {
|
|
9154
9158
|
void handleCountTokens(req, res).catch(() => {
|
|
9155
9159
|
if (!res.headersSent)
|
|
9156
9160
|
writeJson(res, 502, anthropicError(502, "api_error", "router internal error").body);
|
|
9157
9161
|
});
|
|
9158
9162
|
return;
|
|
9159
9163
|
}
|
|
9160
|
-
if (req.method === "GET" &&
|
|
9164
|
+
if (req.method === "GET" && path46 === "/v1/models") {
|
|
9161
9165
|
writeJson(res, 200, modelsList([...advertisedModels]));
|
|
9162
9166
|
return;
|
|
9163
9167
|
}
|
|
9164
|
-
if (req.method === "GET" &&
|
|
9168
|
+
if (req.method === "GET" && path46 === "/healthz") {
|
|
9165
9169
|
writeJson(res, 200, { ready: server?.listening === true, upstreamReachable, lastError });
|
|
9166
9170
|
return;
|
|
9167
9171
|
}
|
|
@@ -9430,11 +9434,11 @@ async function probeCaptainChannel(project, opts = {}) {
|
|
|
9430
9434
|
}
|
|
9431
9435
|
}
|
|
9432
9436
|
async function httpReachable(port, fetchImpl, timeoutMs) {
|
|
9433
|
-
for (const
|
|
9437
|
+
for (const path46 of ["/session", "/api/session"]) {
|
|
9434
9438
|
const ac = new AbortController();
|
|
9435
9439
|
const t = setTimeout(() => ac.abort(), timeoutMs);
|
|
9436
9440
|
try {
|
|
9437
|
-
const res = await fetchImpl(`http://127.0.0.1:${port}${
|
|
9441
|
+
const res = await fetchImpl(`http://127.0.0.1:${port}${path46}`, { signal: ac.signal });
|
|
9438
9442
|
if (res.ok)
|
|
9439
9443
|
return true;
|
|
9440
9444
|
} catch {
|
|
@@ -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) {
|
|
@@ -10617,28 +10697,28 @@ function requireSnippet() {
|
|
|
10617
10697
|
const maxLineLength = options.maxLength - (options.indent + lineNoLength + 3);
|
|
10618
10698
|
for (let i = 1; i <= options.linesBefore; i++) {
|
|
10619
10699
|
if (foundLineNo - i < 0) break;
|
|
10620
|
-
const
|
|
10700
|
+
const line22 = getLine(
|
|
10621
10701
|
mark2.buffer,
|
|
10622
10702
|
lineStarts[foundLineNo - i],
|
|
10623
10703
|
lineEnds[foundLineNo - i],
|
|
10624
10704
|
mark2.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),
|
|
10625
10705
|
maxLineLength
|
|
10626
10706
|
);
|
|
10627
|
-
result = common2.repeat(" ", options.indent) + padStart((mark2.line - i + 1).toString(), lineNoLength) + " | " +
|
|
10707
|
+
result = common2.repeat(" ", options.indent) + padStart((mark2.line - i + 1).toString(), lineNoLength) + " | " + line22.str + "\n" + result;
|
|
10628
10708
|
}
|
|
10629
|
-
const
|
|
10630
|
-
result += common2.repeat(" ", options.indent) + padStart((mark2.line + 1).toString(), lineNoLength) + " | " +
|
|
10631
|
-
result += common2.repeat("-", options.indent + lineNoLength + 3 +
|
|
10709
|
+
const line2 = getLine(mark2.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark2.position, maxLineLength);
|
|
10710
|
+
result += common2.repeat(" ", options.indent) + padStart((mark2.line + 1).toString(), lineNoLength) + " | " + line2.str + "\n";
|
|
10711
|
+
result += common2.repeat("-", options.indent + lineNoLength + 3 + line2.pos) + "^\n";
|
|
10632
10712
|
for (let i = 1; i <= options.linesAfter; i++) {
|
|
10633
10713
|
if (foundLineNo + i >= lineEnds.length) break;
|
|
10634
|
-
const
|
|
10714
|
+
const line22 = getLine(
|
|
10635
10715
|
mark2.buffer,
|
|
10636
10716
|
lineStarts[foundLineNo + i],
|
|
10637
10717
|
lineEnds[foundLineNo + i],
|
|
10638
10718
|
mark2.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),
|
|
10639
10719
|
maxLineLength
|
|
10640
10720
|
);
|
|
10641
|
-
result += common2.repeat(" ", options.indent) + padStart((mark2.line + i + 1).toString(), lineNoLength) + " | " +
|
|
10721
|
+
result += common2.repeat(" ", options.indent) + padStart((mark2.line + i + 1).toString(), lineNoLength) + " | " + line22.str + "\n";
|
|
10642
10722
|
}
|
|
10643
10723
|
return result.replace(/\n$/, "");
|
|
10644
10724
|
}
|
|
@@ -12925,17 +13005,17 @@ function requireDumper() {
|
|
|
12925
13005
|
let result = "";
|
|
12926
13006
|
const length = string.length;
|
|
12927
13007
|
while (position < length) {
|
|
12928
|
-
let
|
|
13008
|
+
let line2;
|
|
12929
13009
|
const next = string.indexOf("\n", position);
|
|
12930
13010
|
if (next === -1) {
|
|
12931
|
-
|
|
13011
|
+
line2 = string.slice(position);
|
|
12932
13012
|
position = length;
|
|
12933
13013
|
} else {
|
|
12934
|
-
|
|
13014
|
+
line2 = string.slice(position, next + 1);
|
|
12935
13015
|
position = next + 1;
|
|
12936
13016
|
}
|
|
12937
|
-
if (
|
|
12938
|
-
result +=
|
|
13017
|
+
if (line2.length && line2 !== "\n") result += ind;
|
|
13018
|
+
result += line2;
|
|
12939
13019
|
}
|
|
12940
13020
|
return result;
|
|
12941
13021
|
}
|
|
@@ -13120,15 +13200,15 @@ function requireDumper() {
|
|
|
13120
13200
|
let match;
|
|
13121
13201
|
while (match = lineRe.exec(string)) {
|
|
13122
13202
|
const prefix = match[1];
|
|
13123
|
-
const
|
|
13124
|
-
moreIndented =
|
|
13125
|
-
result += prefix + (!prevMoreIndented && !moreIndented &&
|
|
13203
|
+
const line2 = match[2];
|
|
13204
|
+
moreIndented = line2[0] === " ";
|
|
13205
|
+
result += prefix + (!prevMoreIndented && !moreIndented && line2 !== "" ? "\n" : "") + foldLine(line2, width);
|
|
13126
13206
|
prevMoreIndented = moreIndented;
|
|
13127
13207
|
}
|
|
13128
13208
|
return result;
|
|
13129
13209
|
}
|
|
13130
|
-
function foldLine(
|
|
13131
|
-
if (
|
|
13210
|
+
function foldLine(line2, width) {
|
|
13211
|
+
if (line2 === "" || line2[0] === " ") return line2;
|
|
13132
13212
|
const breakRe = / [^ ]/g;
|
|
13133
13213
|
let match;
|
|
13134
13214
|
let start = 0;
|
|
@@ -13136,20 +13216,20 @@ function requireDumper() {
|
|
|
13136
13216
|
let curr = 0;
|
|
13137
13217
|
let next = 0;
|
|
13138
13218
|
let result = "";
|
|
13139
|
-
while (match = breakRe.exec(
|
|
13219
|
+
while (match = breakRe.exec(line2)) {
|
|
13140
13220
|
next = match.index;
|
|
13141
13221
|
if (next - start > width) {
|
|
13142
13222
|
end = curr > start ? curr : next;
|
|
13143
|
-
result += "\n" +
|
|
13223
|
+
result += "\n" + line2.slice(start, end);
|
|
13144
13224
|
start = end + 1;
|
|
13145
13225
|
}
|
|
13146
13226
|
curr = next;
|
|
13147
13227
|
}
|
|
13148
13228
|
result += "\n";
|
|
13149
|
-
if (
|
|
13150
|
-
result +=
|
|
13229
|
+
if (line2.length - start > width && curr > start) {
|
|
13230
|
+
result += line2.slice(start, curr) + "\n" + line2.slice(curr + 1);
|
|
13151
13231
|
} else {
|
|
13152
|
-
result +=
|
|
13232
|
+
result += line2.slice(start);
|
|
13153
13233
|
}
|
|
13154
13234
|
return result.slice(1);
|
|
13155
13235
|
}
|
|
@@ -13558,6 +13638,220 @@ var init_sources = __esm({
|
|
|
13558
13638
|
}
|
|
13559
13639
|
});
|
|
13560
13640
|
|
|
13641
|
+
// packages/core/dist/knowledge/inject.js
|
|
13642
|
+
import fs19 from "fs";
|
|
13643
|
+
import path18 from "path";
|
|
13644
|
+
function sessionOrder(a, b) {
|
|
13645
|
+
const proj = (r) => r.layer.startsWith("project:") ? 0 : 1;
|
|
13646
|
+
const mod = (r) => r.modality === "must-not" ? 0 : 1;
|
|
13647
|
+
return proj(a) - proj(b) || mod(a) - mod(b) || a.id.localeCompare(b.id);
|
|
13648
|
+
}
|
|
13649
|
+
function formatSessionContext(kbs, rules, cap = SESSION_RULE_CAP) {
|
|
13650
|
+
const hard = rules.filter((r) => r.status === "active" && (r.modality === "must" || r.modality === "must-not")).sort(sessionOrder);
|
|
13651
|
+
const shown = hard.slice(0, cap);
|
|
13652
|
+
const lines = [
|
|
13653
|
+
`<squadrant-project-rules kb="${kbs.join(",")}">`,
|
|
13654
|
+
`Rules KB: ${kbs.join(", ")}. ${DATA_NOTE}`,
|
|
13655
|
+
...shown.map(line)
|
|
13656
|
+
];
|
|
13657
|
+
if (hard.length > shown.length)
|
|
13658
|
+
lines.push(`+${hard.length - shown.length} more: squadrant rules list --brief`);
|
|
13659
|
+
lines.push("Look up rules for a task with `squadrant rules search <terms>` / `squadrant rules show <id>` (rules-ops skill).");
|
|
13660
|
+
lines.push("</squadrant-project-rules>");
|
|
13661
|
+
return { text: lines.join("\n"), ids: shown.map((r) => r.id) };
|
|
13662
|
+
}
|
|
13663
|
+
function cleanPromptQuery(prompt) {
|
|
13664
|
+
const text = prompt.replace(/```[\s\S]*?(```|$)/g, " ").split("\n").filter((l) => l.length <= MAX_LINE_CHARS).join("\n").slice(0, MAX_PROMPT_CHARS).toLowerCase().replace(/https?:\/\/\S+/g, " ");
|
|
13665
|
+
const terms = [];
|
|
13666
|
+
const add = (t) => {
|
|
13667
|
+
if (t.length < 3 && !SHORT_TERMS.has(t) || /^\d+$/.test(t) || STOPWORDS.has(t) || terms.includes(t))
|
|
13668
|
+
return;
|
|
13669
|
+
terms.push(t);
|
|
13670
|
+
};
|
|
13671
|
+
for (const raw of text.split(/[^a-z0-9._-]+/)) {
|
|
13672
|
+
const t = raw.replace(/^[._-]+|[._-]+$/g, "");
|
|
13673
|
+
if (STOPWORDS.has(t))
|
|
13674
|
+
continue;
|
|
13675
|
+
add(t);
|
|
13676
|
+
const parts = t.split(/[._-]+/);
|
|
13677
|
+
if (parts.length > 1 && parts.every((x) => x.length >= 3 && !PREFIXES.has(x)))
|
|
13678
|
+
parts.forEach(add);
|
|
13679
|
+
if (terms.length >= MAX_TERMS)
|
|
13680
|
+
break;
|
|
13681
|
+
}
|
|
13682
|
+
return terms.slice(0, MAX_TERMS).join(" ");
|
|
13683
|
+
}
|
|
13684
|
+
function wholeWordTerms(rule2, terms) {
|
|
13685
|
+
const text = [
|
|
13686
|
+
rule2.id,
|
|
13687
|
+
...rule2.triggers?.keywords ?? [],
|
|
13688
|
+
...rule2.triggers?.expanded ?? [],
|
|
13689
|
+
rule2.triggers?.when ?? "",
|
|
13690
|
+
rule2.statement
|
|
13691
|
+
].join("\n").toLowerCase();
|
|
13692
|
+
return terms.filter((t) => {
|
|
13693
|
+
const base = t.length > 4 ? t.replace(/(es|s)$/, "") : t;
|
|
13694
|
+
return new RegExp(`(^|[^a-z0-9])(?:${escapeRe(t)}|${escapeRe(base)}(?:s|es)?)($|[^a-z0-9])`).test(text);
|
|
13695
|
+
});
|
|
13696
|
+
}
|
|
13697
|
+
function selectPromptHits(hits, query, seen, opts = {}) {
|
|
13698
|
+
const minScore = opts.minScore ?? PROMPT_MIN_SCORE;
|
|
13699
|
+
const terms = query.split(" ").filter(Boolean);
|
|
13700
|
+
return hits.filter((h) => {
|
|
13701
|
+
if (seen.has(h.rule.id))
|
|
13702
|
+
return false;
|
|
13703
|
+
const n = wholeWordTerms(h.rule, terms).length;
|
|
13704
|
+
return n >= 2 && h.score >= minScore || n === 1 && h.score >= PROMPT_SINGLE_TERM_MIN_SCORE;
|
|
13705
|
+
}).slice(0, opts.cap ?? PROMPT_RULE_CAP);
|
|
13706
|
+
}
|
|
13707
|
+
function isSystemPrompt(prompt) {
|
|
13708
|
+
const t = prompt.trimStart();
|
|
13709
|
+
return /^(⚠|🗒|\[stale\b|CREW [A-Z]|Another Claude session sent a message)/u.test(t) || t.includes("<cross-session-message");
|
|
13710
|
+
}
|
|
13711
|
+
function promptText(prompt) {
|
|
13712
|
+
const m = BRIEF_POINTER_RE.exec(prompt.trimStart());
|
|
13713
|
+
if (!m)
|
|
13714
|
+
return prompt;
|
|
13715
|
+
try {
|
|
13716
|
+
return fs19.readFileSync(m[1], "utf8").slice(0, MAX_PROMPT_CHARS * 2);
|
|
13717
|
+
} catch {
|
|
13718
|
+
return prompt;
|
|
13719
|
+
}
|
|
13720
|
+
}
|
|
13721
|
+
function formatPromptContext(hits) {
|
|
13722
|
+
return [
|
|
13723
|
+
'<squadrant-project-rules match="prompt">',
|
|
13724
|
+
`Rules that may apply to this prompt. ${DATA_NOTE}`,
|
|
13725
|
+
...hits.map((h) => line(h.rule)),
|
|
13726
|
+
"</squadrant-project-rules>"
|
|
13727
|
+
].join("\n");
|
|
13728
|
+
}
|
|
13729
|
+
function injectStateDir(stateRoot) {
|
|
13730
|
+
return path18.join(stateRoot, "rules-inject");
|
|
13731
|
+
}
|
|
13732
|
+
function readSeenRules(stateRoot, sessionId) {
|
|
13733
|
+
try {
|
|
13734
|
+
const ids = JSON.parse(fs19.readFileSync(sessionFile(stateRoot, sessionId), "utf8"));
|
|
13735
|
+
return new Set(Array.isArray(ids) ? ids.filter((x) => typeof x === "string") : []);
|
|
13736
|
+
} catch {
|
|
13737
|
+
return /* @__PURE__ */ new Set();
|
|
13738
|
+
}
|
|
13739
|
+
}
|
|
13740
|
+
function recordSeenRules(stateRoot, sessionId, ids) {
|
|
13741
|
+
if (!ids.length)
|
|
13742
|
+
return;
|
|
13743
|
+
const seen = readSeenRules(stateRoot, sessionId);
|
|
13744
|
+
for (const id of ids)
|
|
13745
|
+
seen.add(id);
|
|
13746
|
+
fs19.mkdirSync(injectStateDir(stateRoot), { recursive: true });
|
|
13747
|
+
fs19.writeFileSync(sessionFile(stateRoot, sessionId), JSON.stringify([...seen]));
|
|
13748
|
+
}
|
|
13749
|
+
function pruneSeenRules(stateRoot, now = Date.now()) {
|
|
13750
|
+
const dir = injectStateDir(stateRoot);
|
|
13751
|
+
let names;
|
|
13752
|
+
try {
|
|
13753
|
+
names = fs19.readdirSync(dir);
|
|
13754
|
+
} catch {
|
|
13755
|
+
return;
|
|
13756
|
+
}
|
|
13757
|
+
for (const n of names) {
|
|
13758
|
+
const f = path18.join(dir, n);
|
|
13759
|
+
try {
|
|
13760
|
+
if (now - fs19.statSync(f).mtimeMs > SEEN_TTL_MS)
|
|
13761
|
+
fs19.unlinkSync(f);
|
|
13762
|
+
} catch {
|
|
13763
|
+
}
|
|
13764
|
+
}
|
|
13765
|
+
}
|
|
13766
|
+
async function withBudget(fn, budgetMs = INJECT_BUDGET_MS) {
|
|
13767
|
+
const start = Date.now();
|
|
13768
|
+
let timer;
|
|
13769
|
+
try {
|
|
13770
|
+
const timeout = new Promise((resolve4) => {
|
|
13771
|
+
timer = setTimeout(() => resolve4(null), budgetMs);
|
|
13772
|
+
});
|
|
13773
|
+
const out = await Promise.race([Promise.resolve().then(fn), timeout]);
|
|
13774
|
+
return Date.now() - start > budgetMs ? null : out;
|
|
13775
|
+
} catch {
|
|
13776
|
+
return null;
|
|
13777
|
+
} finally {
|
|
13778
|
+
clearTimeout(timer);
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13781
|
+
function resolveInjectProject(cfg, cwd, env, project) {
|
|
13782
|
+
const named = project ?? env.SQUADRANT_CREW_PROJECT;
|
|
13783
|
+
if (named)
|
|
13784
|
+
return cfg.projects[named] ? named : null;
|
|
13785
|
+
let best = null;
|
|
13786
|
+
let bestLen = -1;
|
|
13787
|
+
for (const [name, pc] of Object.entries(cfg.projects)) {
|
|
13788
|
+
const root = resolveHome(pc.path);
|
|
13789
|
+
if ((cwd === root || cwd.startsWith(root + path18.sep)) && root.length > bestLen) {
|
|
13790
|
+
best = name;
|
|
13791
|
+
bestLen = root.length;
|
|
13792
|
+
}
|
|
13793
|
+
}
|
|
13794
|
+
return best;
|
|
13795
|
+
}
|
|
13796
|
+
function computeRulesInjection(i) {
|
|
13797
|
+
if (i.env.SQUADRANT_RULES_INJECT === "0")
|
|
13798
|
+
return null;
|
|
13799
|
+
const p = typeof i.payload === "object" && i.payload !== null ? i.payload : {};
|
|
13800
|
+
const cwd = typeof p.cwd === "string" && p.cwd ? p.cwd : i.cwd;
|
|
13801
|
+
const project = resolveInjectProject(i.cfg, cwd, i.env, i.project);
|
|
13802
|
+
if (!project)
|
|
13803
|
+
return null;
|
|
13804
|
+
const kbs = subscribedKbs(i.cfg, project);
|
|
13805
|
+
if (!kbs.length)
|
|
13806
|
+
return null;
|
|
13807
|
+
const sessionId = typeof p.session_id === "string" && p.session_id ? p.session_id : null;
|
|
13808
|
+
if (i.event === "session-start") {
|
|
13809
|
+
const { text, ids } = formatSessionContext(kbs, resolveProjectRules(i.cfg, project).rules);
|
|
13810
|
+
if (sessionId) {
|
|
13811
|
+
pruneSeenRules(i.stateRoot);
|
|
13812
|
+
recordSeenRules(i.stateRoot, sessionId, ids);
|
|
13813
|
+
}
|
|
13814
|
+
return text;
|
|
13815
|
+
}
|
|
13816
|
+
if (typeof p.prompt !== "string" || isSystemPrompt(p.prompt))
|
|
13817
|
+
return null;
|
|
13818
|
+
const query = cleanPromptQuery(promptText(p.prompt));
|
|
13819
|
+
if (!query)
|
|
13820
|
+
return null;
|
|
13821
|
+
const seen = sessionId ? readSeenRules(i.stateRoot, sessionId) : /* @__PURE__ */ new Set();
|
|
13822
|
+
const picked = selectPromptHits(searchRules(resolveProjectRules(i.cfg, project).rules, query), query, seen);
|
|
13823
|
+
if (!picked.length)
|
|
13824
|
+
return null;
|
|
13825
|
+
if (sessionId)
|
|
13826
|
+
recordSeenRules(i.stateRoot, sessionId, picked.map((h) => h.rule.id));
|
|
13827
|
+
return formatPromptContext(picked);
|
|
13828
|
+
}
|
|
13829
|
+
var SESSION_RULE_CAP, PROMPT_RULE_CAP, PROMPT_MIN_SCORE, PROMPT_SINGLE_TERM_MIN_SCORE, INJECT_BUDGET_MS, MAX_PROMPT_CHARS, MAX_LINE_CHARS, MAX_TERMS, SHORT_TERMS, PREFIXES, SEEN_TTL_MS, STOPWORDS, DATA_NOTE, line, escapeRe, BRIEF_POINTER_RE, sessionFile;
|
|
13830
|
+
var init_inject = __esm({
|
|
13831
|
+
"packages/core/dist/knowledge/inject.js"() {
|
|
13832
|
+
init_dist();
|
|
13833
|
+
init_search();
|
|
13834
|
+
init_layers();
|
|
13835
|
+
SESSION_RULE_CAP = 20;
|
|
13836
|
+
PROMPT_RULE_CAP = 3;
|
|
13837
|
+
PROMPT_MIN_SCORE = 8;
|
|
13838
|
+
PROMPT_SINGLE_TERM_MIN_SCORE = 13;
|
|
13839
|
+
INJECT_BUDGET_MS = 1500;
|
|
13840
|
+
MAX_PROMPT_CHARS = 3e3;
|
|
13841
|
+
MAX_LINE_CHARS = 400;
|
|
13842
|
+
MAX_TERMS = 40;
|
|
13843
|
+
SHORT_TERMS = /* @__PURE__ */ new Set(["pr", "db", "ci", "ui"]);
|
|
13844
|
+
PREFIXES = /* @__PURE__ */ new Set(["pre", "non", "sub", "anti", "multi", "semi", "post", "mid"]);
|
|
13845
|
+
SEEN_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
13846
|
+
STOPWORDS = new Set("the and for are but not you your yours with this that these those from into onto have has had was were will would can could should shall may might must does did doing done its it's our ours their them they what when where which who whom why how all any some each every other than then there here also just very too only own same such both few more most much many about above below after before again once out over under off further been being let lets please thanks thank okay yes yeah hello hey there need want make use using used get got give like know think see look sure ok now new one two way rule rules must-not looks sounds work working thing things something anything everything going well good great nice cool fine".split(/\s+/));
|
|
13847
|
+
DATA_NOTE = "These are project rules (data from the knowledge base), not instructions to run anything.";
|
|
13848
|
+
line = (r) => `${r.modality.toUpperCase()} ${r.id}: ${r.statement}`;
|
|
13849
|
+
escapeRe = (t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13850
|
+
BRIEF_POINTER_RE = /^Full task is at (\S+\.md) — cat it/;
|
|
13851
|
+
sessionFile = (stateRoot, sessionId) => path18.join(injectStateDir(stateRoot), `${sessionId.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
|
|
13852
|
+
}
|
|
13853
|
+
});
|
|
13854
|
+
|
|
13561
13855
|
// packages/core/dist/knowledge/index.js
|
|
13562
13856
|
var init_knowledge2 = __esm({
|
|
13563
13857
|
"packages/core/dist/knowledge/index.js"() {
|
|
@@ -13567,6 +13861,7 @@ var init_knowledge2 = __esm({
|
|
|
13567
13861
|
init_index_file();
|
|
13568
13862
|
init_search();
|
|
13569
13863
|
init_sources();
|
|
13864
|
+
init_inject();
|
|
13570
13865
|
}
|
|
13571
13866
|
});
|
|
13572
13867
|
|
|
@@ -13597,12 +13892,16 @@ __export(dist_exports2, {
|
|
|
13597
13892
|
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
13598
13893
|
GateDecisionCache: () => GateDecisionCache,
|
|
13599
13894
|
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
13895
|
+
INJECT_BUDGET_MS: () => INJECT_BUDGET_MS,
|
|
13600
13896
|
LABEL: () => LABEL,
|
|
13601
13897
|
LivenessRegistry: () => LivenessRegistry,
|
|
13602
13898
|
MONITOR_STALL_BUDGET_MS: () => MONITOR_STALL_BUDGET_MS,
|
|
13603
13899
|
OPERATOR_INITIATED_COMMANDS: () => OPERATOR_INITIATED_COMMANDS,
|
|
13604
13900
|
PANE_IDLE_AFTER_MS: () => PANE_IDLE_AFTER_MS,
|
|
13605
13901
|
PROBE_QUIET_MS: () => PROBE_QUIET_MS,
|
|
13902
|
+
PROMPT_MIN_SCORE: () => PROMPT_MIN_SCORE,
|
|
13903
|
+
PROMPT_RULE_CAP: () => PROMPT_RULE_CAP,
|
|
13904
|
+
PROMPT_SINGLE_TERM_MIN_SCORE: () => PROMPT_SINGLE_TERM_MIN_SCORE,
|
|
13606
13905
|
PROPOSED_DIR: () => PROPOSED_DIR,
|
|
13607
13906
|
PROTOCOL_VERSION: () => PROTOCOL_VERSION,
|
|
13608
13907
|
READ_ONLY_CREW_SUBCOMMANDS: () => READ_ONLY_CREW_SUBCOMMANDS,
|
|
@@ -13610,6 +13909,7 @@ __export(dist_exports2, {
|
|
|
13610
13909
|
ROUTER_READY_ATTEMPTS: () => ROUTER_READY_ATTEMPTS,
|
|
13611
13910
|
ROUTER_READY_DELAY_MS: () => ROUTER_READY_DELAY_MS,
|
|
13612
13911
|
SERVER_TOOL_TYPE_RE: () => SERVER_TOOL_TYPE_RE,
|
|
13912
|
+
SESSION_RULE_CAP: () => SESSION_RULE_CAP,
|
|
13613
13913
|
SIDE_SESSION_ENV: () => SIDE_SESSION_ENV,
|
|
13614
13914
|
SOURCES_TEMPLATE: () => SOURCES_TEMPLATE,
|
|
13615
13915
|
STALE_THRESHOLD_MS: () => STALE_THRESHOLD_MS,
|
|
@@ -13650,9 +13950,11 @@ __export(dist_exports2, {
|
|
|
13650
13950
|
classifyCaptainPane: () => classifyCaptainPane,
|
|
13651
13951
|
classifyHealth: () => classifyHealth,
|
|
13652
13952
|
claudeEnvShadowsRouter: () => claudeEnvShadowsRouter,
|
|
13953
|
+
cleanPromptQuery: () => cleanPromptQuery,
|
|
13653
13954
|
clearPending: () => clearPending,
|
|
13654
13955
|
closeWorkItem: () => closeWorkItem,
|
|
13655
13956
|
compileIndex: () => compileIndex,
|
|
13957
|
+
computeRulesInjection: () => computeRulesInjection,
|
|
13656
13958
|
computeTemplateHash: () => computeTemplateHash,
|
|
13657
13959
|
consumeExitMarker: () => consumeExitMarker,
|
|
13658
13960
|
createAttach: () => createAttach,
|
|
@@ -13693,6 +13995,7 @@ __export(dist_exports2, {
|
|
|
13693
13995
|
defaultIsPidAlive: () => defaultIsPidAlive,
|
|
13694
13996
|
defaultListenError: () => defaultListenError,
|
|
13695
13997
|
defaultWorkRoot: () => defaultWorkRoot,
|
|
13998
|
+
deliverFirstTurnViaPeer: () => deliverFirstTurnViaPeer,
|
|
13696
13999
|
deliverStartupPrompt: () => deliverStartupPrompt,
|
|
13697
14000
|
deliverToCaptain: () => deliverToCaptain,
|
|
13698
14001
|
deliverable: () => deliverable,
|
|
@@ -13726,6 +14029,8 @@ __export(dist_exports2, {
|
|
|
13726
14029
|
formatLifecycle: () => formatLifecycle,
|
|
13727
14030
|
formatMediaReceipt: () => formatMediaReceipt,
|
|
13728
14031
|
formatPermissionDecision: () => formatPermissionDecision,
|
|
14032
|
+
formatPromptContext: () => formatPromptContext,
|
|
14033
|
+
formatSessionContext: () => formatSessionContext,
|
|
13729
14034
|
formatUsageLine: () => formatUsageLine,
|
|
13730
14035
|
formatWatchdogWarning: () => formatWatchdogWarning,
|
|
13731
14036
|
freshTrace: () => freshTrace,
|
|
@@ -13735,6 +14040,7 @@ __export(dist_exports2, {
|
|
|
13735
14040
|
hasModalOptionList: () => hasModalOptionList,
|
|
13736
14041
|
healCmdFor: () => healCmdFor,
|
|
13737
14042
|
inboundBody: () => inboundBody,
|
|
14043
|
+
injectStateDir: () => injectStateDir,
|
|
13738
14044
|
isAuthorized: () => isAuthorized,
|
|
13739
14045
|
isBareSpawn: () => isBareSpawn,
|
|
13740
14046
|
isCaptainAlive: () => isCaptainAlive,
|
|
@@ -13750,6 +14056,7 @@ __export(dist_exports2, {
|
|
|
13750
14056
|
isReadOnlyTopLevelCommand: () => isReadOnlyTopLevelCommand,
|
|
13751
14057
|
isSideTitle: () => isSideTitle,
|
|
13752
14058
|
isStickyAttention: () => isStickyAttention,
|
|
14059
|
+
isSystemPrompt: () => isSystemPrompt,
|
|
13753
14060
|
isTurnAccepted: () => isTurnAccepted,
|
|
13754
14061
|
isVersionUpgrade: () => isVersionUpgrade,
|
|
13755
14062
|
joinUrl: () => joinUrl,
|
|
@@ -13795,6 +14102,7 @@ __export(dist_exports2, {
|
|
|
13795
14102
|
programArgsBlock: () => programArgsBlock,
|
|
13796
14103
|
projectHealth: () => projectHealth,
|
|
13797
14104
|
projectRulesDir: () => projectRulesDir,
|
|
14105
|
+
pruneSeenRules: () => pruneSeenRules,
|
|
13798
14106
|
pruneTopics: () => pruneTopics,
|
|
13799
14107
|
purgeExpiredWorkItems: () => purgeExpiredWorkItems,
|
|
13800
14108
|
readCaptainAddress: () => readCaptainAddress,
|
|
@@ -13802,11 +14110,13 @@ __export(dist_exports2, {
|
|
|
13802
14110
|
readFromCursor: () => readFromCursor,
|
|
13803
14111
|
readIndex: () => readIndex,
|
|
13804
14112
|
readRunningMarker: () => readRunningMarker,
|
|
14113
|
+
readSeenRules: () => readSeenRules,
|
|
13805
14114
|
readUserIntent: () => readUserIntent,
|
|
13806
14115
|
realpathOrSelf: () => realpathOrSelf,
|
|
13807
14116
|
reapCrewChildren: () => reapCrewChildren,
|
|
13808
14117
|
reapOrphanedCrews: () => reapOrphanedCrews,
|
|
13809
14118
|
reconcileLiveness: () => reconcileLiveness,
|
|
14119
|
+
recordSeenRules: () => recordSeenRules,
|
|
13810
14120
|
recordSession: () => recordSession,
|
|
13811
14121
|
recoverStall: () => recoverStall,
|
|
13812
14122
|
reduce: () => reduce,
|
|
@@ -13828,6 +14138,7 @@ __export(dist_exports2, {
|
|
|
13828
14138
|
resolveGateMode: () => resolveGateMode,
|
|
13829
14139
|
resolveGatePolicy: () => resolveGatePolicy,
|
|
13830
14140
|
resolveGateTools: () => resolveGateTools,
|
|
14141
|
+
resolveInjectProject: () => resolveInjectProject,
|
|
13831
14142
|
resolveProject: () => resolveProject,
|
|
13832
14143
|
resolveProjectRules: () => resolveProjectRules,
|
|
13833
14144
|
resolveRouterCredentials: () => resolveRouterCredentials,
|
|
@@ -13866,7 +14177,9 @@ __export(dist_exports2, {
|
|
|
13866
14177
|
saveState: () => saveState,
|
|
13867
14178
|
screenHasSplashMarker: () => screenHasSplashMarker,
|
|
13868
14179
|
screenHash: () => screenHash,
|
|
14180
|
+
screenShowsTrustPanel: () => screenShowsTrustPanel,
|
|
13869
14181
|
searchRules: () => searchRules,
|
|
14182
|
+
selectPromptHits: () => selectPromptHits,
|
|
13870
14183
|
sendRequest: () => sendRequest,
|
|
13871
14184
|
setLastUserId: () => setLastUserId,
|
|
13872
14185
|
setNotify: () => setNotify,
|
|
@@ -13890,11 +14203,14 @@ __export(dist_exports2, {
|
|
|
13890
14203
|
toControlEvent: () => toControlEvent,
|
|
13891
14204
|
topicKey: () => topicKey,
|
|
13892
14205
|
topicName: () => topicName,
|
|
14206
|
+
trustPanelAcceptKeys: () => trustPanelAcceptKeys,
|
|
13893
14207
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
13894
14208
|
usageFromJson: () => usageFromJson,
|
|
13895
14209
|
validateKb: () => validateKb,
|
|
13896
14210
|
waitForCaptainDelivery: () => waitForCaptainDelivery,
|
|
13897
14211
|
waitForWarmup: () => waitForWarmup,
|
|
14212
|
+
wholeWordTerms: () => wholeWordTerms,
|
|
14213
|
+
withBudget: () => withBudget,
|
|
13898
14214
|
writeCaptainAddress: () => writeCaptainAddress,
|
|
13899
14215
|
writeCursor: () => writeCursor,
|
|
13900
14216
|
writeExitMarker: () => writeExitMarker,
|
|
@@ -13950,6 +14266,7 @@ var init_dist2 = __esm({
|
|
|
13950
14266
|
init_conformance();
|
|
13951
14267
|
init_source();
|
|
13952
14268
|
init_control_channel();
|
|
14269
|
+
init_peer_first_turn();
|
|
13953
14270
|
init_captain_channel();
|
|
13954
14271
|
init_captain_record();
|
|
13955
14272
|
init_opencode_session();
|
|
@@ -13958,7 +14275,7 @@ var init_dist2 = __esm({
|
|
|
13958
14275
|
});
|
|
13959
14276
|
|
|
13960
14277
|
// packages/agents/dist/claude/registry.js
|
|
13961
|
-
import
|
|
14278
|
+
import fs20 from "fs";
|
|
13962
14279
|
import { join as join17 } from "path";
|
|
13963
14280
|
import { homedir as homedir7 } from "os";
|
|
13964
14281
|
function parseRegistryDir(files, readFile7) {
|
|
@@ -14003,11 +14320,11 @@ function readClaudeStatus(task) {
|
|
|
14003
14320
|
return void 0;
|
|
14004
14321
|
let files;
|
|
14005
14322
|
try {
|
|
14006
|
-
files =
|
|
14323
|
+
files = fs20.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
14007
14324
|
} catch {
|
|
14008
14325
|
return void 0;
|
|
14009
14326
|
}
|
|
14010
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
14327
|
+
const entries = parseRegistryDir(files, (name) => fs20.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
14011
14328
|
let entry;
|
|
14012
14329
|
if (task.messagingSocketPath) {
|
|
14013
14330
|
entry = entries.find((e) => e.messagingSocketPath === task.messagingSocketPath);
|
|
@@ -14022,11 +14339,11 @@ function readClaudeStatus(task) {
|
|
|
14022
14339
|
function readClaudeStatusByCwd(cwd) {
|
|
14023
14340
|
let files;
|
|
14024
14341
|
try {
|
|
14025
|
-
files =
|
|
14342
|
+
files = fs20.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
14026
14343
|
} catch {
|
|
14027
14344
|
return void 0;
|
|
14028
14345
|
}
|
|
14029
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
14346
|
+
const entries = parseRegistryDir(files, (name) => fs20.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
14030
14347
|
const entry = entries.find((e) => e.cwd === cwd);
|
|
14031
14348
|
if (!entry)
|
|
14032
14349
|
return void 0;
|
|
@@ -14035,11 +14352,11 @@ function readClaudeStatusByCwd(cwd) {
|
|
|
14035
14352
|
function readClaudeStatusBySocketPath(socketPath2) {
|
|
14036
14353
|
let files;
|
|
14037
14354
|
try {
|
|
14038
|
-
files =
|
|
14355
|
+
files = fs20.readdirSync(CLAUDE_SESSIONS_DIR);
|
|
14039
14356
|
} catch {
|
|
14040
14357
|
return void 0;
|
|
14041
14358
|
}
|
|
14042
|
-
const entries = parseRegistryDir(files, (name) =>
|
|
14359
|
+
const entries = parseRegistryDir(files, (name) => fs20.readFileSync(join17(CLAUDE_SESSIONS_DIR, name), "utf8"));
|
|
14043
14360
|
const entry = entries.find((e) => e.messagingSocketPath === socketPath2);
|
|
14044
14361
|
if (!entry)
|
|
14045
14362
|
return void 0;
|
|
@@ -14462,8 +14779,8 @@ var init_registry2 = __esm({
|
|
|
14462
14779
|
});
|
|
14463
14780
|
|
|
14464
14781
|
// packages/agents/dist/drivers/launch-cmd.js
|
|
14465
|
-
import
|
|
14466
|
-
import
|
|
14782
|
+
import fs21 from "fs";
|
|
14783
|
+
import path19 from "path";
|
|
14467
14784
|
function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model, templatesDir, messagingSocketPath, sessionName, thinking, captainBoot, settingsPath) {
|
|
14468
14785
|
const driver = registry.getDriver(agentName);
|
|
14469
14786
|
if (driver.name === "claude") {
|
|
@@ -14490,14 +14807,14 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
|
|
|
14490
14807
|
cmd += ` --settings ${settingsPath}`;
|
|
14491
14808
|
}
|
|
14492
14809
|
if (templatesDir) {
|
|
14493
|
-
const roleFile2 =
|
|
14494
|
-
const legacyRoleFile =
|
|
14495
|
-
const actualRoleFile =
|
|
14810
|
+
const roleFile2 = path19.join(templatesDir, `${role}.claude.md`);
|
|
14811
|
+
const legacyRoleFile = path19.join(templatesDir, `${role}.CLAUDE.md`);
|
|
14812
|
+
const actualRoleFile = fs21.existsSync(roleFile2) ? roleFile2 : fs21.existsSync(legacyRoleFile) ? legacyRoleFile : null;
|
|
14496
14813
|
if (actualRoleFile) {
|
|
14497
14814
|
cmd += ` --append-system-prompt-file ${actualRoleFile}`;
|
|
14498
14815
|
}
|
|
14499
|
-
const pluginDir =
|
|
14500
|
-
if (
|
|
14816
|
+
const pluginDir = path19.join(templatesDir, "..", "plugin");
|
|
14817
|
+
if (fs21.existsSync(pluginDir)) {
|
|
14501
14818
|
cmd += ` --plugin-dir ${pluginDir}`;
|
|
14502
14819
|
}
|
|
14503
14820
|
}
|
|
@@ -14506,14 +14823,14 @@ function buildAgentCmd(agentName, registry, role, fresh, permissionMode, model,
|
|
|
14506
14823
|
}
|
|
14507
14824
|
return cmd;
|
|
14508
14825
|
}
|
|
14509
|
-
const roleFile = templatesDir ?
|
|
14826
|
+
const roleFile = templatesDir ? path19.join(templatesDir, `${role}.${driver.templateSuffix}.md`) : void 0;
|
|
14510
14827
|
return driver.buildCommand({
|
|
14511
14828
|
prompt: `You are a squadrant ${role}. Read your instructions from ${roleFile ?? role} and begin.`,
|
|
14512
14829
|
workdir: process.cwd(),
|
|
14513
14830
|
role,
|
|
14514
14831
|
model,
|
|
14515
14832
|
autoApprove: true,
|
|
14516
|
-
promptFile: roleFile &&
|
|
14833
|
+
promptFile: roleFile && fs21.existsSync(roleFile) ? roleFile : void 0,
|
|
14517
14834
|
...captainBoot && role === "captain" ? {
|
|
14518
14835
|
interactive: true,
|
|
14519
14836
|
...captainBoot.port ? { port: captainBoot.port } : {},
|
|
@@ -14543,7 +14860,7 @@ var init_drivers = __esm({
|
|
|
14543
14860
|
|
|
14544
14861
|
// packages/agents/dist/projection/cursor.js
|
|
14545
14862
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
14546
|
-
import
|
|
14863
|
+
import path20 from "path";
|
|
14547
14864
|
import os8 from "os";
|
|
14548
14865
|
function renderMdc(source) {
|
|
14549
14866
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
@@ -14592,7 +14909,7 @@ function createCursorEmitter() {
|
|
|
14592
14909
|
if (scope === "user") {
|
|
14593
14910
|
return [
|
|
14594
14911
|
{
|
|
14595
|
-
path:
|
|
14912
|
+
path: path20.join(os8.homedir(), ".cursor/rules/squadrant-global.mdc"),
|
|
14596
14913
|
shared: false,
|
|
14597
14914
|
format: "mdc"
|
|
14598
14915
|
}
|
|
@@ -14602,7 +14919,7 @@ function createCursorEmitter() {
|
|
|
14602
14919
|
return [];
|
|
14603
14920
|
return [
|
|
14604
14921
|
{
|
|
14605
|
-
path:
|
|
14922
|
+
path: path20.join(projectRoot, ".cursor/rules/squadrant.mdc"),
|
|
14606
14923
|
shared: false,
|
|
14607
14924
|
format: "mdc"
|
|
14608
14925
|
}
|
|
@@ -14619,7 +14936,7 @@ function createCursorEmitter() {
|
|
|
14619
14936
|
diff: buildDiff(existing, generated)
|
|
14620
14937
|
};
|
|
14621
14938
|
}
|
|
14622
|
-
await mkdir(
|
|
14939
|
+
await mkdir(path20.dirname(dest.path), { recursive: true });
|
|
14623
14940
|
await writeFile(dest.path, generated, "utf-8");
|
|
14624
14941
|
return {
|
|
14625
14942
|
written: true,
|
|
@@ -14670,7 +14987,7 @@ var init_marker = __esm({
|
|
|
14670
14987
|
|
|
14671
14988
|
// packages/agents/dist/projection/codex.js
|
|
14672
14989
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
14673
|
-
import
|
|
14990
|
+
import path21 from "path";
|
|
14674
14991
|
import os9 from "os";
|
|
14675
14992
|
function renderMarkdown(source) {
|
|
14676
14993
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
@@ -14695,7 +15012,7 @@ function createCodexEmitter() {
|
|
|
14695
15012
|
destinations(scope, projectRoot) {
|
|
14696
15013
|
if (scope === "user") {
|
|
14697
15014
|
return [{
|
|
14698
|
-
path:
|
|
15015
|
+
path: path21.join(os9.homedir(), ".codex/AGENTS.md"),
|
|
14699
15016
|
shared: true,
|
|
14700
15017
|
format: "markdown"
|
|
14701
15018
|
}];
|
|
@@ -14703,7 +15020,7 @@ function createCodexEmitter() {
|
|
|
14703
15020
|
if (!projectRoot)
|
|
14704
15021
|
return [];
|
|
14705
15022
|
return [{
|
|
14706
|
-
path:
|
|
15023
|
+
path: path21.join(projectRoot, "AGENTS.md"),
|
|
14707
15024
|
shared: true,
|
|
14708
15025
|
format: "markdown"
|
|
14709
15026
|
}];
|
|
@@ -14724,7 +15041,7 @@ ${existing ?? ""}
|
|
|
14724
15041
|
${generated}`
|
|
14725
15042
|
};
|
|
14726
15043
|
}
|
|
14727
|
-
await mkdir2(
|
|
15044
|
+
await mkdir2(path21.dirname(dest.path), { recursive: true });
|
|
14728
15045
|
await writeFile2(dest.path, generated, "utf-8");
|
|
14729
15046
|
return {
|
|
14730
15047
|
written: true,
|
|
@@ -14742,7 +15059,7 @@ var init_codex2 = __esm({
|
|
|
14742
15059
|
|
|
14743
15060
|
// packages/agents/dist/projection/gemini.js
|
|
14744
15061
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
14745
|
-
import
|
|
15062
|
+
import path22 from "path";
|
|
14746
15063
|
import os10 from "os";
|
|
14747
15064
|
function renderMarkdown2(source) {
|
|
14748
15065
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
@@ -14767,7 +15084,7 @@ function createGeminiEmitter() {
|
|
|
14767
15084
|
destinations(scope, projectRoot) {
|
|
14768
15085
|
if (scope === "user") {
|
|
14769
15086
|
return [{
|
|
14770
|
-
path:
|
|
15087
|
+
path: path22.join(os10.homedir(), ".gemini/GEMINI.md"),
|
|
14771
15088
|
shared: true,
|
|
14772
15089
|
format: "markdown"
|
|
14773
15090
|
}];
|
|
@@ -14775,7 +15092,7 @@ function createGeminiEmitter() {
|
|
|
14775
15092
|
if (!projectRoot)
|
|
14776
15093
|
return [];
|
|
14777
15094
|
return [{
|
|
14778
|
-
path:
|
|
15095
|
+
path: path22.join(projectRoot, "GEMINI.md"),
|
|
14779
15096
|
shared: true,
|
|
14780
15097
|
format: "markdown"
|
|
14781
15098
|
}];
|
|
@@ -14796,7 +15113,7 @@ ${existing ?? ""}
|
|
|
14796
15113
|
${generated}`
|
|
14797
15114
|
};
|
|
14798
15115
|
}
|
|
14799
|
-
await mkdir3(
|
|
15116
|
+
await mkdir3(path22.dirname(dest.path), { recursive: true });
|
|
14800
15117
|
await writeFile3(dest.path, generated, "utf-8");
|
|
14801
15118
|
return {
|
|
14802
15119
|
written: true,
|
|
@@ -14814,7 +15131,7 @@ var init_gemini2 = __esm({
|
|
|
14814
15131
|
|
|
14815
15132
|
// packages/agents/dist/projection/opencode.js
|
|
14816
15133
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
14817
|
-
import
|
|
15134
|
+
import path23 from "path";
|
|
14818
15135
|
import os11 from "os";
|
|
14819
15136
|
function renderMarkdown3(source) {
|
|
14820
15137
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
@@ -14839,7 +15156,7 @@ function createOpencodeEmitter() {
|
|
|
14839
15156
|
destinations(scope, projectRoot) {
|
|
14840
15157
|
if (scope === "user") {
|
|
14841
15158
|
return [{
|
|
14842
|
-
path:
|
|
15159
|
+
path: path23.join(os11.homedir(), ".config", "opencode", "AGENTS.md"),
|
|
14843
15160
|
shared: true,
|
|
14844
15161
|
format: "markdown"
|
|
14845
15162
|
}];
|
|
@@ -14847,7 +15164,7 @@ function createOpencodeEmitter() {
|
|
|
14847
15164
|
if (!projectRoot)
|
|
14848
15165
|
return [];
|
|
14849
15166
|
return [{
|
|
14850
|
-
path:
|
|
15167
|
+
path: path23.join(projectRoot, "AGENTS.md"),
|
|
14851
15168
|
shared: true,
|
|
14852
15169
|
format: "markdown"
|
|
14853
15170
|
}];
|
|
@@ -14868,7 +15185,7 @@ ${existing ?? ""}
|
|
|
14868
15185
|
${generated}`
|
|
14869
15186
|
};
|
|
14870
15187
|
}
|
|
14871
|
-
await mkdir4(
|
|
15188
|
+
await mkdir4(path23.dirname(dest.path), { recursive: true });
|
|
14872
15189
|
await writeFile4(dest.path, generated, "utf-8");
|
|
14873
15190
|
return {
|
|
14874
15191
|
written: true,
|
|
@@ -14885,12 +15202,12 @@ var init_opencode2 = __esm({
|
|
|
14885
15202
|
});
|
|
14886
15203
|
|
|
14887
15204
|
// packages/agents/dist/projection/opencode-skills.js
|
|
14888
|
-
import
|
|
15205
|
+
import fs22 from "fs";
|
|
14889
15206
|
import os12 from "os";
|
|
14890
|
-
import
|
|
15207
|
+
import path24 from "path";
|
|
14891
15208
|
function defaultOpencodeSkillsRoot(env = process.env) {
|
|
14892
|
-
const configHome = env.XDG_CONFIG_HOME ||
|
|
14893
|
-
return
|
|
15209
|
+
const configHome = env.XDG_CONFIG_HOME || path24.join(os12.homedir(), ".config");
|
|
15210
|
+
return path24.join(configHome, "opencode", "skills");
|
|
14894
15211
|
}
|
|
14895
15212
|
function parseSkillName(raw) {
|
|
14896
15213
|
const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -14904,62 +15221,62 @@ function parseSkillName(raw) {
|
|
|
14904
15221
|
}
|
|
14905
15222
|
function listSourceSkills(sourceSkillsDir) {
|
|
14906
15223
|
const out = /* @__PURE__ */ new Map();
|
|
14907
|
-
if (!
|
|
15224
|
+
if (!fs22.existsSync(sourceSkillsDir))
|
|
14908
15225
|
return out;
|
|
14909
|
-
for (const entry of
|
|
15226
|
+
for (const entry of fs22.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
|
|
14910
15227
|
if (!entry.isDirectory())
|
|
14911
15228
|
continue;
|
|
14912
|
-
const skillPath =
|
|
14913
|
-
if (!
|
|
15229
|
+
const skillPath = path24.join(sourceSkillsDir, entry.name, "SKILL.md");
|
|
15230
|
+
if (!fs22.existsSync(skillPath))
|
|
14914
15231
|
continue;
|
|
14915
|
-
const name = parseSkillName(
|
|
15232
|
+
const name = parseSkillName(fs22.readFileSync(skillPath, "utf-8"));
|
|
14916
15233
|
if (name && SAFE_NAME.test(name))
|
|
14917
15234
|
out.set(name, skillPath);
|
|
14918
15235
|
}
|
|
14919
15236
|
return out;
|
|
14920
15237
|
}
|
|
14921
15238
|
function hasMarker(dir) {
|
|
14922
|
-
return
|
|
15239
|
+
return fs22.existsSync(path24.join(dir, OPENCODE_SKILL_MARKER));
|
|
14923
15240
|
}
|
|
14924
15241
|
function syncOpencodeSkills(opts) {
|
|
14925
15242
|
const result = { written: [], unchanged: [], removed: [], skipped: [] };
|
|
14926
15243
|
const wanted = listSourceSkills(opts.sourceSkillsDir);
|
|
14927
|
-
if (wanted.size === 0 && !
|
|
15244
|
+
if (wanted.size === 0 && !fs22.existsSync(opts.sourceSkillsDir))
|
|
14928
15245
|
return result;
|
|
14929
15246
|
if (!opts.dryRun)
|
|
14930
|
-
|
|
15247
|
+
fs22.mkdirSync(opts.skillsRoot, { recursive: true });
|
|
14931
15248
|
for (const [name, srcPath] of wanted) {
|
|
14932
|
-
const targetDir =
|
|
14933
|
-
const targetSkill =
|
|
15249
|
+
const targetDir = path24.join(opts.skillsRoot, name);
|
|
15250
|
+
const targetSkill = path24.join(targetDir, "SKILL.md");
|
|
14934
15251
|
const managed = hasMarker(targetDir);
|
|
14935
|
-
if (
|
|
15252
|
+
if (fs22.existsSync(targetSkill) && !managed) {
|
|
14936
15253
|
result.skipped.push({ name, reason: "foreign-collision" });
|
|
14937
15254
|
continue;
|
|
14938
15255
|
}
|
|
14939
|
-
const current =
|
|
14940
|
-
if (current && current.equals(
|
|
15256
|
+
const current = fs22.existsSync(targetSkill) ? fs22.readFileSync(targetSkill) : null;
|
|
15257
|
+
if (current && current.equals(fs22.readFileSync(srcPath))) {
|
|
14941
15258
|
if (!managed && !opts.dryRun) {
|
|
14942
|
-
|
|
15259
|
+
fs22.writeFileSync(path24.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
14943
15260
|
}
|
|
14944
15261
|
result.unchanged.push(name);
|
|
14945
15262
|
continue;
|
|
14946
15263
|
}
|
|
14947
15264
|
if (!opts.dryRun) {
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
15265
|
+
fs22.mkdirSync(targetDir, { recursive: true });
|
|
15266
|
+
fs22.copyFileSync(srcPath, targetSkill);
|
|
15267
|
+
fs22.writeFileSync(path24.join(targetDir, OPENCODE_SKILL_MARKER), MARKER_BODY);
|
|
14951
15268
|
}
|
|
14952
15269
|
result.written.push(name);
|
|
14953
15270
|
}
|
|
14954
|
-
if (
|
|
14955
|
-
for (const entry of
|
|
15271
|
+
if (fs22.existsSync(opts.skillsRoot)) {
|
|
15272
|
+
for (const entry of fs22.readdirSync(opts.skillsRoot, { withFileTypes: true })) {
|
|
14956
15273
|
if (!entry.isDirectory() || wanted.has(entry.name))
|
|
14957
15274
|
continue;
|
|
14958
|
-
const dir =
|
|
15275
|
+
const dir = path24.join(opts.skillsRoot, entry.name);
|
|
14959
15276
|
if (!hasMarker(dir))
|
|
14960
15277
|
continue;
|
|
14961
15278
|
if (!opts.dryRun)
|
|
14962
|
-
|
|
15279
|
+
fs22.rmSync(dir, { recursive: true, force: true });
|
|
14963
15280
|
result.removed.push(entry.name);
|
|
14964
15281
|
}
|
|
14965
15282
|
}
|
|
@@ -14967,7 +15284,7 @@ function syncOpencodeSkills(opts) {
|
|
|
14967
15284
|
}
|
|
14968
15285
|
function syncShippedOpencodeSkills(opts) {
|
|
14969
15286
|
return syncOpencodeSkills({
|
|
14970
|
-
sourceSkillsDir:
|
|
15287
|
+
sourceSkillsDir: path24.join(opts.pkgRoot, "plugin", "skills"),
|
|
14971
15288
|
skillsRoot: opts.skillsRoot ?? defaultOpencodeSkillsRoot(),
|
|
14972
15289
|
dryRun: opts.dryRun
|
|
14973
15290
|
});
|
|
@@ -14983,7 +15300,7 @@ var init_opencode_skills = __esm({
|
|
|
14983
15300
|
|
|
14984
15301
|
// packages/agents/dist/projection/claude.js
|
|
14985
15302
|
import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
14986
|
-
import
|
|
15303
|
+
import path25 from "path";
|
|
14987
15304
|
import os13 from "os";
|
|
14988
15305
|
function renderMarkdown4(source) {
|
|
14989
15306
|
const skillSections = source.skills.map((s) => `## Skill: ${s.name}
|
|
@@ -15008,7 +15325,7 @@ function createClaudeEmitter() {
|
|
|
15008
15325
|
destinations(scope, projectRoot) {
|
|
15009
15326
|
if (scope === "user") {
|
|
15010
15327
|
return [{
|
|
15011
|
-
path:
|
|
15328
|
+
path: path25.join(os13.homedir(), ".claude/CLAUDE.md"),
|
|
15012
15329
|
shared: true,
|
|
15013
15330
|
format: "markdown"
|
|
15014
15331
|
}];
|
|
@@ -15016,7 +15333,7 @@ function createClaudeEmitter() {
|
|
|
15016
15333
|
if (!projectRoot)
|
|
15017
15334
|
return [];
|
|
15018
15335
|
return [{
|
|
15019
|
-
path:
|
|
15336
|
+
path: path25.join(projectRoot, "CLAUDE.md"),
|
|
15020
15337
|
shared: true,
|
|
15021
15338
|
format: "markdown"
|
|
15022
15339
|
}];
|
|
@@ -15037,7 +15354,7 @@ ${existing ?? ""}
|
|
|
15037
15354
|
${generated}`
|
|
15038
15355
|
};
|
|
15039
15356
|
}
|
|
15040
|
-
await mkdir5(
|
|
15357
|
+
await mkdir5(path25.dirname(dest.path), { recursive: true });
|
|
15041
15358
|
await writeFile5(dest.path, generated, "utf-8");
|
|
15042
15359
|
return {
|
|
15043
15360
|
written: true,
|
|
@@ -15099,12 +15416,12 @@ function _parseChunk(acc, chunk) {
|
|
|
15099
15416
|
const out = [];
|
|
15100
15417
|
let idx;
|
|
15101
15418
|
while ((idx = acc.buf.indexOf("\n")) >= 0) {
|
|
15102
|
-
const
|
|
15419
|
+
const line2 = acc.buf.slice(0, idx);
|
|
15103
15420
|
acc.buf = acc.buf.slice(idx + 1);
|
|
15104
|
-
if (!
|
|
15421
|
+
if (!line2.trim())
|
|
15105
15422
|
continue;
|
|
15106
15423
|
try {
|
|
15107
|
-
out.push(JSON.parse(
|
|
15424
|
+
out.push(JSON.parse(line2));
|
|
15108
15425
|
} catch {
|
|
15109
15426
|
}
|
|
15110
15427
|
}
|
|
@@ -15865,9 +16182,9 @@ var init_sse_bridge = __esm({
|
|
|
15865
16182
|
buf += decoder.decode(value, { stream: true });
|
|
15866
16183
|
let nl;
|
|
15867
16184
|
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
15868
|
-
const
|
|
16185
|
+
const line2 = buf.slice(0, nl);
|
|
15869
16186
|
buf = buf.slice(nl + 1);
|
|
15870
|
-
this.handleLine(taskId,
|
|
16187
|
+
this.handleLine(taskId, line2);
|
|
15871
16188
|
}
|
|
15872
16189
|
}
|
|
15873
16190
|
} finally {
|
|
@@ -15878,16 +16195,16 @@ var init_sse_bridge = __esm({
|
|
|
15878
16195
|
}
|
|
15879
16196
|
}
|
|
15880
16197
|
handleLine(taskId, rawLine) {
|
|
15881
|
-
let
|
|
15882
|
-
if (!
|
|
16198
|
+
let line2 = rawLine.trim();
|
|
16199
|
+
if (!line2)
|
|
15883
16200
|
return;
|
|
15884
|
-
if (
|
|
15885
|
-
|
|
15886
|
-
if (!
|
|
16201
|
+
if (line2.startsWith("data:"))
|
|
16202
|
+
line2 = line2.slice(5).trim();
|
|
16203
|
+
if (!line2.startsWith("{"))
|
|
15887
16204
|
return;
|
|
15888
16205
|
let json2;
|
|
15889
16206
|
try {
|
|
15890
|
-
json2 = JSON.parse(
|
|
16207
|
+
json2 = JSON.parse(line2);
|
|
15891
16208
|
} catch {
|
|
15892
16209
|
return;
|
|
15893
16210
|
}
|
|
@@ -16009,14 +16326,14 @@ function detectTrailingQuestion2(text) {
|
|
|
16009
16326
|
let inFence = false;
|
|
16010
16327
|
let lastLine = null;
|
|
16011
16328
|
for (const raw of text.split(/\r?\n/)) {
|
|
16012
|
-
const
|
|
16013
|
-
if (
|
|
16329
|
+
const line2 = raw.trim();
|
|
16330
|
+
if (line2.startsWith("```")) {
|
|
16014
16331
|
inFence = !inFence;
|
|
16015
16332
|
continue;
|
|
16016
16333
|
}
|
|
16017
|
-
if (inFence ||
|
|
16334
|
+
if (inFence || line2 === "")
|
|
16018
16335
|
continue;
|
|
16019
|
-
lastLine =
|
|
16336
|
+
lastLine = line2;
|
|
16020
16337
|
}
|
|
16021
16338
|
if (lastLine && lastLine.endsWith("?"))
|
|
16022
16339
|
return lastLine;
|
|
@@ -16033,12 +16350,12 @@ function readLastAssistantText(transcriptPath) {
|
|
|
16033
16350
|
const raw = readFileSync14(transcriptPath, "utf-8");
|
|
16034
16351
|
const lines = raw.split(/\r?\n/);
|
|
16035
16352
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
16036
|
-
const
|
|
16037
|
-
if (!
|
|
16353
|
+
const line2 = lines[i].trim();
|
|
16354
|
+
if (!line2)
|
|
16038
16355
|
continue;
|
|
16039
16356
|
let entry;
|
|
16040
16357
|
try {
|
|
16041
|
-
entry = JSON.parse(
|
|
16358
|
+
entry = JSON.parse(line2);
|
|
16042
16359
|
} catch {
|
|
16043
16360
|
continue;
|
|
16044
16361
|
}
|
|
@@ -16072,8 +16389,8 @@ function resolveLastAssistantText(payload) {
|
|
|
16072
16389
|
const derived = deriveTranscriptPath(p?.session_id, cwd);
|
|
16073
16390
|
if (derived)
|
|
16074
16391
|
candidates.push(derived);
|
|
16075
|
-
for (const
|
|
16076
|
-
const text = readLastAssistantText(
|
|
16392
|
+
for (const path46 of candidates) {
|
|
16393
|
+
const text = readLastAssistantText(path46);
|
|
16077
16394
|
if (text != null)
|
|
16078
16395
|
return text;
|
|
16079
16396
|
}
|
|
@@ -16341,9 +16658,9 @@ function isQuotedLine2(raw) {
|
|
|
16341
16658
|
return QUOTED_PREFIX_RE2.test(noAnsi);
|
|
16342
16659
|
}
|
|
16343
16660
|
function stripChrome2(raw) {
|
|
16344
|
-
let
|
|
16345
|
-
|
|
16346
|
-
const trimmed =
|
|
16661
|
+
let line2 = raw.replace(/\[[0-9;]*m/g, "");
|
|
16662
|
+
line2 = line2.replace(/^[\s│┃▏▕|]+/, "").replace(/[\s│┃▏▕|]+$/, "");
|
|
16663
|
+
const trimmed = line2.trim();
|
|
16347
16664
|
if (trimmed === "")
|
|
16348
16665
|
return null;
|
|
16349
16666
|
if (PURE_CHROME_RE2.test(trimmed))
|
|
@@ -16717,13 +17034,13 @@ function buildUserEnvelope(opts) {
|
|
|
16717
17034
|
};
|
|
16718
17035
|
}
|
|
16719
17036
|
function writeLine(socketPath2, envelope, deps) {
|
|
16720
|
-
const
|
|
17037
|
+
const line2 = `${JSON.stringify(envelope)}
|
|
16721
17038
|
`;
|
|
16722
|
-
if (Buffer.byteLength(
|
|
17039
|
+
if (Buffer.byteLength(line2, "utf8") > MAX_LINE_BYTES) {
|
|
16723
17040
|
return Promise.resolve({
|
|
16724
17041
|
ok: false,
|
|
16725
17042
|
reason: "transport",
|
|
16726
|
-
error: `line exceeds the receiver's 1 MiB buffer cap (${Buffer.byteLength(
|
|
17043
|
+
error: `line exceeds the receiver's 1 MiB buffer cap (${Buffer.byteLength(line2, "utf8")} bytes)`
|
|
16727
17044
|
});
|
|
16728
17045
|
}
|
|
16729
17046
|
return new Promise((resolve4) => {
|
|
@@ -16743,7 +17060,7 @@ function writeLine(socketPath2, envelope, deps) {
|
|
|
16743
17060
|
done({ ok: false, reason: "transport", error: `timeout after ${deps.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms` });
|
|
16744
17061
|
}, deps.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
16745
17062
|
sock.on("connect", () => {
|
|
16746
|
-
sock.write(
|
|
17063
|
+
sock.write(line2, () => {
|
|
16747
17064
|
clearTimeout(timer);
|
|
16748
17065
|
try {
|
|
16749
17066
|
sock.end();
|
|
@@ -16920,20 +17237,20 @@ var init_receipt_listener = __esm({
|
|
|
16920
17237
|
buf += chunk;
|
|
16921
17238
|
let nl;
|
|
16922
17239
|
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
16923
|
-
const
|
|
17240
|
+
const line2 = buf.slice(0, nl);
|
|
16924
17241
|
buf = buf.slice(nl + 1);
|
|
16925
|
-
this.dispatch(
|
|
17242
|
+
this.dispatch(line2);
|
|
16926
17243
|
}
|
|
16927
17244
|
});
|
|
16928
17245
|
conn.on("error", () => {
|
|
16929
17246
|
});
|
|
16930
17247
|
}
|
|
16931
|
-
dispatch(
|
|
16932
|
-
if (!
|
|
17248
|
+
dispatch(line2) {
|
|
17249
|
+
if (!line2.trim())
|
|
16933
17250
|
return;
|
|
16934
17251
|
let f;
|
|
16935
17252
|
try {
|
|
16936
|
-
f = JSON.parse(
|
|
17253
|
+
f = JSON.parse(line2);
|
|
16937
17254
|
} catch {
|
|
16938
17255
|
this.deps.log?.(`claude-receipt: discarded non-JSON frame`);
|
|
16939
17256
|
return;
|
|
@@ -16962,22 +17279,22 @@ var init_receipt_listener = __esm({
|
|
|
16962
17279
|
});
|
|
16963
17280
|
|
|
16964
17281
|
// packages/agents/dist/claude/api-key-approval.js
|
|
16965
|
-
import
|
|
17282
|
+
import fs23 from "fs";
|
|
16966
17283
|
import os14 from "os";
|
|
16967
|
-
import
|
|
17284
|
+
import path26 from "path";
|
|
16968
17285
|
function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
|
|
16969
17286
|
const suffix = apiKey.slice(-20);
|
|
16970
17287
|
if (!suffix)
|
|
16971
17288
|
return { changed: false, reason: "empty api key" };
|
|
16972
|
-
const file = opts.claudeJsonPath ??
|
|
17289
|
+
const file = opts.claudeJsonPath ?? path26.join(os14.homedir(), ".claude.json");
|
|
16973
17290
|
const log = opts.log ?? (() => {
|
|
16974
17291
|
});
|
|
16975
|
-
if (!
|
|
17292
|
+
if (!fs23.existsSync(file)) {
|
|
16976
17293
|
return { changed: false, reason: "no ~/.claude.json yet \u2014 skipping routed-key pre-approval" };
|
|
16977
17294
|
}
|
|
16978
17295
|
let doc;
|
|
16979
17296
|
try {
|
|
16980
|
-
doc = JSON.parse(
|
|
17297
|
+
doc = JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
16981
17298
|
} catch {
|
|
16982
17299
|
return {
|
|
16983
17300
|
changed: false,
|
|
@@ -16999,11 +17316,11 @@ function ensureClaudeApiKeyApproved(apiKey, opts = {}) {
|
|
|
16999
17316
|
};
|
|
17000
17317
|
const tmp = `${file}.squadrant-tmp`;
|
|
17001
17318
|
try {
|
|
17002
|
-
|
|
17003
|
-
|
|
17319
|
+
fs23.writeFileSync(tmp, JSON.stringify(doc, null, 2) + "\n", { mode: fs23.statSync(file).mode & 511 });
|
|
17320
|
+
fs23.renameSync(tmp, file);
|
|
17004
17321
|
} catch (e) {
|
|
17005
17322
|
try {
|
|
17006
|
-
|
|
17323
|
+
fs23.unlinkSync(tmp);
|
|
17007
17324
|
} catch {
|
|
17008
17325
|
}
|
|
17009
17326
|
return {
|
|
@@ -17112,10 +17429,10 @@ var init_http_channel = __esm({
|
|
|
17112
17429
|
return cached;
|
|
17113
17430
|
const directory = this.directoryFor?.(taskId);
|
|
17114
17431
|
const createdAfterMs = this.createdAfterFor?.(taskId);
|
|
17115
|
-
for (const
|
|
17432
|
+
for (const path46 of ["/session?", "/api/session?"]) {
|
|
17116
17433
|
let res;
|
|
17117
17434
|
try {
|
|
17118
|
-
res = await this.request(`http://127.0.0.1:${port}${
|
|
17435
|
+
res = await this.request(`http://127.0.0.1:${port}${path46}`, { method: "GET" });
|
|
17119
17436
|
} catch {
|
|
17120
17437
|
return void 0;
|
|
17121
17438
|
}
|
|
@@ -17349,6 +17666,15 @@ async function cmuxHasInputGuards() {
|
|
|
17349
17666
|
inputGuardsCache = major > 0 || minor >= 65;
|
|
17350
17667
|
return inputGuardsCache;
|
|
17351
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
|
+
}
|
|
17352
17678
|
function parseList(output) {
|
|
17353
17679
|
let parsed;
|
|
17354
17680
|
try {
|
|
@@ -17391,13 +17717,13 @@ function parseDraftFromScreen(screen) {
|
|
|
17391
17717
|
if (topHR === -1)
|
|
17392
17718
|
return null;
|
|
17393
17719
|
const inputLines = lines.slice(topHR + 1, bottomHR);
|
|
17394
|
-
for (const
|
|
17720
|
+
for (const line2 of inputLines) {
|
|
17395
17721
|
let extracted;
|
|
17396
|
-
const boxMatch =
|
|
17722
|
+
const boxMatch = line2.match(/│\s*[>❯]\s+(.*?)\s*│/);
|
|
17397
17723
|
if (boxMatch) {
|
|
17398
17724
|
extracted = boxMatch[1].trim();
|
|
17399
17725
|
} else {
|
|
17400
|
-
const plainMatch =
|
|
17726
|
+
const plainMatch = line2.match(/^\s*[>❯]\s*(.*)$/);
|
|
17401
17727
|
if (plainMatch)
|
|
17402
17728
|
extracted = plainMatch[1].trim();
|
|
17403
17729
|
}
|
|
@@ -17476,8 +17802,8 @@ function parseModalOptions(screen) {
|
|
|
17476
17802
|
return null;
|
|
17477
17803
|
const OPTION_RE3 = /^\s*(❯)?\s*(\d+)\.\s*(.*?)\s*$/;
|
|
17478
17804
|
const options = [];
|
|
17479
|
-
for (const
|
|
17480
|
-
const m =
|
|
17805
|
+
for (const line2 of lines.slice(topHR + 1, bottomHR)) {
|
|
17806
|
+
const m = line2.match(OPTION_RE3);
|
|
17481
17807
|
if (!m)
|
|
17482
17808
|
continue;
|
|
17483
17809
|
options.push({ index: Number(m[2]), label: m[3], highlighted: m[1] === "\u276F" });
|
|
@@ -17535,8 +17861,8 @@ function readInputBoxRaw(screen, opts) {
|
|
|
17535
17861
|
if (topHR === -1)
|
|
17536
17862
|
return null;
|
|
17537
17863
|
const parts = [];
|
|
17538
|
-
for (const
|
|
17539
|
-
let s =
|
|
17864
|
+
for (const line2 of lines.slice(topHR + 1, bottomHR)) {
|
|
17865
|
+
let s = line2.replace(/│/g, " ");
|
|
17540
17866
|
s = s.replace(/^\s*[>❯]\s?/, "");
|
|
17541
17867
|
s = s.replace(/\s*[▌█▔▎▏]+\s*$/, "");
|
|
17542
17868
|
parts.push(s);
|
|
@@ -17772,6 +18098,26 @@ function createCmuxDriver() {
|
|
|
17772
18098
|
await cmux(["send-key", "--workspace", wsId, "--surface", surfaceId, "Enter"]);
|
|
17773
18099
|
return { workspaceId: wsId, surfaceId, title: opts.title };
|
|
17774
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
|
+
},
|
|
17775
18121
|
async sendToSurface(surface, text, opts) {
|
|
17776
18122
|
const ws = surface.workspaceId;
|
|
17777
18123
|
const sf = surface.surfaceId;
|
|
@@ -17798,6 +18144,17 @@ function createCmuxDriver() {
|
|
|
17798
18144
|
`);
|
|
17799
18145
|
}
|
|
17800
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
|
+
}
|
|
17801
18158
|
let screen = "";
|
|
17802
18159
|
try {
|
|
17803
18160
|
screen = await cmux(["read-screen", "--workspace", ws, "--surface", sf]);
|
|
@@ -17876,6 +18233,39 @@ function createCmuxDriver() {
|
|
|
17876
18233
|
args.push("--focus", "true");
|
|
17877
18234
|
await cmux(args);
|
|
17878
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
|
+
},
|
|
17879
18269
|
async showPatch(opts) {
|
|
17880
18270
|
const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
|
|
17881
18271
|
if (opts.title)
|
|
@@ -18064,13 +18454,13 @@ var init_notifiers = __esm({
|
|
|
18064
18454
|
});
|
|
18065
18455
|
|
|
18066
18456
|
// packages/workspaces/dist/workspaces/obsidian.js
|
|
18067
|
-
import
|
|
18457
|
+
import fs24 from "fs/promises";
|
|
18068
18458
|
import { existsSync as existsSync11 } from "fs";
|
|
18069
|
-
import
|
|
18459
|
+
import path27 from "path";
|
|
18070
18460
|
function resolveInRoot(root, relative) {
|
|
18071
|
-
const joined =
|
|
18072
|
-
const normalized2 =
|
|
18073
|
-
if (joined !==
|
|
18461
|
+
const joined = path27.resolve(root, relative);
|
|
18462
|
+
const normalized2 = path27.resolve(root) + path27.sep;
|
|
18463
|
+
if (joined !== path27.resolve(root) && !joined.startsWith(normalized2)) {
|
|
18074
18464
|
throw new Error(`Path '${relative}' escapes workspace root`);
|
|
18075
18465
|
}
|
|
18076
18466
|
return joined;
|
|
@@ -18089,16 +18479,16 @@ function createObsidianDriver(scope) {
|
|
|
18089
18479
|
};
|
|
18090
18480
|
},
|
|
18091
18481
|
async read(rel) {
|
|
18092
|
-
return
|
|
18482
|
+
return fs24.readFile(resolveInRoot(root, rel), "utf-8");
|
|
18093
18483
|
},
|
|
18094
18484
|
async write(rel, content) {
|
|
18095
18485
|
const abs = resolveInRoot(root, rel);
|
|
18096
|
-
await
|
|
18097
|
-
await
|
|
18486
|
+
await fs24.mkdir(path27.dirname(abs), { recursive: true });
|
|
18487
|
+
await fs24.writeFile(abs, content);
|
|
18098
18488
|
},
|
|
18099
18489
|
async exists(rel) {
|
|
18100
18490
|
try {
|
|
18101
|
-
await
|
|
18491
|
+
await fs24.access(resolveInRoot(root, rel));
|
|
18102
18492
|
return true;
|
|
18103
18493
|
} catch {
|
|
18104
18494
|
return false;
|
|
@@ -18106,13 +18496,13 @@ function createObsidianDriver(scope) {
|
|
|
18106
18496
|
},
|
|
18107
18497
|
async list(rel) {
|
|
18108
18498
|
try {
|
|
18109
|
-
return await
|
|
18499
|
+
return await fs24.readdir(resolveInRoot(root, rel));
|
|
18110
18500
|
} catch {
|
|
18111
18501
|
return [];
|
|
18112
18502
|
}
|
|
18113
18503
|
},
|
|
18114
18504
|
async mkdir(rel) {
|
|
18115
|
-
await
|
|
18505
|
+
await fs24.mkdir(resolveInRoot(root, rel), { recursive: true });
|
|
18116
18506
|
}
|
|
18117
18507
|
};
|
|
18118
18508
|
}
|
|
@@ -18266,18 +18656,18 @@ var init_events_bridge = __esm({
|
|
|
18266
18656
|
this.buf += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
18267
18657
|
let nl;
|
|
18268
18658
|
while ((nl = this.buf.indexOf("\n")) >= 0) {
|
|
18269
|
-
const
|
|
18659
|
+
const line2 = this.buf.slice(0, nl);
|
|
18270
18660
|
this.buf = this.buf.slice(nl + 1);
|
|
18271
|
-
this.handleLine(
|
|
18661
|
+
this.handleLine(line2);
|
|
18272
18662
|
}
|
|
18273
18663
|
}
|
|
18274
18664
|
handleLine(rawLine) {
|
|
18275
|
-
const
|
|
18276
|
-
if (!
|
|
18665
|
+
const line2 = rawLine.trim();
|
|
18666
|
+
if (!line2 || line2[0] !== "{")
|
|
18277
18667
|
return;
|
|
18278
18668
|
let f;
|
|
18279
18669
|
try {
|
|
18280
|
-
f = JSON.parse(
|
|
18670
|
+
f = JSON.parse(line2);
|
|
18281
18671
|
} catch {
|
|
18282
18672
|
return;
|
|
18283
18673
|
}
|
|
@@ -18526,9 +18916,9 @@ function defaultListFiles(dir) {
|
|
|
18526
18916
|
return [];
|
|
18527
18917
|
}
|
|
18528
18918
|
}
|
|
18529
|
-
function defaultReadFile(
|
|
18919
|
+
function defaultReadFile(path46) {
|
|
18530
18920
|
try {
|
|
18531
|
-
return readFileSync17(
|
|
18921
|
+
return readFileSync17(path46, "utf-8");
|
|
18532
18922
|
} catch {
|
|
18533
18923
|
return void 0;
|
|
18534
18924
|
}
|
|
@@ -19021,16 +19411,16 @@ function extractDetail(sub, payload) {
|
|
|
19021
19411
|
}
|
|
19022
19412
|
return void 0;
|
|
19023
19413
|
}
|
|
19024
|
-
function defaultReadFile2(
|
|
19414
|
+
function defaultReadFile2(path46) {
|
|
19025
19415
|
try {
|
|
19026
|
-
return readFileSync18(
|
|
19416
|
+
return readFileSync18(path46, "utf-8");
|
|
19027
19417
|
} catch {
|
|
19028
19418
|
return void 0;
|
|
19029
19419
|
}
|
|
19030
19420
|
}
|
|
19031
|
-
function defaultWriteFile(
|
|
19032
|
-
mkdirSync8(
|
|
19033
|
-
writeFileSync11(
|
|
19421
|
+
function defaultWriteFile(path46, content) {
|
|
19422
|
+
mkdirSync8(path46.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
19423
|
+
writeFileSync11(path46, content, "utf-8");
|
|
19034
19424
|
}
|
|
19035
19425
|
var CLAUDE_HOOK_EVENTS, DEFAULT_CLI_CMD, FOREIGN_AUTO_GATE_MARKER, SUBAGENT_MODEL_KEY, SMALL_FAST_MODEL_KEY, NativeHookSource;
|
|
19036
19426
|
var init_native_hook_source = __esm({
|
|
@@ -19190,6 +19580,11 @@ async function readModalOptions(runtime, pane) {
|
|
|
19190
19580
|
return parseModal(screen);
|
|
19191
19581
|
}
|
|
19192
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 };
|
|
19193
19588
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
19194
19589
|
if (hasModalOptionList2(preSendScreen)) {
|
|
19195
19590
|
return { delivered: false, blockedByModal: true };
|
|
@@ -19459,9 +19854,9 @@ async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
|
|
|
19459
19854
|
const resolved = resolveTarget(registry, config, target, !!opts.command);
|
|
19460
19855
|
await needRef(resolved);
|
|
19461
19856
|
const finalProject = opts.command ? config.commandName : target;
|
|
19462
|
-
const { join:
|
|
19857
|
+
const { join: join38, dirname: dirname13 } = await import("path");
|
|
19463
19858
|
const { DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
19464
|
-
const stateRoot =
|
|
19859
|
+
const stateRoot = join38(dirname13(DEFAULT_CONFIG_PATH2), "state");
|
|
19465
19860
|
const seq2 = await appendCaptainMessage2({
|
|
19466
19861
|
stateRoot,
|
|
19467
19862
|
project: finalProject,
|
|
@@ -19584,7 +19979,7 @@ init_dist3();
|
|
|
19584
19979
|
import { Command as Command40 } from "commander";
|
|
19585
19980
|
import { existsSync as existsSync14, readFileSync as readFileSync21 } from "fs";
|
|
19586
19981
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
19587
|
-
import { dirname as dirname12, join as
|
|
19982
|
+
import { dirname as dirname12, join as join37 } from "path";
|
|
19588
19983
|
import { homedir as homedir21 } from "os";
|
|
19589
19984
|
|
|
19590
19985
|
// packages/cli/src/commands/doctor.ts
|
|
@@ -19595,9 +19990,9 @@ init_dist();
|
|
|
19595
19990
|
init_dist4();
|
|
19596
19991
|
import { Command } from "commander";
|
|
19597
19992
|
import { execSync as execSync7 } from "child_process";
|
|
19598
|
-
import
|
|
19993
|
+
import fs25 from "fs";
|
|
19599
19994
|
import { stat } from "fs/promises";
|
|
19600
|
-
import
|
|
19995
|
+
import path28 from "path";
|
|
19601
19996
|
import chalk3 from "chalk";
|
|
19602
19997
|
|
|
19603
19998
|
// packages/cli/src/commands/health-view.ts
|
|
@@ -19699,7 +20094,7 @@ function settingsHaveAgentTeams() {
|
|
|
19699
20094
|
try {
|
|
19700
20095
|
const home = process.env.HOME || "";
|
|
19701
20096
|
const settings = JSON.parse(
|
|
19702
|
-
|
|
20097
|
+
fs25.readFileSync(`${home}/.claude/settings.json`, "utf-8")
|
|
19703
20098
|
);
|
|
19704
20099
|
return settings?.env?.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS === "1";
|
|
19705
20100
|
} catch {
|
|
@@ -19710,7 +20105,7 @@ function pluginInstalled(pluginKey) {
|
|
|
19710
20105
|
try {
|
|
19711
20106
|
const home = process.env.HOME || "";
|
|
19712
20107
|
const plugins = JSON.parse(
|
|
19713
|
-
|
|
20108
|
+
fs25.readFileSync(
|
|
19714
20109
|
`${home}/.claude/plugins/installed_plugins.json`,
|
|
19715
20110
|
"utf-8"
|
|
19716
20111
|
)
|
|
@@ -19734,9 +20129,9 @@ function tryGetVersion(cmd) {
|
|
|
19734
20129
|
}
|
|
19735
20130
|
function candidateGlobalInstalls(roots) {
|
|
19736
20131
|
const out = [];
|
|
19737
|
-
if (roots.npm) out.push({ manager: "npm", packageJsonPath:
|
|
19738
|
-
if (roots.pnpm) out.push({ manager: "pnpm", packageJsonPath:
|
|
19739
|
-
if (roots.yarn) out.push({ manager: "yarn", packageJsonPath:
|
|
20132
|
+
if (roots.npm) out.push({ manager: "npm", packageJsonPath: path28.join(roots.npm, "squadrant", "package.json") });
|
|
20133
|
+
if (roots.pnpm) out.push({ manager: "pnpm", packageJsonPath: path28.join(roots.pnpm, "squadrant", "package.json") });
|
|
20134
|
+
if (roots.yarn) out.push({ manager: "yarn", packageJsonPath: path28.join(roots.yarn, "node_modules", "squadrant", "package.json") });
|
|
19740
20135
|
return out;
|
|
19741
20136
|
}
|
|
19742
20137
|
function findInstalledSquadrants(candidates, readVersion) {
|
|
@@ -19756,7 +20151,7 @@ ${lines.join("\n")}
|
|
|
19756
20151
|
}
|
|
19757
20152
|
function readPackageVersion(packageJsonPath) {
|
|
19758
20153
|
try {
|
|
19759
|
-
const pkg2 = JSON.parse(
|
|
20154
|
+
const pkg2 = JSON.parse(fs25.readFileSync(packageJsonPath, "utf-8"));
|
|
19760
20155
|
return typeof pkg2.version === "string" ? pkg2.version : null;
|
|
19761
20156
|
} catch {
|
|
19762
20157
|
return null;
|
|
@@ -19821,7 +20216,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
19821
20216
|
));
|
|
19822
20217
|
checkOptional(
|
|
19823
20218
|
"Obsidian installed",
|
|
19824
|
-
commandExists("obsidian") ||
|
|
20219
|
+
commandExists("obsidian") || fs25.existsSync("/Applications/Obsidian.app"),
|
|
19825
20220
|
"viewer for the hub vault; the vault is plain Markdown",
|
|
19826
20221
|
"Install from: https://obsidian.md"
|
|
19827
20222
|
);
|
|
@@ -19918,7 +20313,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
19918
20313
|
const emitter = projectionRegistry.get(name);
|
|
19919
20314
|
const [userDest] = emitter.destinations("user");
|
|
19920
20315
|
if (!userDest) continue;
|
|
19921
|
-
const dir =
|
|
20316
|
+
const dir = path28.dirname(userDest.path);
|
|
19922
20317
|
let status;
|
|
19923
20318
|
try {
|
|
19924
20319
|
await stat(dir);
|
|
@@ -19939,7 +20334,7 @@ var doctorCommand = new Command("doctor").description("Check system health and p
|
|
|
19939
20334
|
results.push(
|
|
19940
20335
|
check(
|
|
19941
20336
|
"Squadrant config exists",
|
|
19942
|
-
|
|
20337
|
+
fs25.existsSync(
|
|
19943
20338
|
process.env.SQUADRANT_CONFIG || `${process.env.HOME}/.config/squadrant/config.json`
|
|
19944
20339
|
),
|
|
19945
20340
|
"Run: squadrant init"
|
|
@@ -20023,8 +20418,8 @@ init_dist();
|
|
|
20023
20418
|
init_dist4();
|
|
20024
20419
|
init_dist();
|
|
20025
20420
|
import { Command as Command2 } from "commander";
|
|
20026
|
-
import
|
|
20027
|
-
import
|
|
20421
|
+
import fs26 from "fs";
|
|
20422
|
+
import path29 from "path";
|
|
20028
20423
|
import os15 from "os";
|
|
20029
20424
|
import readline from "readline";
|
|
20030
20425
|
import chalk4 from "chalk";
|
|
@@ -20275,22 +20670,22 @@ function detectClaudeAuth(probe = {}) {
|
|
|
20275
20670
|
// packages/cli/src/commands/init.ts
|
|
20276
20671
|
init_dist3();
|
|
20277
20672
|
function findPackageRoot() {
|
|
20278
|
-
let dir =
|
|
20673
|
+
let dir = path29.dirname(new URL(import.meta.url).pathname);
|
|
20279
20674
|
while (dir !== "/") {
|
|
20280
|
-
if (
|
|
20281
|
-
dir =
|
|
20675
|
+
if (fs26.existsSync(path29.join(dir, "package.json"))) return dir;
|
|
20676
|
+
dir = path29.dirname(dir);
|
|
20282
20677
|
}
|
|
20283
20678
|
return process.cwd();
|
|
20284
20679
|
}
|
|
20285
20680
|
function copyDirRecursive(src, dest) {
|
|
20286
|
-
|
|
20287
|
-
for (const entry of
|
|
20288
|
-
const srcPath =
|
|
20289
|
-
const destPath =
|
|
20681
|
+
fs26.mkdirSync(dest, { recursive: true });
|
|
20682
|
+
for (const entry of fs26.readdirSync(src, { withFileTypes: true })) {
|
|
20683
|
+
const srcPath = path29.join(src, entry.name);
|
|
20684
|
+
const destPath = path29.join(dest, entry.name);
|
|
20290
20685
|
if (entry.isDirectory()) {
|
|
20291
20686
|
copyDirRecursive(srcPath, destPath);
|
|
20292
20687
|
} else {
|
|
20293
|
-
|
|
20688
|
+
fs26.copyFileSync(srcPath, destPath);
|
|
20294
20689
|
}
|
|
20295
20690
|
}
|
|
20296
20691
|
}
|
|
@@ -20332,7 +20727,7 @@ function warnIfNoAnthropic(preset, auth) {
|
|
|
20332
20727
|
var initCommand = new Command2("init").description("Guided first-time setup: provider, hub vault, agents, plugins, projects (re-run-safe)").option("--hub <path>", "Hub vault path", "~/squadrant-hub").option("--preset <id>", "provider preset: a|b|c (non-interactive; d accepted as a deprecated alias for c)").action(async (opts) => {
|
|
20333
20728
|
const hubPath = resolveHome(opts.hub);
|
|
20334
20729
|
const pkgRoot = findPackageRoot();
|
|
20335
|
-
const configDir =
|
|
20730
|
+
const configDir = path29.join(os15.homedir(), ".config", "squadrant");
|
|
20336
20731
|
const isTTY = process.stdin.isTTY === true;
|
|
20337
20732
|
console.log(chalk4.bold("\nSquadrant Init\n"));
|
|
20338
20733
|
let explicitPreset;
|
|
@@ -20370,8 +20765,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
20370
20765
|
}
|
|
20371
20766
|
const wsRegistry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
20372
20767
|
try {
|
|
20373
|
-
if (
|
|
20374
|
-
const existing = JSON.parse(
|
|
20768
|
+
if (fs26.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
20769
|
+
const existing = JSON.parse(fs26.readFileSync(DEFAULT_CONFIG_PATH, "utf-8"));
|
|
20375
20770
|
wsRegistry.get(existing.workspace ?? "obsidian");
|
|
20376
20771
|
}
|
|
20377
20772
|
} catch (err) {
|
|
@@ -20379,7 +20774,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
20379
20774
|
return;
|
|
20380
20775
|
}
|
|
20381
20776
|
stepHeader(1, 5, "Hub vault");
|
|
20382
|
-
const configExists =
|
|
20777
|
+
const configExists = fs26.existsSync(DEFAULT_CONFIG_PATH);
|
|
20383
20778
|
let config;
|
|
20384
20779
|
if (configExists) {
|
|
20385
20780
|
console.log(chalk4.yellow(" \u26A0 Config already exists, skipping creation"));
|
|
@@ -20417,37 +20812,37 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
20417
20812
|
saveConfig(config);
|
|
20418
20813
|
console.log(chalk4.green(` \u2714 Config updated at ${DEFAULT_CONFIG_PATH}`));
|
|
20419
20814
|
}
|
|
20420
|
-
const hubTemplate =
|
|
20421
|
-
if (
|
|
20815
|
+
const hubTemplate = path29.join(pkgRoot, "obsidian", "hub");
|
|
20816
|
+
if (fs26.existsSync(hubPath)) {
|
|
20422
20817
|
console.log(chalk4.yellow(` \u26A0 Hub vault already exists at ${hubPath}`));
|
|
20423
|
-
} else if (
|
|
20818
|
+
} else if (fs26.existsSync(hubTemplate)) {
|
|
20424
20819
|
copyDirRecursive(hubTemplate, hubPath);
|
|
20425
20820
|
console.log(chalk4.green(` \u2714 Hub vault scaffolded at ${hubPath}`));
|
|
20426
20821
|
} else {
|
|
20427
|
-
|
|
20822
|
+
fs26.mkdirSync(hubPath, { recursive: true });
|
|
20428
20823
|
console.log(chalk4.yellow(` \u26A0 Hub template not found; created empty directory at ${hubPath}`));
|
|
20429
20824
|
}
|
|
20430
|
-
const hubDashboardSrc =
|
|
20431
|
-
const hubDashboardDest =
|
|
20432
|
-
if (
|
|
20433
|
-
|
|
20825
|
+
const hubDashboardSrc = path29.join(pkgRoot, "obsidian", "hub", "dashboard.md");
|
|
20826
|
+
const hubDashboardDest = path29.join(hubPath, "dashboard.md");
|
|
20827
|
+
if (fs26.existsSync(hubDashboardSrc)) {
|
|
20828
|
+
fs26.copyFileSync(hubDashboardSrc, hubDashboardDest);
|
|
20434
20829
|
console.log(chalk4.green(` \u2714 Dashboard refreshed`));
|
|
20435
20830
|
}
|
|
20436
|
-
|
|
20831
|
+
fs26.mkdirSync(path29.join(hubPath, "projects"), { recursive: true });
|
|
20437
20832
|
ensureRuntimeSynced({ sourceRoot: pkgRoot, runtimeRoot: configDir });
|
|
20438
20833
|
console.log(chalk4.green(` \u2714 Runtime assets synced to ${configDir}`));
|
|
20439
20834
|
stepHeader(2, 5, "Agent + projection setup");
|
|
20440
|
-
const settingsPath =
|
|
20835
|
+
const settingsPath = path29.join(os15.homedir(), ".claude", "settings.json");
|
|
20441
20836
|
try {
|
|
20442
20837
|
let settings = {};
|
|
20443
|
-
if (
|
|
20444
|
-
settings = JSON.parse(
|
|
20838
|
+
if (fs26.existsSync(settingsPath)) {
|
|
20839
|
+
settings = JSON.parse(fs26.readFileSync(settingsPath, "utf-8"));
|
|
20445
20840
|
}
|
|
20446
20841
|
const env = settings.env || {};
|
|
20447
20842
|
if (env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS !== "1") {
|
|
20448
20843
|
settings.env = { ...env, CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" };
|
|
20449
|
-
|
|
20450
|
-
|
|
20844
|
+
fs26.mkdirSync(path29.dirname(settingsPath), { recursive: true });
|
|
20845
|
+
fs26.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
20451
20846
|
console.log(chalk4.green(" \u2714 Agent Teams enabled in ~/.claude/settings.json"));
|
|
20452
20847
|
} else {
|
|
20453
20848
|
console.log(chalk4.green(" \u2714 Agent Teams already enabled"));
|
|
@@ -20500,7 +20895,7 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
20500
20895
|
chalk4.cyan(" Absolute path to your first project (Enter to skip): ")
|
|
20501
20896
|
);
|
|
20502
20897
|
if (projectPath) {
|
|
20503
|
-
const projectName =
|
|
20898
|
+
const projectName = path29.basename(projectPath);
|
|
20504
20899
|
console.log(chalk4.bold(`
|
|
20505
20900
|
Run this to register it:`));
|
|
20506
20901
|
console.log(chalk4.cyan(` squadrant projects add ${projectName} ${projectPath}
|
|
@@ -20528,8 +20923,8 @@ var initCommand = new Command2("init").description("Guided first-time setup: pro
|
|
|
20528
20923
|
init_dist();
|
|
20529
20924
|
init_dist2();
|
|
20530
20925
|
import { Command as Command3 } from "commander";
|
|
20531
|
-
import
|
|
20532
|
-
import
|
|
20926
|
+
import fs27 from "fs";
|
|
20927
|
+
import path30 from "path";
|
|
20533
20928
|
import chalk5 from "chalk";
|
|
20534
20929
|
function restartAfterProjectsAdd(opts) {
|
|
20535
20930
|
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
@@ -20541,22 +20936,22 @@ function restartAfterProjectsAdd(opts) {
|
|
|
20541
20936
|
}
|
|
20542
20937
|
}
|
|
20543
20938
|
function findPackageRoot2() {
|
|
20544
|
-
let dir =
|
|
20939
|
+
let dir = path30.dirname(new URL(import.meta.url).pathname);
|
|
20545
20940
|
while (dir !== "/") {
|
|
20546
|
-
if (
|
|
20547
|
-
dir =
|
|
20941
|
+
if (fs27.existsSync(path30.join(dir, "package.json"))) return dir;
|
|
20942
|
+
dir = path30.dirname(dir);
|
|
20548
20943
|
}
|
|
20549
20944
|
return process.cwd();
|
|
20550
20945
|
}
|
|
20551
20946
|
function copyDirRecursive2(src, dest) {
|
|
20552
|
-
|
|
20553
|
-
for (const entry of
|
|
20554
|
-
const srcPath =
|
|
20555
|
-
const destPath =
|
|
20947
|
+
fs27.mkdirSync(dest, { recursive: true });
|
|
20948
|
+
for (const entry of fs27.readdirSync(src, { withFileTypes: true })) {
|
|
20949
|
+
const srcPath = path30.join(src, entry.name);
|
|
20950
|
+
const destPath = path30.join(dest, entry.name);
|
|
20556
20951
|
if (entry.isDirectory()) {
|
|
20557
20952
|
copyDirRecursive2(srcPath, destPath);
|
|
20558
20953
|
} else {
|
|
20559
|
-
|
|
20954
|
+
fs27.copyFileSync(srcPath, destPath);
|
|
20560
20955
|
}
|
|
20561
20956
|
}
|
|
20562
20957
|
}
|
|
@@ -20592,7 +20987,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
20592
20987
|
process.exit(1);
|
|
20593
20988
|
}
|
|
20594
20989
|
const resolvedPath = resolveHome(projectPath);
|
|
20595
|
-
if (!
|
|
20990
|
+
if (!fs27.existsSync(path30.join(resolvedPath, ".git"))) {
|
|
20596
20991
|
console.log(chalk5.yellow(`
|
|
20597
20992
|
\u26A0 No .git found at ${resolvedPath}. Make sure this is the project root, not a parent directory.
|
|
20598
20993
|
`));
|
|
@@ -20645,7 +21040,7 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
20645
21040
|
\u26A0 Group '${group}' already has '${primary[0]}' as primary. Overriding.`));
|
|
20646
21041
|
}
|
|
20647
21042
|
}
|
|
20648
|
-
const spokeVault = opts.spoke ? resolveHome(opts.spoke) :
|
|
21043
|
+
const spokeVault = opts.spoke ? resolveHome(opts.spoke) : path30.join(config.hubVault, "spokes", name);
|
|
20649
21044
|
const project = {
|
|
20650
21045
|
path: resolvedPath,
|
|
20651
21046
|
captainName,
|
|
@@ -20660,20 +21055,20 @@ var addCmd = new Command3("add").description("Register a project").argument("<na
|
|
|
20660
21055
|
\u2714 Project '${name}' registered`));
|
|
20661
21056
|
restartAfterProjectsAdd({ noRestart: opts.restart === false });
|
|
20662
21057
|
const pkgRoot = findPackageRoot2();
|
|
20663
|
-
const spokeTemplate =
|
|
20664
|
-
if (
|
|
21058
|
+
const spokeTemplate = path30.join(pkgRoot, "obsidian", "spoke");
|
|
21059
|
+
if (fs27.existsSync(spokeVault)) {
|
|
20665
21060
|
console.log(chalk5.yellow(` \u26A0 Spoke vault already exists at ${spokeVault}, skipping scaffold`));
|
|
20666
|
-
} else if (
|
|
21061
|
+
} else if (fs27.existsSync(spokeTemplate)) {
|
|
20667
21062
|
copyDirRecursive2(spokeTemplate, spokeVault);
|
|
20668
|
-
const statusPath =
|
|
20669
|
-
if (
|
|
20670
|
-
const content =
|
|
21063
|
+
const statusPath = path30.join(spokeVault, "status.md");
|
|
21064
|
+
if (fs27.existsSync(statusPath)) {
|
|
21065
|
+
const content = fs27.readFileSync(statusPath, "utf-8");
|
|
20671
21066
|
const updated = content.replace(/^project: unnamed/m, `project: ${name}`);
|
|
20672
|
-
|
|
21067
|
+
fs27.writeFileSync(statusPath, updated);
|
|
20673
21068
|
}
|
|
20674
21069
|
console.log(chalk5.green(` \u2714 Spoke vault scaffolded at ${spokeVault}`));
|
|
20675
21070
|
} else {
|
|
20676
|
-
|
|
21071
|
+
fs27.mkdirSync(spokeVault, { recursive: true });
|
|
20677
21072
|
console.log(chalk5.yellow(` \u26A0 Spoke template not found; created empty dir at ${spokeVault}`));
|
|
20678
21073
|
}
|
|
20679
21074
|
console.log("");
|
|
@@ -20742,10 +21137,10 @@ init_dist3();
|
|
|
20742
21137
|
init_dist();
|
|
20743
21138
|
import { Command as Command5 } from "commander";
|
|
20744
21139
|
import { execSync as execSync9 } from "child_process";
|
|
20745
|
-
import
|
|
21140
|
+
import path31 from "path";
|
|
20746
21141
|
import os16 from "os";
|
|
20747
21142
|
import chalk7 from "chalk";
|
|
20748
|
-
var TEMPLATES_DIR2 =
|
|
21143
|
+
var TEMPLATES_DIR2 = path31.join(os16.homedir(), ".config", "squadrant", "templates");
|
|
20749
21144
|
var TASK_PROMPTS = {
|
|
20750
21145
|
briefing: "Run your daily briefing using the squadrant:command-ops skill. Read all spoke handoffs, yesterday's logs, current status; produce a concise cross-project briefing; save to {hubVault}/daily-logs/YYYY-MM-DD.md; then exit.",
|
|
20751
21146
|
"learnings-review": "Run a learnings review using the squadrant:command-ops skill. Scan {spokeVault}/learnings across all projects, identify cross-project patterns, propose captured-skill or fix actions, and exit when done.",
|
|
@@ -20778,7 +21173,7 @@ async function runCommandSpawn(input) {
|
|
|
20778
21173
|
if (!agent) {
|
|
20779
21174
|
throw new Error(`Unknown agent '${agentName}'. Known: claude, codex, gemini, opencode.`);
|
|
20780
21175
|
}
|
|
20781
|
-
const promptFile =
|
|
21176
|
+
const promptFile = path31.join(TEMPLATES_DIR2, `command.${agent.templateSuffix}.md`);
|
|
20782
21177
|
const cliCommand = agent.buildCommand({
|
|
20783
21178
|
prompt,
|
|
20784
21179
|
workdir: process.cwd(),
|
|
@@ -20807,9 +21202,9 @@ init_dist4();
|
|
|
20807
21202
|
init_dist3();
|
|
20808
21203
|
init_dist2();
|
|
20809
21204
|
import { Command as Command10 } from "commander";
|
|
20810
|
-
import
|
|
20811
|
-
import { createServer as
|
|
20812
|
-
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";
|
|
20813
21208
|
|
|
20814
21209
|
// packages/cli/src/commands/crew-control.ts
|
|
20815
21210
|
init_dist2();
|
|
@@ -20834,10 +21229,10 @@ function tailLines(text, maxLines = 40, maxBytes = 4096) {
|
|
|
20834
21229
|
if (Buffer.byteLength(result, "utf-8") > maxBytes) {
|
|
20835
21230
|
const truncated = [];
|
|
20836
21231
|
let bytes = 0;
|
|
20837
|
-
for (const
|
|
20838
|
-
const lineBytes = Buffer.byteLength(
|
|
21232
|
+
for (const line2 of kept) {
|
|
21233
|
+
const lineBytes = Buffer.byteLength(line2, "utf-8") + 1;
|
|
20839
21234
|
if (bytes + lineBytes > maxBytes) break;
|
|
20840
|
-
truncated.push(
|
|
21235
|
+
truncated.push(line2);
|
|
20841
21236
|
bytes += lineBytes;
|
|
20842
21237
|
}
|
|
20843
21238
|
result = truncated.join("\n");
|
|
@@ -21567,6 +21962,131 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
21567
21962
|
var crewControlCommand = new Command9("crew").description("Dispatch and track crew via the squadrant control plane");
|
|
21568
21963
|
addControlPlaneCrewCommands(crewControlCommand);
|
|
21569
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
|
+
|
|
21570
22090
|
// packages/cli/src/lib/model-guard.ts
|
|
21571
22091
|
var ANTHROPIC_MODEL_PREFIX = "anthropic/";
|
|
21572
22092
|
var BARE_CLAUDE_MODEL_PREFIX = "claude-";
|
|
@@ -21585,8 +22105,8 @@ function anthropicRefusalMessage(role, workspaceName, agentName, model) {
|
|
|
21585
22105
|
|
|
21586
22106
|
// packages/cli/src/commands/crew.ts
|
|
21587
22107
|
async function fetchRouterCredentials(project, backend, deps) {
|
|
21588
|
-
const log = deps.log ?? ((m) => console.log(
|
|
21589
|
-
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)));
|
|
21590
22110
|
const creds = await deps.call(buildRouterCredentialsRequest(project, backend));
|
|
21591
22111
|
if (creds.backend === "direct" && creds.apiKey) {
|
|
21592
22112
|
const approve = deps.approveKey ?? ensureClaudeApiKeyApproved;
|
|
@@ -21622,6 +22142,11 @@ async function runCrewSpawn2(input) {
|
|
|
21622
22142
|
writeOpencodeConfig: writePerCrewOpencodeConfig,
|
|
21623
22143
|
writeRouterSettings,
|
|
21624
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
|
+
} : {},
|
|
21625
22150
|
getFreePort,
|
|
21626
22151
|
sendCodexFirstTurn,
|
|
21627
22152
|
// #466: wire delivery confirmation so the daemon stamps firstTurnConfirmedAt.
|
|
@@ -21634,25 +22159,25 @@ async function runCrewSpawn2(input) {
|
|
|
21634
22159
|
getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
|
|
21635
22160
|
isAgentInstalled: (name) => commandExists(name),
|
|
21636
22161
|
onRouteSkipped: (route, fallback) => console.log(
|
|
21637
|
-
|
|
22162
|
+
chalk11.dim(`routed: tier=${route.tier} \u2192 ${route.agent} (not installed) \u2192 ${fallback}`)
|
|
21638
22163
|
),
|
|
21639
22164
|
onRouted: (route) => console.log(
|
|
21640
|
-
|
|
22165
|
+
chalk11.dim(
|
|
21641
22166
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
21642
22167
|
)
|
|
21643
22168
|
),
|
|
21644
|
-
onBaseResolved: (base) => console.log(
|
|
22169
|
+
onBaseResolved: (base) => console.log(chalk11.dim(`base: ${base}`)),
|
|
21645
22170
|
// #627 item B: warn (don't block) when a fallback crew silently resolves to
|
|
21646
22171
|
// an Anthropic model — less catastrophic than a captain on the same path,
|
|
21647
22172
|
// and more often intentional, so it just needs to be visible.
|
|
21648
22173
|
onBackendResolved: ({ backend }) => {
|
|
21649
|
-
if (backend !== "native") console.log(
|
|
22174
|
+
if (backend !== "native") console.log(chalk11.dim(`backend: ${backend}`));
|
|
21650
22175
|
},
|
|
21651
22176
|
onModelResolved: ({ agentName, model }) => {
|
|
21652
22177
|
const effectiveModel = model ?? (agentName === "opencode" ? readGlobalOpencodeModel() : void 0);
|
|
21653
22178
|
if (isBlockedFallback(agentName, effectiveModel)) {
|
|
21654
22179
|
console.error(
|
|
21655
|
-
|
|
22180
|
+
chalk11.yellow(
|
|
21656
22181
|
` \u26A0 ${anthropicFallbackMessage(agentName, effectiveModel)} (crew \u2014 not blocked; pass --model to pin a different provider)`
|
|
21657
22182
|
)
|
|
21658
22183
|
);
|
|
@@ -21666,8 +22191,8 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21666
22191
|
let tasks = [];
|
|
21667
22192
|
const receipts = new ClaudeReceiptListener({
|
|
21668
22193
|
socketPath: "/tmp/cc-socks/squadrantd.sock",
|
|
21669
|
-
createServer: (h) =>
|
|
21670
|
-
log: (m) => console.error(
|
|
22194
|
+
createServer: (h) => createServer5(h),
|
|
22195
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21671
22196
|
});
|
|
21672
22197
|
await receipts.start();
|
|
21673
22198
|
const controlChannels = [
|
|
@@ -21681,17 +22206,17 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21681
22206
|
// a prior crew's session. Gate on the crew's createdAt so a stale session
|
|
21682
22207
|
// the live TUI is not showing is never selected.
|
|
21683
22208
|
createdAfterFor: (taskId) => tasks.find((t) => t.id === taskId)?.createdAt,
|
|
21684
|
-
log: (m) => console.error(
|
|
22209
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21685
22210
|
}),
|
|
21686
22211
|
new ClaudePeerChannel({
|
|
21687
22212
|
socketPathFor: (taskId) => tasks.find((t) => t.id === taskId)?.messagingSocketPath,
|
|
21688
22213
|
sessionIdFor: (taskId) => tasks.find((t) => t.id === taskId)?.sessionId,
|
|
21689
22214
|
statusFor: (taskId) => readClaudeStatus(tasks.find((t) => t.id === taskId)),
|
|
21690
|
-
wire: (p, e) => writeLine(p, e, { connect: (
|
|
22215
|
+
wire: (p, e) => writeLine(p, e, { connect: (path46) => netConnect3(path46) }),
|
|
21691
22216
|
receipts,
|
|
21692
|
-
newMsgId: () =>
|
|
22217
|
+
newMsgId: () => randomUUID8(),
|
|
21693
22218
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
21694
|
-
log: (m) => console.error(
|
|
22219
|
+
log: (m) => console.error(chalk11.dim(m))
|
|
21695
22220
|
})
|
|
21696
22221
|
];
|
|
21697
22222
|
try {
|
|
@@ -21707,7 +22232,7 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
21707
22232
|
isBlockedByModal: (pane) => paneHasOpenModal(runtime, pane),
|
|
21708
22233
|
controlChannels,
|
|
21709
22234
|
controlChannelMode: (agent) => resolveControlChannelMode(cfg.defaults.controlChannel, agent),
|
|
21710
|
-
onChannelLog: (m) => console.error(
|
|
22235
|
+
onChannelLog: (m) => console.error(chalk11.dim(m))
|
|
21711
22236
|
}, opts);
|
|
21712
22237
|
} finally {
|
|
21713
22238
|
receipts.stop();
|
|
@@ -21743,7 +22268,14 @@ async function runCrewAnswer2(project, name, option, opts) {
|
|
|
21743
22268
|
workspaceId,
|
|
21744
22269
|
{
|
|
21745
22270
|
readModalOptions: (pane) => readModalOptions(runtime, pane),
|
|
21746
|
-
|
|
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))
|
|
21747
22279
|
},
|
|
21748
22280
|
opts
|
|
21749
22281
|
);
|
|
@@ -21781,9 +22313,9 @@ crewCommand.command("spawn").description(
|
|
|
21781
22313
|
// into the isolated worktree root for relative-path access.
|
|
21782
22314
|
...opts.taskFile && opts.taskFile !== "-" ? { taskFile: opts.taskFile } : {}
|
|
21783
22315
|
});
|
|
21784
|
-
console.log(
|
|
22316
|
+
console.log(chalk11.green(`\u2714 Crew '${pane.title}' spawned (${pane.surfaceId})`));
|
|
21785
22317
|
} catch (err) {
|
|
21786
|
-
console.error(
|
|
22318
|
+
console.error(chalk11.red(err.message));
|
|
21787
22319
|
process.exit(1);
|
|
21788
22320
|
}
|
|
21789
22321
|
}
|
|
@@ -21792,7 +22324,7 @@ crewCommand.command("list").description("List live crew sessions for a project")
|
|
|
21792
22324
|
try {
|
|
21793
22325
|
const crews = await runCrewList2(project);
|
|
21794
22326
|
if (crews.length === 0) {
|
|
21795
|
-
console.log(
|
|
22327
|
+
console.log(chalk11.yellow(`No live crew sessions for ${project}.`));
|
|
21796
22328
|
return;
|
|
21797
22329
|
}
|
|
21798
22330
|
const tasks = await squadrantdCall({ kind: "list", project }).catch(() => []);
|
|
@@ -21823,7 +22355,7 @@ crewCommand.command("list").description("List live crew sessions for a project")
|
|
|
21823
22355
|
}
|
|
21824
22356
|
}
|
|
21825
22357
|
} catch (err) {
|
|
21826
|
-
console.error(
|
|
22358
|
+
console.error(chalk11.red(err.message));
|
|
21827
22359
|
process.exit(1);
|
|
21828
22360
|
}
|
|
21829
22361
|
});
|
|
@@ -21831,10 +22363,10 @@ crewCommand.command("send").description("Send a follow-up message to an existing
|
|
|
21831
22363
|
try {
|
|
21832
22364
|
const resolvedMessage = await resolveTextInput({ positional: message, filePath: opts.messageFile, label: "message" });
|
|
21833
22365
|
const { reopened } = await runCrewSend2(project, name, resolvedMessage, opts);
|
|
21834
|
-
if (reopened) console.log(
|
|
21835
|
-
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}`));
|
|
21836
22368
|
} catch (e) {
|
|
21837
|
-
console.error(
|
|
22369
|
+
console.error(chalk11.red(e.message));
|
|
21838
22370
|
process.exit(1);
|
|
21839
22371
|
}
|
|
21840
22372
|
});
|
|
@@ -21844,12 +22376,12 @@ crewCommand.command("answer").description(
|
|
|
21844
22376
|
try {
|
|
21845
22377
|
const { selected, closed } = await runCrewAnswer2(project, name, option, opts);
|
|
21846
22378
|
if (closed) {
|
|
21847
|
-
console.log(
|
|
22379
|
+
console.log(chalk11.green(`\u2714 Answered ${project}:${name} with ${selected.index}. "${selected.label}" \u2014 prompt closed`));
|
|
21848
22380
|
} else {
|
|
21849
|
-
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}'`));
|
|
21850
22382
|
}
|
|
21851
22383
|
} catch (err) {
|
|
21852
|
-
console.error(
|
|
22384
|
+
console.error(chalk11.red(err.message));
|
|
21853
22385
|
process.exit(1);
|
|
21854
22386
|
}
|
|
21855
22387
|
});
|
|
@@ -21859,16 +22391,16 @@ crewCommand.command("read").description("Read the current screen of a crew sessi
|
|
|
21859
22391
|
const out = opts.full ? screen : tailLines(screen, Number(opts.lines ?? 40));
|
|
21860
22392
|
console.log(out);
|
|
21861
22393
|
} catch (err) {
|
|
21862
|
-
console.error(
|
|
22394
|
+
console.error(chalk11.red(err.message));
|
|
21863
22395
|
process.exit(1);
|
|
21864
22396
|
}
|
|
21865
22397
|
});
|
|
21866
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) => {
|
|
21867
22399
|
try {
|
|
21868
22400
|
await runCrewClose2(project, name, opts);
|
|
21869
|
-
console.log(
|
|
22401
|
+
console.log(chalk11.green(`\u2714 Closed ${project}:${name}`));
|
|
21870
22402
|
} catch (err) {
|
|
21871
|
-
console.error(
|
|
22403
|
+
console.error(chalk11.red(err.message));
|
|
21872
22404
|
process.exit(1);
|
|
21873
22405
|
}
|
|
21874
22406
|
});
|
|
@@ -21877,7 +22409,7 @@ crewCommand.command("close").description("Shutdown a crew session (closes its ta
|
|
|
21877
22409
|
init_dist();
|
|
21878
22410
|
init_dist4();
|
|
21879
22411
|
import { Command as Command11 } from "commander";
|
|
21880
|
-
import
|
|
22412
|
+
import chalk12 from "chalk";
|
|
21881
22413
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
21882
22414
|
import readline2 from "readline";
|
|
21883
22415
|
function resolveDiffTarget(tasks, crew, projectPath) {
|
|
@@ -21994,7 +22526,7 @@ async function openCrewDiff(project, proj, crew, opts, runtime, workspaceId) {
|
|
|
21994
22526
|
const label = sources.length > 1 ? "staged or unstaged" : sources[0];
|
|
21995
22527
|
console.log(`No ${label} changes on ${branchLabel}.`);
|
|
21996
22528
|
} else {
|
|
21997
|
-
console.log(
|
|
22529
|
+
console.log(chalk12.dim(`Opened ${opened} working-tree diff(s) (${sources.join(", ")}) for ${branchLabel}.`));
|
|
21998
22530
|
}
|
|
21999
22531
|
return;
|
|
22000
22532
|
}
|
|
@@ -22017,7 +22549,7 @@ async function openCrewDiff(project, proj, crew, opts, runtime, workspaceId) {
|
|
|
22017
22549
|
lastTurn: opts.lastTurn,
|
|
22018
22550
|
source: "branch"
|
|
22019
22551
|
});
|
|
22020
|
-
console.log(
|
|
22552
|
+
console.log(chalk12.dim(`Opened ${branchLabel} vs ${base} in cmux diff.`));
|
|
22021
22553
|
}
|
|
22022
22554
|
async function runDiff(project, crewArg, opts) {
|
|
22023
22555
|
const config = loadConfig();
|
|
@@ -22038,7 +22570,7 @@ async function runDiff(project, crewArg, opts) {
|
|
|
22038
22570
|
return;
|
|
22039
22571
|
}
|
|
22040
22572
|
await runtime.showPatch({ workspaceId, patch, title, layout: opts.layout, focus: opts.focus });
|
|
22041
|
-
console.log(
|
|
22573
|
+
console.log(chalk12.dim(`Opened ${title} in cmux diff.`));
|
|
22042
22574
|
return;
|
|
22043
22575
|
}
|
|
22044
22576
|
let crew;
|
|
@@ -22074,11 +22606,11 @@ init_dist4();
|
|
|
22074
22606
|
init_dist();
|
|
22075
22607
|
init_dist2();
|
|
22076
22608
|
import { Command as Command12 } from "commander";
|
|
22077
|
-
import
|
|
22078
|
-
import
|
|
22609
|
+
import fs29 from "fs";
|
|
22610
|
+
import path32 from "path";
|
|
22079
22611
|
import os17 from "os";
|
|
22080
|
-
import
|
|
22081
|
-
var TEMPLATES_DIR3 =
|
|
22612
|
+
import chalk13 from "chalk";
|
|
22613
|
+
var TEMPLATES_DIR3 = path32.join(os17.homedir(), ".config", "squadrant", "templates");
|
|
22082
22614
|
async function runSideSpawn2(input) {
|
|
22083
22615
|
const config = loadConfig();
|
|
22084
22616
|
const proj = config.projects[input.project];
|
|
@@ -22103,7 +22635,7 @@ async function runSideSpawn2(input) {
|
|
|
22103
22635
|
}
|
|
22104
22636
|
const sideModel = sideRole?.model;
|
|
22105
22637
|
const sideThinking = sideRole?.thinking;
|
|
22106
|
-
const promptFile =
|
|
22638
|
+
const promptFile = path32.join(
|
|
22107
22639
|
TEMPLATES_DIR3,
|
|
22108
22640
|
`side.${input.role}.${agent.templateSuffix}.md`
|
|
22109
22641
|
);
|
|
@@ -22111,7 +22643,7 @@ async function runSideSpawn2(input) {
|
|
|
22111
22643
|
prompt: input.topic,
|
|
22112
22644
|
workdir: spawnCwd,
|
|
22113
22645
|
role: "side",
|
|
22114
|
-
promptFile:
|
|
22646
|
+
promptFile: fs29.existsSync(promptFile) ? promptFile : void 0,
|
|
22115
22647
|
interactive: true,
|
|
22116
22648
|
permissionMode: config.defaults.permissions?.crew ?? "auto",
|
|
22117
22649
|
...sideModel ? { model: sideModel } : {},
|
|
@@ -22166,9 +22698,9 @@ sideCommand.command("spawn").description(
|
|
|
22166
22698
|
direction: opts.direction,
|
|
22167
22699
|
agent: opts.agent
|
|
22168
22700
|
});
|
|
22169
|
-
console.log(
|
|
22701
|
+
console.log(chalk13.green(`\u2714 Side session '${pane.title}' spawned (${pane.surfaceId})`));
|
|
22170
22702
|
} catch (err) {
|
|
22171
|
-
console.error(
|
|
22703
|
+
console.error(chalk13.red(err.message));
|
|
22172
22704
|
process.exit(1);
|
|
22173
22705
|
}
|
|
22174
22706
|
}
|
|
@@ -22177,14 +22709,14 @@ sideCommand.command("list").description("List live side-sessions for a project")
|
|
|
22177
22709
|
try {
|
|
22178
22710
|
const sessions = await runSideList2(project);
|
|
22179
22711
|
if (sessions.length === 0) {
|
|
22180
|
-
console.log(
|
|
22712
|
+
console.log(chalk13.yellow(`No live side-sessions for ${project}.`));
|
|
22181
22713
|
return;
|
|
22182
22714
|
}
|
|
22183
22715
|
for (const s of sessions) {
|
|
22184
22716
|
console.log(` ${s.name} (${s.surfaceId})`);
|
|
22185
22717
|
}
|
|
22186
22718
|
} catch (err) {
|
|
22187
|
-
console.error(
|
|
22719
|
+
console.error(chalk13.red(err.message));
|
|
22188
22720
|
process.exit(1);
|
|
22189
22721
|
}
|
|
22190
22722
|
});
|
|
@@ -22197,9 +22729,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
|
|
|
22197
22729
|
label: "message"
|
|
22198
22730
|
});
|
|
22199
22731
|
await runSideSend2(project, name, resolvedMessage);
|
|
22200
|
-
console.log(
|
|
22732
|
+
console.log(chalk13.green(`\u2714 Sent to ${project}:${name}`));
|
|
22201
22733
|
} catch (err) {
|
|
22202
|
-
console.error(
|
|
22734
|
+
console.error(chalk13.red(err.message));
|
|
22203
22735
|
process.exit(1);
|
|
22204
22736
|
}
|
|
22205
22737
|
}
|
|
@@ -22207,9 +22739,9 @@ sideCommand.command("send").description("Send a follow-up message to an existing
|
|
|
22207
22739
|
sideCommand.command("close").description("Close a side-session (closes its tab)").argument("<project>", "Project name").argument("<name>", "Session name").action(async (project, name) => {
|
|
22208
22740
|
try {
|
|
22209
22741
|
await runSideClose2(project, name);
|
|
22210
|
-
console.log(
|
|
22742
|
+
console.log(chalk13.green(`\u2714 Closed ${project}:${name}`));
|
|
22211
22743
|
} catch (err) {
|
|
22212
|
-
console.error(
|
|
22744
|
+
console.error(chalk13.red(err.message));
|
|
22213
22745
|
process.exit(1);
|
|
22214
22746
|
}
|
|
22215
22747
|
});
|
|
@@ -22219,7 +22751,7 @@ init_dist();
|
|
|
22219
22751
|
init_dist4();
|
|
22220
22752
|
import { Command as Command13 } from "commander";
|
|
22221
22753
|
import { execSync as execSync10 } from "child_process";
|
|
22222
|
-
import
|
|
22754
|
+
import chalk15 from "chalk";
|
|
22223
22755
|
|
|
22224
22756
|
// packages/web/dist/read-status.js
|
|
22225
22757
|
function deriveState(tasks) {
|
|
@@ -22296,14 +22828,14 @@ async function readAllStatuses(deps) {
|
|
|
22296
22828
|
}
|
|
22297
22829
|
|
|
22298
22830
|
// packages/web/dist/render.js
|
|
22299
|
-
import
|
|
22831
|
+
import chalk14 from "chalk";
|
|
22300
22832
|
var ICON = {
|
|
22301
|
-
idle:
|
|
22302
|
-
busy:
|
|
22303
|
-
blocked:
|
|
22304
|
-
errored:
|
|
22305
|
-
offline:
|
|
22306
|
-
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
|
|
22307
22839
|
};
|
|
22308
22840
|
var ICON_CHAR = {
|
|
22309
22841
|
idle: "\u25CF",
|
|
@@ -22330,9 +22862,9 @@ function formatAge(lastChecked, now) {
|
|
|
22330
22862
|
return "stale";
|
|
22331
22863
|
}
|
|
22332
22864
|
function firstLine(s) {
|
|
22333
|
-
for (const
|
|
22334
|
-
if (
|
|
22335
|
-
return
|
|
22865
|
+
for (const line2 of s.split(/\r?\n/)) {
|
|
22866
|
+
if (line2.trim().length > 0)
|
|
22867
|
+
return line2.trim();
|
|
22336
22868
|
}
|
|
22337
22869
|
return "";
|
|
22338
22870
|
}
|
|
@@ -22346,10 +22878,10 @@ function renderDashboard(rows, opts) {
|
|
|
22346
22878
|
const width = opts.width ?? 100;
|
|
22347
22879
|
const lines = [];
|
|
22348
22880
|
lines.push("");
|
|
22349
|
-
lines.push(" " +
|
|
22881
|
+
lines.push(" " + chalk14.bold("\u{1F4CA} Squadrant Dashboard") + " " + chalk14.dim(opts.now));
|
|
22350
22882
|
lines.push("");
|
|
22351
22883
|
if (rows.length === 0) {
|
|
22352
|
-
lines.push(" " +
|
|
22884
|
+
lines.push(" " + chalk14.yellow("No projects registered. Add one with: squadrant projects add <name> <path>"));
|
|
22353
22885
|
lines.push("");
|
|
22354
22886
|
return lines.join("\n");
|
|
22355
22887
|
}
|
|
@@ -22360,22 +22892,22 @@ function renderDashboard(rows, opts) {
|
|
|
22360
22892
|
const excerptW = Math.max(20, width - FIXED);
|
|
22361
22893
|
for (const r of rows) {
|
|
22362
22894
|
const icon = ICON[r.state](ICON_CHAR[r.state]);
|
|
22363
|
-
const name =
|
|
22895
|
+
const name = chalk14.cyan(pad(r.project, NAME_W));
|
|
22364
22896
|
const state = ICON[r.state](pad(r.state, STATE_W));
|
|
22365
22897
|
const age = pad(formatAge(r.lastChecked, opts.now), AGE_W);
|
|
22366
|
-
const excerpt =
|
|
22898
|
+
const excerpt = chalk14.dim(truncate(firstLine(r.excerpt), excerptW));
|
|
22367
22899
|
lines.push(` ${icon} ${name} ${state} ${age} \u2502 ${excerpt}`);
|
|
22368
22900
|
}
|
|
22369
22901
|
lines.push("");
|
|
22370
|
-
lines.push(
|
|
22902
|
+
lines.push(chalk14.dim(" Refreshes every 10s \xB7 Ctrl+C to exit"));
|
|
22371
22903
|
lines.push("");
|
|
22372
22904
|
return lines.join("\n");
|
|
22373
22905
|
}
|
|
22374
22906
|
|
|
22375
22907
|
// packages/web/dist/sync-hub.js
|
|
22376
22908
|
init_dist();
|
|
22377
|
-
import
|
|
22378
|
-
import
|
|
22909
|
+
import fs30 from "fs";
|
|
22910
|
+
import path33 from "path";
|
|
22379
22911
|
function buildMirrorMarkdown(s) {
|
|
22380
22912
|
const fenced = "```";
|
|
22381
22913
|
return [
|
|
@@ -22401,15 +22933,15 @@ function buildMirrorMarkdown(s) {
|
|
|
22401
22933
|
function syncHub(deps) {
|
|
22402
22934
|
if (!deps.config.hubVault)
|
|
22403
22935
|
return [];
|
|
22404
|
-
const writeFile6 = deps.writeFile ?? ((p, c) =>
|
|
22405
|
-
const mkdir6 = deps.mkdir ?? ((p) =>
|
|
22406
|
-
const projectsDir =
|
|
22936
|
+
const writeFile6 = deps.writeFile ?? ((p, c) => fs30.writeFileSync(p, c));
|
|
22937
|
+
const mkdir6 = deps.mkdir ?? ((p) => fs30.mkdirSync(p, { recursive: true }));
|
|
22938
|
+
const projectsDir = path33.join(resolveHome(deps.config.hubVault), "projects");
|
|
22407
22939
|
mkdir6(projectsDir);
|
|
22408
22940
|
const out = [];
|
|
22409
22941
|
for (const s of deps.statuses) {
|
|
22410
22942
|
if (s.state === "unknown")
|
|
22411
22943
|
continue;
|
|
22412
|
-
const hubPath =
|
|
22944
|
+
const hubPath = path33.join(projectsDir, `${s.project}.md`);
|
|
22413
22945
|
try {
|
|
22414
22946
|
writeFile6(hubPath, buildMirrorMarkdown(s));
|
|
22415
22947
|
out.push({ project: s.project, hubPath });
|
|
@@ -22421,8 +22953,8 @@ function syncHub(deps) {
|
|
|
22421
22953
|
|
|
22422
22954
|
// packages/web/dist/web-server.js
|
|
22423
22955
|
init_dist2();
|
|
22424
|
-
import { createServer as
|
|
22425
|
-
import { dirname as dirname7, join as
|
|
22956
|
+
import { createServer as createServer6 } from "http";
|
|
22957
|
+
import { dirname as dirname7, join as join30 } from "path";
|
|
22426
22958
|
|
|
22427
22959
|
// packages/web/dist/snapshot-merge.js
|
|
22428
22960
|
function mergeSnapshot(daemon, external, now) {
|
|
@@ -22432,7 +22964,7 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
22432
22964
|
// packages/web/dist/probes.js
|
|
22433
22965
|
init_dist();
|
|
22434
22966
|
init_dist();
|
|
22435
|
-
import { join as
|
|
22967
|
+
import { join as join29 } from "path";
|
|
22436
22968
|
import { homedir as homedir16 } from "os";
|
|
22437
22969
|
import { existsSync as existsSync13, readFileSync as readFileSync20 } from "fs";
|
|
22438
22970
|
import { execFile as execFile4 } from "child_process";
|
|
@@ -22470,7 +23002,7 @@ function vaultProbe(run, dir) {
|
|
|
22470
23002
|
return { state: "unknown", detail: "no vault configured" };
|
|
22471
23003
|
if (!run.pathExists(dir))
|
|
22472
23004
|
return { state: "gone", detail: "vault directory missing" };
|
|
22473
|
-
if (!run.pathExists(
|
|
23005
|
+
if (!run.pathExists(join29(dir, ".obsidian")))
|
|
22474
23006
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
22475
23007
|
return { state: "alive" };
|
|
22476
23008
|
} catch {
|
|
@@ -22538,10 +23070,10 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
|
22538
23070
|
const sessions = probeSessions(run);
|
|
22539
23071
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
22540
23072
|
}
|
|
22541
|
-
var SESSIONS_PATH =
|
|
23073
|
+
var SESSIONS_PATH = join29(homedir16(), ".config", "squadrant", "sessions.json");
|
|
22542
23074
|
function onPath(cli) {
|
|
22543
23075
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
22544
|
-
return dirs.some((d) => existsSync13(
|
|
23076
|
+
return dirs.some((d) => existsSync13(join29(d, cli)));
|
|
22545
23077
|
}
|
|
22546
23078
|
function readSessionsHashes() {
|
|
22547
23079
|
const raw = JSON.parse(readFileSync20(SESSIONS_PATH, "utf-8"));
|
|
@@ -23415,18 +23947,18 @@ function renderHtml(snap, opts = {}) {
|
|
|
23415
23947
|
import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2, statSync as statSync4 } from "fs";
|
|
23416
23948
|
var LOG_TAIL_BYTES = 256 * 1024;
|
|
23417
23949
|
var LOG_RING_SIZE = 1e3;
|
|
23418
|
-
function defaultSize(
|
|
23950
|
+
function defaultSize(path46) {
|
|
23419
23951
|
try {
|
|
23420
|
-
return statSync4(
|
|
23952
|
+
return statSync4(path46).size;
|
|
23421
23953
|
} catch {
|
|
23422
23954
|
return 0;
|
|
23423
23955
|
}
|
|
23424
23956
|
}
|
|
23425
|
-
function defaultRead(
|
|
23957
|
+
function defaultRead(path46, start, len) {
|
|
23426
23958
|
if (len <= 0)
|
|
23427
23959
|
return "";
|
|
23428
23960
|
try {
|
|
23429
|
-
const fd = openSync3(
|
|
23961
|
+
const fd = openSync3(path46, "r");
|
|
23430
23962
|
try {
|
|
23431
23963
|
const buf = Buffer.alloc(len);
|
|
23432
23964
|
const n = readSync2(fd, buf, 0, len, start);
|
|
@@ -23446,8 +23978,8 @@ var LogTailer = class {
|
|
|
23446
23978
|
ring = [];
|
|
23447
23979
|
size;
|
|
23448
23980
|
read;
|
|
23449
|
-
constructor(
|
|
23450
|
-
this.path =
|
|
23981
|
+
constructor(path46, ringSize = LOG_RING_SIZE, deps = {}) {
|
|
23982
|
+
this.path = path46;
|
|
23451
23983
|
this.ringSize = ringSize;
|
|
23452
23984
|
this.size = deps.size ?? defaultSize;
|
|
23453
23985
|
this.read = deps.read ?? defaultRead;
|
|
@@ -23518,7 +24050,7 @@ async function startWebServer(opts) {
|
|
|
23518
24050
|
const now = opts.now ?? (() => Date.now());
|
|
23519
24051
|
const clients = /* @__PURE__ */ new Set();
|
|
23520
24052
|
const logClients = /* @__PURE__ */ new Set();
|
|
23521
|
-
const logPath2 = opts.logPath ??
|
|
24053
|
+
const logPath2 = opts.logPath ?? join30(dirname7(opts.sockPath), "squadrantd.log");
|
|
23522
24054
|
const tailer = new LogTailer(logPath2);
|
|
23523
24055
|
let latest = null;
|
|
23524
24056
|
async function tick() {
|
|
@@ -23548,7 +24080,7 @@ async function startWebServer(opts) {
|
|
|
23548
24080
|
}
|
|
23549
24081
|
}
|
|
23550
24082
|
}
|
|
23551
|
-
const server =
|
|
24083
|
+
const server = createServer6((req, res) => {
|
|
23552
24084
|
if (req.method !== "GET") {
|
|
23553
24085
|
res.writeHead(405).end();
|
|
23554
24086
|
return;
|
|
@@ -23679,8 +24211,8 @@ async function runDashboardWeb(input) {
|
|
|
23679
24211
|
sockPath: SOCK3,
|
|
23680
24212
|
runners: defaultProbeRunners()
|
|
23681
24213
|
});
|
|
23682
|
-
console.log(
|
|
23683
|
-
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`));
|
|
23684
24216
|
}
|
|
23685
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) => {
|
|
23686
24218
|
try {
|
|
@@ -23690,12 +24222,12 @@ var dashboardCommand = new Command13("dashboard").description("Live status grid
|
|
|
23690
24222
|
}
|
|
23691
24223
|
if (opts.pane) {
|
|
23692
24224
|
const pane = await runDashboardPane({ direction: opts.direction, interval: opts.interval ?? 10 });
|
|
23693
|
-
console.log(
|
|
24225
|
+
console.log(chalk15.green(`\u2714 Dashboard pane opened in ${pane.workspaceId} ${pane.surfaceId}`));
|
|
23694
24226
|
return;
|
|
23695
24227
|
}
|
|
23696
24228
|
await runDashboardOnce();
|
|
23697
24229
|
} catch (err) {
|
|
23698
|
-
console.error(
|
|
24230
|
+
console.error(chalk15.red(err.message));
|
|
23699
24231
|
process.exit(1);
|
|
23700
24232
|
}
|
|
23701
24233
|
});
|
|
@@ -23706,12 +24238,12 @@ dashboardCommand.command("sync-hub").description("Mirror each spoke status.md in
|
|
|
23706
24238
|
return;
|
|
23707
24239
|
}
|
|
23708
24240
|
if (results.length === 0) {
|
|
23709
|
-
console.log(
|
|
24241
|
+
console.log(chalk15.dim("\n No mirrors written (no projects with usable status.md, or hubVault unset).\n"));
|
|
23710
24242
|
return;
|
|
23711
24243
|
}
|
|
23712
|
-
console.log(
|
|
24244
|
+
console.log(chalk15.bold("\n \u{1F4CA} Hub mirror sync\n"));
|
|
23713
24245
|
for (const r of results) {
|
|
23714
|
-
console.log(` ${
|
|
24246
|
+
console.log(` ${chalk15.green("\u2714")} ${chalk15.cyan(r.project.padEnd(16))} \u2192 ${chalk15.dim(r.hubPath)}`);
|
|
23715
24247
|
}
|
|
23716
24248
|
console.log("");
|
|
23717
24249
|
});
|
|
@@ -23723,10 +24255,10 @@ init_dist4();
|
|
|
23723
24255
|
init_dist2();
|
|
23724
24256
|
import { Command as Command14 } from "commander";
|
|
23725
24257
|
import { execSync as execSync11, execFileSync as execFileSync11 } from "child_process";
|
|
23726
|
-
import
|
|
23727
|
-
import
|
|
24258
|
+
import fs31 from "fs";
|
|
24259
|
+
import path34 from "path";
|
|
23728
24260
|
import os18 from "os";
|
|
23729
|
-
import
|
|
24261
|
+
import chalk16 from "chalk";
|
|
23730
24262
|
|
|
23731
24263
|
// packages/cli/src/commands/launch-interactive.ts
|
|
23732
24264
|
import checkbox, { Separator } from "@inquirer/checkbox";
|
|
@@ -23817,8 +24349,8 @@ function resolveLaunchBackend(o) {
|
|
|
23817
24349
|
// packages/cli/src/commands/launch.ts
|
|
23818
24350
|
init_dist2();
|
|
23819
24351
|
var CMUX_APP = "/Applications/cmux.app";
|
|
23820
|
-
var TEMPLATES_DIR4 =
|
|
23821
|
-
var SESSIONS_PATH2 =
|
|
24352
|
+
var TEMPLATES_DIR4 = path34.join(os18.homedir(), ".config", "squadrant", "templates");
|
|
24353
|
+
var SESSIONS_PATH2 = path34.join(os18.homedir(), ".config", "squadrant", "sessions.json");
|
|
23822
24354
|
function shouldWireCaptainChannel(agentName, config) {
|
|
23823
24355
|
return agentName === "claude" && resolveCaptainChannelMode(config.defaults) !== "off";
|
|
23824
24356
|
}
|
|
@@ -23853,20 +24385,20 @@ function resolveAnthropicRefusal(role, workspaceName, agentName, model, readOpen
|
|
|
23853
24385
|
}
|
|
23854
24386
|
function ensureCmuxReady(headless) {
|
|
23855
24387
|
if (headless || isInsideCmux()) return;
|
|
23856
|
-
console.log(
|
|
24388
|
+
console.log(chalk16.yellow("\n Not running inside cmux. Opening cmux app...\n"));
|
|
23857
24389
|
execSync11(`open "${CMUX_APP}"`, { stdio: "inherit" });
|
|
23858
|
-
console.log(
|
|
24390
|
+
console.log(chalk16.bold(" Run `squadrant launch` from inside a cmux workspace.\n"));
|
|
23859
24391
|
process.exit(0);
|
|
23860
24392
|
}
|
|
23861
24393
|
var launchCommand = new Command14("launch").description(
|
|
23862
24394
|
"Launch a project captain (with project arg) or all captains (--all). Use `squadrant command` for one-shot Command tasks."
|
|
23863
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) => {
|
|
23864
24396
|
if (opts.fresh && opts.keep) {
|
|
23865
|
-
console.error(
|
|
24397
|
+
console.error(chalk16.red("\n \u2718 --fresh and --keep are mutually exclusive\n"));
|
|
23866
24398
|
process.exit(1);
|
|
23867
24399
|
}
|
|
23868
24400
|
if (opts.backend !== void 0 && !isBackendMode(opts.backend)) {
|
|
23869
|
-
console.error(
|
|
24401
|
+
console.error(chalk16.red(`
|
|
23870
24402
|
\u2718 Invalid --backend value '${opts.backend}'. Valid values: native, direct, proxy
|
|
23871
24403
|
`));
|
|
23872
24404
|
process.exit(1);
|
|
@@ -23876,7 +24408,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
23876
24408
|
try {
|
|
23877
24409
|
thinkingOverride = opts.thinking ? parseThinkingLevel(opts.thinking) : void 0;
|
|
23878
24410
|
} catch (err) {
|
|
23879
|
-
console.error(
|
|
24411
|
+
console.error(chalk16.red(`
|
|
23880
24412
|
\u2718 ${err.message}
|
|
23881
24413
|
`));
|
|
23882
24414
|
process.exit(1);
|
|
@@ -23901,7 +24433,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
23901
24433
|
const backend = resolveLaunchBackend({ backendOverride, agentName, roleConfig });
|
|
23902
24434
|
const refusal = resolveAnthropicRefusal(role, workspaceName, agentName, model);
|
|
23903
24435
|
if (refusal) {
|
|
23904
|
-
console.error(
|
|
24436
|
+
console.error(chalk16.red(`
|
|
23905
24437
|
\u2718 ${refusal}
|
|
23906
24438
|
`));
|
|
23907
24439
|
hadFailure = true;
|
|
@@ -23915,10 +24447,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
23915
24447
|
initialPrompt = "Run your startup checklist: use the squadrant:command-ops skill, complete your daily briefing, then report ready.";
|
|
23916
24448
|
}
|
|
23917
24449
|
const runtime = projectName ? runtimes.forProject(projectName, config) : runtimes.global(config);
|
|
23918
|
-
const stateRoot =
|
|
24450
|
+
const stateRoot = path34.join(os18.homedir(), ".config", "squadrant", "state");
|
|
23919
24451
|
const isOpencodeCaptain = role === "captain" && agentName === "opencode" && !!projectName;
|
|
23920
24452
|
if (isOpencodeCaptain && !isOpencodeCaptainDir(cwd)) {
|
|
23921
|
-
console.error(
|
|
24453
|
+
console.error(chalk16.red(`
|
|
23922
24454
|
\u2718 '${projectName}' is not a git repo with a commit \u2014 an opencode captain needs a stable project identity. Run 'git commit' first.
|
|
23923
24455
|
`));
|
|
23924
24456
|
hadFailure = true;
|
|
@@ -23949,10 +24481,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
23949
24481
|
routerCredentials: (o) => fetchRouterCredentials(o.project, o.backend, { call: squadrantdCall }),
|
|
23950
24482
|
writeRouterSettings
|
|
23951
24483
|
},
|
|
23952
|
-
warn: (m) => console.error(
|
|
24484
|
+
warn: (m) => console.error(chalk16.yellow(` \u26A0 ${m}`))
|
|
23953
24485
|
});
|
|
23954
24486
|
} catch (err) {
|
|
23955
|
-
console.error(
|
|
24487
|
+
console.error(chalk16.red(` \u2718 ${err.message}`));
|
|
23956
24488
|
hadFailure = true;
|
|
23957
24489
|
return;
|
|
23958
24490
|
}
|
|
@@ -23992,7 +24524,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
23992
24524
|
route?.settingsPath
|
|
23993
24525
|
);
|
|
23994
24526
|
if (gateDecision?.warn) {
|
|
23995
|
-
console.error(
|
|
24527
|
+
console.error(chalk16.yellow(` \u26A0 gate: ${gateDecision.reason} \u2014 launching opencode captain directly (P6-C safety fallback)`));
|
|
23996
24528
|
}
|
|
23997
24529
|
const effectiveCmd = gateDecision?.wrap && captainBoot?.port ? buildOpencodeGateRunCommand({ port: captainBoot.port, sessionId: captainBoot.sessionId }) : baseCmd;
|
|
23998
24530
|
const gatePrefix = route && Object.keys(route.env).length > 0 ? `${renderEnvAssignments(route.env)} ` : "";
|
|
@@ -24012,10 +24544,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
24012
24544
|
return null;
|
|
24013
24545
|
}
|
|
24014
24546
|
},
|
|
24015
|
-
onFreshReason: (reason) => console.log(
|
|
24016
|
-
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`)),
|
|
24017
24549
|
onAlreadyExists: (name) => {
|
|
24018
|
-
console.log(
|
|
24550
|
+
console.log(chalk16.yellow(` Workspace '${name}' already exists \u2014 switching to it`));
|
|
24019
24551
|
if (!projectName || !isOpencodeCaptain) return;
|
|
24020
24552
|
const live = discoverLiveOpencodeServer({
|
|
24021
24553
|
directory: realpathOrSelf(cwd),
|
|
@@ -24033,7 +24565,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24033
24565
|
});
|
|
24034
24566
|
},
|
|
24035
24567
|
onCreated: (name) => {
|
|
24036
|
-
console.log(
|
|
24568
|
+
console.log(chalk16.green(` \u2714 Workspace '${name}' created`));
|
|
24037
24569
|
if (!projectName) return;
|
|
24038
24570
|
if (isOpencodeCaptain && captainPort) {
|
|
24039
24571
|
writeCaptainAddress(stateRoot, projectName, {
|
|
@@ -24049,7 +24581,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24049
24581
|
directory: realpathOrSelf(cwd),
|
|
24050
24582
|
launchedAt,
|
|
24051
24583
|
sessionId: opencodeResumeSessionId,
|
|
24052
|
-
log: (m) => console.error(
|
|
24584
|
+
log: (m) => console.error(chalk16.yellow(` \u26A0 ${m}`))
|
|
24053
24585
|
}).catch(() => {
|
|
24054
24586
|
});
|
|
24055
24587
|
} else if (role === "captain") {
|
|
@@ -24062,23 +24594,23 @@ var launchCommand = new Command14("launch").description(
|
|
|
24062
24594
|
}
|
|
24063
24595
|
});
|
|
24064
24596
|
} catch (err) {
|
|
24065
|
-
console.error(
|
|
24597
|
+
console.error(chalk16.red(` \u2718 Failed: ${err.message}`));
|
|
24066
24598
|
hadFailure = true;
|
|
24067
24599
|
}
|
|
24068
24600
|
}
|
|
24069
24601
|
if (opts.all) {
|
|
24070
24602
|
const hubPath = resolveHome(config.hubVault);
|
|
24071
|
-
|
|
24072
|
-
console.log(
|
|
24603
|
+
fs31.mkdirSync(hubPath, { recursive: true });
|
|
24604
|
+
console.log(chalk16.bold("\nLaunching all captain workspaces\n"));
|
|
24073
24605
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
24074
24606
|
const projPath = resolveHome(proj.path);
|
|
24075
24607
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24076
|
-
if (!
|
|
24608
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24077
24609
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
24078
24610
|
await ensureSpokeLayout(spokeDriver);
|
|
24079
|
-
console.log(
|
|
24611
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24080
24612
|
}
|
|
24081
|
-
console.log(
|
|
24613
|
+
console.log(chalk16.bold(`
|
|
24082
24614
|
Captain: ${proj.captainName} (${name})`));
|
|
24083
24615
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
24084
24616
|
}
|
|
@@ -24086,7 +24618,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
24086
24618
|
} else if (!project) {
|
|
24087
24619
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
24088
24620
|
console.error(
|
|
24089
|
-
|
|
24621
|
+
chalk16.red(
|
|
24090
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"
|
|
24091
24623
|
)
|
|
24092
24624
|
);
|
|
@@ -24101,22 +24633,22 @@ var launchCommand = new Command14("launch").description(
|
|
|
24101
24633
|
}));
|
|
24102
24634
|
const selected = await selectCaptainsInteractive(entries);
|
|
24103
24635
|
if (selected.length === 0) {
|
|
24104
|
-
console.log(
|
|
24636
|
+
console.log(chalk16.yellow("\n No captains selected.\n"));
|
|
24105
24637
|
return;
|
|
24106
24638
|
}
|
|
24107
|
-
console.log(
|
|
24639
|
+
console.log(chalk16.bold(`
|
|
24108
24640
|
Launching ${selected.length} captain workspace(s) in parallel
|
|
24109
24641
|
`));
|
|
24110
24642
|
await Promise.all(selected.map(async (name) => {
|
|
24111
24643
|
const proj = config.projects[name];
|
|
24112
24644
|
const projPath = resolveHome(proj.path);
|
|
24113
24645
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24114
|
-
if (!
|
|
24646
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24115
24647
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
24116
24648
|
await ensureSpokeLayout(spokeDriver);
|
|
24117
|
-
console.log(
|
|
24649
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24118
24650
|
}
|
|
24119
|
-
console.log(
|
|
24651
|
+
console.log(chalk16.bold(`
|
|
24120
24652
|
Captain: ${proj.captainName} (${name})`));
|
|
24121
24653
|
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
24122
24654
|
}));
|
|
@@ -24124,7 +24656,7 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
24124
24656
|
} else {
|
|
24125
24657
|
if (!config.projects[project]) {
|
|
24126
24658
|
console.error(
|
|
24127
|
-
|
|
24659
|
+
chalk16.red(
|
|
24128
24660
|
`
|
|
24129
24661
|
\u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
|
|
24130
24662
|
`
|
|
@@ -24135,13 +24667,13 @@ Launching ${selected.length} captain workspace(s) in parallel
|
|
|
24135
24667
|
const proj = config.projects[project];
|
|
24136
24668
|
const projPath = resolveHome(proj.path);
|
|
24137
24669
|
const spokePath = resolveHome(proj.spokeVault);
|
|
24138
|
-
if (!
|
|
24670
|
+
if (!fs31.existsSync(spokePath)) {
|
|
24139
24671
|
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(project, config);
|
|
24140
24672
|
await ensureSpokeLayout(spokeDriver);
|
|
24141
|
-
console.log(
|
|
24673
|
+
console.log(chalk16.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
24142
24674
|
}
|
|
24143
24675
|
console.log(
|
|
24144
|
-
|
|
24676
|
+
chalk16.bold(
|
|
24145
24677
|
`
|
|
24146
24678
|
Launching captain workspace for '${project}' (${proj.captainName})
|
|
24147
24679
|
`
|
|
@@ -24156,7 +24688,7 @@ Launching captain workspace for '${project}' (${proj.captainName})
|
|
|
24156
24688
|
init_dist();
|
|
24157
24689
|
init_dist4();
|
|
24158
24690
|
import { Command as Command15 } from "commander";
|
|
24159
|
-
import
|
|
24691
|
+
import chalk17 from "chalk";
|
|
24160
24692
|
init_dist();
|
|
24161
24693
|
function nameVariants(name) {
|
|
24162
24694
|
const stripped = name.replace(/^⚓\s+/, "").trim();
|
|
@@ -24169,17 +24701,17 @@ async function closeMatching(runtime, variants, label) {
|
|
|
24169
24701
|
const failed = [];
|
|
24170
24702
|
if (matches.length === 0) {
|
|
24171
24703
|
console.log(
|
|
24172
|
-
|
|
24704
|
+
chalk17.yellow(` \u26A0 Workspace '${label}' not found \u2014 already closed?`)
|
|
24173
24705
|
);
|
|
24174
24706
|
return { closed, failed };
|
|
24175
24707
|
}
|
|
24176
24708
|
for (const ws of matches) {
|
|
24177
24709
|
try {
|
|
24178
24710
|
await runtime.stop(ws.id);
|
|
24179
|
-
console.log(
|
|
24711
|
+
console.log(chalk17.green(` \u2714 Closed: ${ws.name}`));
|
|
24180
24712
|
closed.push(ws.name);
|
|
24181
24713
|
} catch {
|
|
24182
|
-
console.log(
|
|
24714
|
+
console.log(chalk17.red(` \u2718 Failed to close: ${ws.name}`));
|
|
24183
24715
|
failed.push(ws.name);
|
|
24184
24716
|
}
|
|
24185
24717
|
}
|
|
@@ -24201,11 +24733,11 @@ var shutdownCommand = new Command15("shutdown").description(
|
|
|
24201
24733
|
const allVariants = /* @__PURE__ */ new Set([...captainVariants, ...commandVariants]);
|
|
24202
24734
|
const squadrantWorkspaces = workspaces.filter((w) => allVariants.has(w.name));
|
|
24203
24735
|
if (squadrantWorkspaces.length === 0) {
|
|
24204
|
-
console.log(
|
|
24736
|
+
console.log(chalk17.yellow("\nNo squadrant workspaces found to close.\n"));
|
|
24205
24737
|
return;
|
|
24206
24738
|
}
|
|
24207
24739
|
console.log(
|
|
24208
|
-
|
|
24740
|
+
chalk17.bold(
|
|
24209
24741
|
`
|
|
24210
24742
|
Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
24211
24743
|
`
|
|
@@ -24225,9 +24757,9 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24225
24757
|
for (const ws of squadrantWorkspaces) {
|
|
24226
24758
|
try {
|
|
24227
24759
|
await globalRuntime.stop(ws.id);
|
|
24228
|
-
console.log(
|
|
24760
|
+
console.log(chalk17.green(` \u2714 Closed: ${ws.name}`));
|
|
24229
24761
|
} catch {
|
|
24230
|
-
console.log(
|
|
24762
|
+
console.log(chalk17.red(` \u2718 Failed to close: ${ws.name}`));
|
|
24231
24763
|
}
|
|
24232
24764
|
}
|
|
24233
24765
|
console.log("");
|
|
@@ -24235,7 +24767,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24235
24767
|
}
|
|
24236
24768
|
if (!config.projects[project]) {
|
|
24237
24769
|
console.error(
|
|
24238
|
-
|
|
24770
|
+
chalk17.red(
|
|
24239
24771
|
`
|
|
24240
24772
|
\u2718 Project '${project}' not found. Run 'squadrant projects list' to see registered projects.
|
|
24241
24773
|
`
|
|
@@ -24246,7 +24778,7 @@ Shutting down ${squadrantWorkspaces.length} workspace(s)...
|
|
|
24246
24778
|
const captainName = config.projects[project].captainName;
|
|
24247
24779
|
const runtime = runtimes.forProject(project, config);
|
|
24248
24780
|
console.log(
|
|
24249
|
-
|
|
24781
|
+
chalk17.bold(`
|
|
24250
24782
|
Shutting down captain workspace for '${project}'...
|
|
24251
24783
|
`)
|
|
24252
24784
|
);
|
|
@@ -24271,24 +24803,24 @@ Shutting down captain workspace for '${project}'...
|
|
|
24271
24803
|
// packages/cli/src/commands/feedback.ts
|
|
24272
24804
|
init_dist();
|
|
24273
24805
|
import { Command as Command16 } from "commander";
|
|
24274
|
-
import
|
|
24806
|
+
import fs32 from "fs";
|
|
24275
24807
|
import os19 from "os";
|
|
24276
|
-
import
|
|
24808
|
+
import path35 from "path";
|
|
24277
24809
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24278
24810
|
import { execSync as execSync12 } from "child_process";
|
|
24279
|
-
import
|
|
24811
|
+
import chalk18 from "chalk";
|
|
24280
24812
|
var REPO_URL = "https://github.com/tu11aa/squadrant";
|
|
24281
24813
|
function readPkgVersion() {
|
|
24282
24814
|
try {
|
|
24283
|
-
const pkgPath =
|
|
24284
|
-
return JSON.parse(
|
|
24815
|
+
const pkgPath = path35.join(path35.dirname(fileURLToPath3(import.meta.url)), "..", "package.json");
|
|
24816
|
+
return JSON.parse(fs32.readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
24285
24817
|
} catch {
|
|
24286
24818
|
return "unknown";
|
|
24287
24819
|
}
|
|
24288
24820
|
}
|
|
24289
24821
|
function readMetrics(metricsPath) {
|
|
24290
24822
|
try {
|
|
24291
|
-
return JSON.parse(
|
|
24823
|
+
return JSON.parse(fs32.readFileSync(metricsPath, "utf-8"));
|
|
24292
24824
|
} catch {
|
|
24293
24825
|
return {};
|
|
24294
24826
|
}
|
|
@@ -24326,19 +24858,19 @@ function buildIssueUrl(metrics, squadrantVersion) {
|
|
|
24326
24858
|
}
|
|
24327
24859
|
var feedbackCommand = new Command16("feedback").description("Open a pre-filled GitHub issue for feedback or bug reports").action(() => {
|
|
24328
24860
|
const config = loadConfig();
|
|
24329
|
-
const metricsPath = config.metrics?.path ||
|
|
24861
|
+
const metricsPath = config.metrics?.path || path35.join(os19.homedir(), ".config", "squadrant", "metrics.json");
|
|
24330
24862
|
const metrics = readMetrics(metricsPath);
|
|
24331
24863
|
const version = readStamp(config) ?? readPkgVersion();
|
|
24332
24864
|
const issueUrl = buildIssueUrl(metrics, version);
|
|
24333
|
-
console.log(
|
|
24334
|
-
console.log(
|
|
24865
|
+
console.log(chalk18.bold("\nOpening feedback issue in browser...\n"));
|
|
24866
|
+
console.log(chalk18.dim(` URL: ${issueUrl.substring(0, 80)}...
|
|
24335
24867
|
`));
|
|
24336
24868
|
try {
|
|
24337
24869
|
execSync12(`open "${issueUrl}"`, { stdio: "ignore" });
|
|
24338
|
-
console.log(
|
|
24870
|
+
console.log(chalk18.green(" \u2714 Browser opened\n"));
|
|
24339
24871
|
} catch {
|
|
24340
|
-
console.log(
|
|
24341
|
-
console.log(` Open manually: ${
|
|
24872
|
+
console.log(chalk18.yellow(" \u26A0 Could not open browser automatically."));
|
|
24873
|
+
console.log(` Open manually: ${chalk18.cyan(issueUrl)}
|
|
24342
24874
|
`);
|
|
24343
24875
|
}
|
|
24344
24876
|
});
|
|
@@ -24348,7 +24880,7 @@ init_dist();
|
|
|
24348
24880
|
init_dist();
|
|
24349
24881
|
init_dist4();
|
|
24350
24882
|
import { Command as Command17 } from "commander";
|
|
24351
|
-
import
|
|
24883
|
+
import chalk19 from "chalk";
|
|
24352
24884
|
function getDateStr(yesterday) {
|
|
24353
24885
|
return iso(daysAgo(yesterday ? 1 : 0));
|
|
24354
24886
|
}
|
|
@@ -24367,7 +24899,7 @@ function formatStandup(standups, dateStr, raw) {
|
|
|
24367
24899
|
const lines = [];
|
|
24368
24900
|
const header = `Standup \u2014 ${dateStr}`;
|
|
24369
24901
|
if (!raw) {
|
|
24370
|
-
lines.push(
|
|
24902
|
+
lines.push(chalk19.bold(`
|
|
24371
24903
|
${header}
|
|
24372
24904
|
`));
|
|
24373
24905
|
} else {
|
|
@@ -24377,12 +24909,12 @@ ${header}
|
|
|
24377
24909
|
let hasBlockers = false;
|
|
24378
24910
|
for (const s of standups) {
|
|
24379
24911
|
if (!raw) {
|
|
24380
|
-
lines.push(
|
|
24912
|
+
lines.push(chalk19.cyan.bold(`## ${s.name}`));
|
|
24381
24913
|
} else {
|
|
24382
24914
|
lines.push(`## ${s.name}`);
|
|
24383
24915
|
}
|
|
24384
24916
|
if (s.gitCommits.length > 0) {
|
|
24385
|
-
lines.push(!raw ?
|
|
24917
|
+
lines.push(!raw ? chalk19.green("Done:") : "**Done:**");
|
|
24386
24918
|
for (const commit of s.gitCommits) {
|
|
24387
24919
|
lines.push(` - ${commit}`);
|
|
24388
24920
|
}
|
|
@@ -24394,7 +24926,7 @@ ${header}
|
|
|
24394
24926
|
if (match) {
|
|
24395
24927
|
const items = match[1].trim().split("\n").filter((l) => l.trim().startsWith("-"));
|
|
24396
24928
|
if (items.length > 0 && section === "Tomorrow") {
|
|
24397
|
-
lines.push(!raw ?
|
|
24929
|
+
lines.push(!raw ? chalk19.blue("Next:") : "**Next:**");
|
|
24398
24930
|
for (const item of items) lines.push(` ${item.trim()}`);
|
|
24399
24931
|
}
|
|
24400
24932
|
}
|
|
@@ -24402,19 +24934,19 @@ ${header}
|
|
|
24402
24934
|
}
|
|
24403
24935
|
if (s.blockers.length > 0) {
|
|
24404
24936
|
hasBlockers = true;
|
|
24405
|
-
lines.push(!raw ?
|
|
24937
|
+
lines.push(!raw ? chalk19.red("Blocked:") : "**Blocked:**");
|
|
24406
24938
|
for (const b of s.blockers) {
|
|
24407
24939
|
lines.push(` - ${b}`);
|
|
24408
24940
|
}
|
|
24409
24941
|
}
|
|
24410
24942
|
if (s.gitCommits.length === 0 && !s.dailyLog) {
|
|
24411
|
-
lines.push(!raw ?
|
|
24943
|
+
lines.push(!raw ? chalk19.dim(" (no activity)") : " (no activity)");
|
|
24412
24944
|
}
|
|
24413
24945
|
lines.push("");
|
|
24414
24946
|
}
|
|
24415
24947
|
const totalCommits = standups.reduce((sum, s) => sum + s.gitCommits.length, 0);
|
|
24416
24948
|
if (!raw) {
|
|
24417
|
-
lines.push(
|
|
24949
|
+
lines.push(chalk19.dim(`--- ${totalCommits} commits${hasBlockers ? ", HAS BLOCKERS" : ""} (task tracking: no data source \u2014 #630) ---
|
|
24418
24950
|
`));
|
|
24419
24951
|
} else {
|
|
24420
24952
|
lines.push(`---
|
|
@@ -24428,7 +24960,7 @@ var standupCommand = new Command17("standup").description("Generate daily standu
|
|
|
24428
24960
|
const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
24429
24961
|
const projects = Object.entries(config.projects);
|
|
24430
24962
|
if (projects.length === 0) {
|
|
24431
|
-
console.log(
|
|
24963
|
+
console.log(chalk19.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
|
|
24432
24964
|
return;
|
|
24433
24965
|
}
|
|
24434
24966
|
const dateStr = getDateStr(!!opts.yesterday);
|
|
@@ -24437,7 +24969,7 @@ var standupCommand = new Command17("standup").description("Generate daily standu
|
|
|
24437
24969
|
if (opts.project) {
|
|
24438
24970
|
const match = projects.find(([name]) => name === opts.project);
|
|
24439
24971
|
if (!match) {
|
|
24440
|
-
console.error(
|
|
24972
|
+
console.error(chalk19.red(`Project "${opts.project}" not found.`));
|
|
24441
24973
|
process.exit(1);
|
|
24442
24974
|
}
|
|
24443
24975
|
targets = [match];
|
|
@@ -24456,7 +24988,7 @@ init_dist();
|
|
|
24456
24988
|
init_dist();
|
|
24457
24989
|
init_dist4();
|
|
24458
24990
|
import { Command as Command18 } from "commander";
|
|
24459
|
-
import
|
|
24991
|
+
import chalk20 from "chalk";
|
|
24460
24992
|
function dedupe(items) {
|
|
24461
24993
|
const seen = /* @__PURE__ */ new Set();
|
|
24462
24994
|
const out = [];
|
|
@@ -24512,7 +25044,7 @@ function formatRetro(retros, fromStr, toStr, raw) {
|
|
|
24512
25044
|
const lines = [];
|
|
24513
25045
|
const header = `Retro \u2014 ${fromStr} \u2192 ${toStr}`;
|
|
24514
25046
|
lines.push(raw ? `# ${header}
|
|
24515
|
-
` :
|
|
25047
|
+
` : chalk20.bold(`
|
|
24516
25048
|
${header}
|
|
24517
25049
|
`));
|
|
24518
25050
|
let totalCommits = 0;
|
|
@@ -24522,30 +25054,30 @@ ${header}
|
|
|
24522
25054
|
totalCommits += r.commits.length;
|
|
24523
25055
|
totalPRs += r.mergedPRs.length;
|
|
24524
25056
|
totalShipped += r.shipped.length;
|
|
24525
|
-
lines.push(raw ? `## ${r.name}` :
|
|
24526
|
-
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);
|
|
24527
25059
|
if (r.mergedPRs.length > 0) {
|
|
24528
|
-
lines.push(raw ? `**PRs merged:**` :
|
|
25060
|
+
lines.push(raw ? `**PRs merged:**` : chalk20.green("PRs merged:"));
|
|
24529
25061
|
for (const pr of r.mergedPRs) lines.push(` - ${pr}`);
|
|
24530
25062
|
}
|
|
24531
|
-
renderList(lines, r.inProgress, raw, "In Progress",
|
|
24532
|
-
renderList(lines, r.blocked, raw, "Blocked",
|
|
24533
|
-
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);
|
|
24534
25066
|
const metricBits = [
|
|
24535
25067
|
`${r.commits.length} commits`,
|
|
24536
25068
|
`${r.mergedPRs.length} PRs merged`,
|
|
24537
25069
|
`${r.shipped.length} shipped`
|
|
24538
25070
|
];
|
|
24539
|
-
lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` :
|
|
25071
|
+
lines.push(raw ? `*${metricBits.join(" \xB7 ")}*` : chalk20.dim(` ${metricBits.join(" \xB7 ")}`));
|
|
24540
25072
|
if (r.shipped.length === 0 && r.commits.length === 0 && r.mergedPRs.length === 0 && r.inProgress.length === 0 && r.blocked.length === 0) {
|
|
24541
|
-
lines.push(raw ? "_(no activity in this window)_" :
|
|
25073
|
+
lines.push(raw ? "_(no activity in this window)_" : chalk20.dim(" (no activity in this window)"));
|
|
24542
25074
|
}
|
|
24543
25075
|
lines.push("");
|
|
24544
25076
|
}
|
|
24545
25077
|
const summary = `${totalShipped} items shipped \xB7 ${totalCommits} commits \xB7 ${totalPRs} PRs merged`;
|
|
24546
25078
|
lines.push(raw ? `---
|
|
24547
25079
|
*${summary}*
|
|
24548
|
-
` :
|
|
25080
|
+
` : chalk20.dim(`--- ${summary} ---
|
|
24549
25081
|
`));
|
|
24550
25082
|
return lines.join("\n");
|
|
24551
25083
|
}
|
|
@@ -24554,7 +25086,7 @@ var retroCommand = new Command18("retro").description("Generate a retro (weekly/
|
|
|
24554
25086
|
const registry = new WorkspaceRegistry({ obsidian: createObsidianDriver });
|
|
24555
25087
|
const projects = Object.entries(config.projects);
|
|
24556
25088
|
if (projects.length === 0) {
|
|
24557
|
-
console.log(
|
|
25089
|
+
console.log(chalk20.yellow("\nNo projects registered. Use: squadrant projects add <name> <path>\n"));
|
|
24558
25090
|
return;
|
|
24559
25091
|
}
|
|
24560
25092
|
let windowDays = 7;
|
|
@@ -24571,7 +25103,7 @@ var retroCommand = new Command18("retro").description("Generate a retro (weekly/
|
|
|
24571
25103
|
if (opts.project) {
|
|
24572
25104
|
const match = projects.find(([name]) => name === opts.project);
|
|
24573
25105
|
if (!match) {
|
|
24574
|
-
console.error(
|
|
25106
|
+
console.error(chalk20.red(`Project "${opts.project}" not found.`));
|
|
24575
25107
|
process.exit(1);
|
|
24576
25108
|
}
|
|
24577
25109
|
targets = [match];
|
|
@@ -24591,7 +25123,7 @@ init_runtime2();
|
|
|
24591
25123
|
init_dist();
|
|
24592
25124
|
init_dist4();
|
|
24593
25125
|
import { Command as Command19 } from "commander";
|
|
24594
|
-
import
|
|
25126
|
+
import chalk21 from "chalk";
|
|
24595
25127
|
function buildRegistry2() {
|
|
24596
25128
|
return new WorkspaceRegistry({
|
|
24597
25129
|
obsidian: createObsidianDriver
|
|
@@ -24626,12 +25158,12 @@ workspaceCommand.command("read").description("Print the contents of a scope-rela
|
|
|
24626
25158
|
const config = loadConfig();
|
|
24627
25159
|
const registry = buildRegistry2();
|
|
24628
25160
|
try {
|
|
24629
|
-
const { projectTarget, path:
|
|
25161
|
+
const { projectTarget, path: path46 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
24630
25162
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24631
|
-
const content = await driver.read(
|
|
25163
|
+
const content = await driver.read(path46);
|
|
24632
25164
|
process.stdout.write(content);
|
|
24633
25165
|
} catch (err) {
|
|
24634
|
-
console.error(
|
|
25166
|
+
console.error(chalk21.red(err.message));
|
|
24635
25167
|
process.exit(1);
|
|
24636
25168
|
}
|
|
24637
25169
|
});
|
|
@@ -24640,28 +25172,28 @@ workspaceCommand.command("write").description("Write content to a scope-relative
|
|
|
24640
25172
|
const registry = buildRegistry2();
|
|
24641
25173
|
try {
|
|
24642
25174
|
let projectTarget;
|
|
24643
|
-
let
|
|
25175
|
+
let path46;
|
|
24644
25176
|
let rawContent;
|
|
24645
25177
|
if (opts.hub) {
|
|
24646
25178
|
if (arg3 !== void 0) {
|
|
24647
25179
|
throw new Error("With --hub, pass only the path and content");
|
|
24648
25180
|
}
|
|
24649
25181
|
projectTarget = void 0;
|
|
24650
|
-
|
|
25182
|
+
path46 = arg1;
|
|
24651
25183
|
rawContent = arg2;
|
|
24652
25184
|
} else {
|
|
24653
25185
|
if (arg3 === void 0) {
|
|
24654
25186
|
throw new Error("Missing content \u2014 usage: <project> <path> <content>");
|
|
24655
25187
|
}
|
|
24656
25188
|
projectTarget = arg1;
|
|
24657
|
-
|
|
25189
|
+
path46 = arg2;
|
|
24658
25190
|
rawContent = arg3;
|
|
24659
25191
|
}
|
|
24660
25192
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24661
25193
|
const payload = rawContent === "-" ? await readStdin() : rawContent;
|
|
24662
|
-
await driver.write(
|
|
25194
|
+
await driver.write(path46, payload);
|
|
24663
25195
|
} catch (err) {
|
|
24664
|
-
console.error(
|
|
25196
|
+
console.error(chalk21.red(err.message));
|
|
24665
25197
|
process.exit(1);
|
|
24666
25198
|
}
|
|
24667
25199
|
});
|
|
@@ -24669,12 +25201,12 @@ workspaceCommand.command("list").description("List entries in a scope-relative d
|
|
|
24669
25201
|
const config = loadConfig();
|
|
24670
25202
|
const registry = buildRegistry2();
|
|
24671
25203
|
try {
|
|
24672
|
-
const { projectTarget, path:
|
|
25204
|
+
const { projectTarget, path: path46 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
24673
25205
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24674
|
-
const entries = await driver.list(
|
|
25206
|
+
const entries = await driver.list(path46);
|
|
24675
25207
|
for (const entry of entries) console.log(entry);
|
|
24676
25208
|
} catch (err) {
|
|
24677
|
-
console.error(
|
|
25209
|
+
console.error(chalk21.red(err.message));
|
|
24678
25210
|
process.exit(1);
|
|
24679
25211
|
}
|
|
24680
25212
|
});
|
|
@@ -24682,12 +25214,12 @@ workspaceCommand.command("exists").description("Exit 0 if path exists, 1 if not"
|
|
|
24682
25214
|
const config = loadConfig();
|
|
24683
25215
|
const registry = buildRegistry2();
|
|
24684
25216
|
try {
|
|
24685
|
-
const { projectTarget, path:
|
|
25217
|
+
const { projectTarget, path: path46 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
24686
25218
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24687
|
-
const ok2 = await driver.exists(
|
|
25219
|
+
const ok2 = await driver.exists(path46);
|
|
24688
25220
|
process.exit(ok2 ? 0 : 1);
|
|
24689
25221
|
} catch (err) {
|
|
24690
|
-
console.error(
|
|
25222
|
+
console.error(chalk21.red(err.message));
|
|
24691
25223
|
process.exit(2);
|
|
24692
25224
|
}
|
|
24693
25225
|
});
|
|
@@ -24695,11 +25227,11 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
|
|
|
24695
25227
|
const config = loadConfig();
|
|
24696
25228
|
const registry = buildRegistry2();
|
|
24697
25229
|
try {
|
|
24698
|
-
const { projectTarget, path:
|
|
25230
|
+
const { projectTarget, path: path46 } = resolveTargetAndPath(arg1, arg2, !!opts.hub);
|
|
24699
25231
|
const driver = resolveDriver(registry, config, projectTarget, !!opts.hub);
|
|
24700
|
-
await driver.mkdir(
|
|
25232
|
+
await driver.mkdir(path46);
|
|
24701
25233
|
} catch (err) {
|
|
24702
|
-
console.error(
|
|
25234
|
+
console.error(chalk21.red(err.message));
|
|
24703
25235
|
process.exit(1);
|
|
24704
25236
|
}
|
|
24705
25237
|
});
|
|
@@ -24708,7 +25240,7 @@ workspaceCommand.command("mkdir").description("Recursively create a scope-relati
|
|
|
24708
25240
|
init_dist();
|
|
24709
25241
|
init_dist4();
|
|
24710
25242
|
import { Command as Command20 } from "commander";
|
|
24711
|
-
import
|
|
25243
|
+
import chalk22 from "chalk";
|
|
24712
25244
|
async function readStdin2() {
|
|
24713
25245
|
const chunks = [];
|
|
24714
25246
|
for await (const chunk of process.stdin) {
|
|
@@ -24724,7 +25256,7 @@ var notifyCommand = new Command20("notify").description("Send a message to the u
|
|
|
24724
25256
|
if (!payload) throw new Error("Empty message");
|
|
24725
25257
|
await registry.get(config).notify(payload);
|
|
24726
25258
|
} catch (err) {
|
|
24727
|
-
console.error(
|
|
25259
|
+
console.error(chalk22.red(err.message));
|
|
24728
25260
|
process.exit(1);
|
|
24729
25261
|
}
|
|
24730
25262
|
});
|
|
@@ -24735,9 +25267,9 @@ init_dist3();
|
|
|
24735
25267
|
init_dist4();
|
|
24736
25268
|
init_dist();
|
|
24737
25269
|
import { Command as Command21 } from "commander";
|
|
24738
|
-
import
|
|
24739
|
-
import
|
|
24740
|
-
import
|
|
25270
|
+
import chalk23 from "chalk";
|
|
25271
|
+
import fs33 from "fs";
|
|
25272
|
+
import path36 from "path";
|
|
24741
25273
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
24742
25274
|
function parseScope(v2) {
|
|
24743
25275
|
if (v2 !== "user" && v2 !== "project") {
|
|
@@ -24746,10 +25278,10 @@ function parseScope(v2) {
|
|
|
24746
25278
|
return v2;
|
|
24747
25279
|
}
|
|
24748
25280
|
function findPackageRoot3() {
|
|
24749
|
-
let dir =
|
|
25281
|
+
let dir = path36.dirname(fileURLToPath4(import.meta.url));
|
|
24750
25282
|
while (dir !== "/" && dir !== "") {
|
|
24751
|
-
if (
|
|
24752
|
-
dir =
|
|
25283
|
+
if (fs33.existsSync(path36.join(dir, "package.json"))) return dir;
|
|
25284
|
+
dir = path36.dirname(dir);
|
|
24753
25285
|
}
|
|
24754
25286
|
return process.cwd();
|
|
24755
25287
|
}
|
|
@@ -24783,17 +25315,17 @@ async function runEmit(opts) {
|
|
|
24783
25315
|
for (const dest of emitter.destinations(scope, projectRoot)) {
|
|
24784
25316
|
const result = await emitter.emit(source, dest, { dryRun: opts.dryRun });
|
|
24785
25317
|
if (opts.dryRun) {
|
|
24786
|
-
console.log(
|
|
25318
|
+
console.log(chalk23.cyan(`[${emitter.name}] ${dest.path}`));
|
|
24787
25319
|
console.log(result.diff ?? "(no diff)");
|
|
24788
25320
|
} else if (result.written) {
|
|
24789
25321
|
console.log(
|
|
24790
|
-
|
|
25322
|
+
chalk23.green(
|
|
24791
25323
|
`\u2714 ${emitter.name} \u2192 ${dest.path} (${result.bytesWritten} bytes)`
|
|
24792
25324
|
)
|
|
24793
25325
|
);
|
|
24794
25326
|
emittedCount.written++;
|
|
24795
25327
|
} else {
|
|
24796
|
-
console.log(
|
|
25328
|
+
console.log(chalk23.gray(`- ${emitter.name} \u2192 ${dest.path} (skipped)`));
|
|
24797
25329
|
emittedCount.skipped++;
|
|
24798
25330
|
}
|
|
24799
25331
|
}
|
|
@@ -24816,14 +25348,14 @@ async function runEmit(opts) {
|
|
|
24816
25348
|
`Unknown project '${projectName}'. Available: ${Object.keys(cfg.projects).join(", ") || "(none)"}`
|
|
24817
25349
|
);
|
|
24818
25350
|
}
|
|
24819
|
-
console.error(
|
|
25351
|
+
console.error(chalk23.yellow(`\u26A0 unknown project: ${projectName}`));
|
|
24820
25352
|
continue;
|
|
24821
25353
|
}
|
|
24822
25354
|
const source = await readProjectLevelSource(
|
|
24823
25355
|
createObsidianDriver({ root: proj.path })
|
|
24824
25356
|
);
|
|
24825
25357
|
if (!source) {
|
|
24826
|
-
console.log(
|
|
25358
|
+
console.log(chalk23.gray(`- ${projectName}: no AGENTS.md, skipping`));
|
|
24827
25359
|
continue;
|
|
24828
25360
|
}
|
|
24829
25361
|
for (const name of targets) {
|
|
@@ -24833,7 +25365,7 @@ async function runEmit(opts) {
|
|
|
24833
25365
|
}
|
|
24834
25366
|
if (!opts.dryRun) {
|
|
24835
25367
|
console.log(
|
|
24836
|
-
|
|
25368
|
+
chalk23.bold(
|
|
24837
25369
|
`
|
|
24838
25370
|
Projection complete \u2014 ${emittedCount.written} written, ${emittedCount.skipped} skipped.`
|
|
24839
25371
|
)
|
|
@@ -24847,7 +25379,7 @@ projectionCommand.command("emit").description("Emit projections to disk").option
|
|
|
24847
25379
|
try {
|
|
24848
25380
|
await runEmit({ ...opts, dryRun: false });
|
|
24849
25381
|
} catch (err) {
|
|
24850
|
-
console.error(
|
|
25382
|
+
console.error(chalk23.red(err.message));
|
|
24851
25383
|
process.exit(1);
|
|
24852
25384
|
}
|
|
24853
25385
|
});
|
|
@@ -24855,7 +25387,7 @@ projectionCommand.command("diff").description("Preview changes without writing")
|
|
|
24855
25387
|
try {
|
|
24856
25388
|
await runEmit({ ...opts, dryRun: true });
|
|
24857
25389
|
} catch (err) {
|
|
24858
|
-
console.error(
|
|
25390
|
+
console.error(chalk23.red(err.message));
|
|
24859
25391
|
process.exit(1);
|
|
24860
25392
|
}
|
|
24861
25393
|
});
|
|
@@ -24865,7 +25397,7 @@ projectionCommand.command("list").description("List registered projection target
|
|
|
24865
25397
|
const emitter = registry.get(name);
|
|
24866
25398
|
const userDests = emitter.destinations("user").map((d) => d.path);
|
|
24867
25399
|
const projectDests = emitter.destinations("project", "<project>").map((d) => d.path);
|
|
24868
|
-
console.log(
|
|
25400
|
+
console.log(chalk23.bold(name));
|
|
24869
25401
|
console.log(` user: ${userDests.join(", ") || "(none)"}`);
|
|
24870
25402
|
console.log(` project: ${projectDests.join(", ") || "(none)"}`);
|
|
24871
25403
|
}
|
|
@@ -24945,12 +25477,12 @@ init_dist();
|
|
|
24945
25477
|
init_dist();
|
|
24946
25478
|
init_dist2();
|
|
24947
25479
|
import { Command as Command23 } from "commander";
|
|
24948
|
-
import
|
|
25480
|
+
import fs34 from "fs";
|
|
24949
25481
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
24950
|
-
import { dirname as dirname8, join as
|
|
24951
|
-
import
|
|
25482
|
+
import { dirname as dirname8, join as join31 } from "path";
|
|
25483
|
+
import chalk24 from "chalk";
|
|
24952
25484
|
function runConfigCheck(opts) {
|
|
24953
|
-
const raw = JSON.parse(
|
|
25485
|
+
const raw = JSON.parse(fs34.readFileSync(opts.configPath, "utf-8"));
|
|
24954
25486
|
const def = getDefaultConfig();
|
|
24955
25487
|
const items = detectDrift(raw, def);
|
|
24956
25488
|
let working = raw;
|
|
@@ -25003,14 +25535,14 @@ function runConfigSet(key, value, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
25003
25535
|
}
|
|
25004
25536
|
function printRestartOutcome(outcome) {
|
|
25005
25537
|
if (outcome === "skipped-not-running") {
|
|
25006
|
-
console.log(
|
|
25538
|
+
console.log(chalk24.dim("(daemon not running \u2014 change applies on next start)"));
|
|
25007
25539
|
} else if (outcome === "skipped-opt-out") {
|
|
25008
|
-
console.log(
|
|
25540
|
+
console.log(chalk24.dim("(run 'squadrant heal daemon' to apply)"));
|
|
25009
25541
|
}
|
|
25010
25542
|
}
|
|
25011
25543
|
function runConfigSetAction(opts) {
|
|
25012
25544
|
runConfigSet(opts.key, opts.value, opts.configPath);
|
|
25013
|
-
console.log(
|
|
25545
|
+
console.log(chalk24.green(`\u2714 set ${opts.key} = ${opts.value}`));
|
|
25014
25546
|
if (isDaemonCachedKey(opts.key)) {
|
|
25015
25547
|
const doRestart = opts.doRestart ?? restartDaemonIfRunning;
|
|
25016
25548
|
const outcome = doRestart({ reason: `config ${opts.key}`, noRestart: opts.noRestart });
|
|
@@ -25018,9 +25550,9 @@ function runConfigSetAction(opts) {
|
|
|
25018
25550
|
}
|
|
25019
25551
|
}
|
|
25020
25552
|
var SEV_COLOR = {
|
|
25021
|
-
info:
|
|
25022
|
-
advisory:
|
|
25023
|
-
warn:
|
|
25553
|
+
info: chalk24.green,
|
|
25554
|
+
advisory: chalk24.yellow,
|
|
25555
|
+
warn: chalk24.red
|
|
25024
25556
|
};
|
|
25025
25557
|
var KIND_GLYPH = {
|
|
25026
25558
|
missing: "+",
|
|
@@ -25032,14 +25564,14 @@ function printItems(items) {
|
|
|
25032
25564
|
for (const i of items) {
|
|
25033
25565
|
const color = SEV_COLOR[i.severity] ?? ((s) => s);
|
|
25034
25566
|
const detail = i.note ? ` (${i.note})` : i.suggested !== void 0 ? ` \u2192 ${JSON.stringify(i.suggested)}` : "";
|
|
25035
|
-
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));
|
|
25036
25568
|
}
|
|
25037
25569
|
}
|
|
25038
25570
|
var configCommand = new Command23("config").description("Inspect and reconcile squadrant config");
|
|
25039
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) => {
|
|
25040
25572
|
const pkgVersion = readPkgVersion2();
|
|
25041
|
-
if (!
|
|
25042
|
-
console.log(
|
|
25573
|
+
if (!fs34.existsSync(DEFAULT_CONFIG_PATH)) {
|
|
25574
|
+
console.log(chalk24.yellow("No config found \u2014 run `squadrant init` first."));
|
|
25043
25575
|
return;
|
|
25044
25576
|
}
|
|
25045
25577
|
const res = runConfigCheck({ configPath: DEFAULT_CONFIG_PATH, pkgVersion, fix: opts.fix, accept: opts.accept });
|
|
@@ -25048,21 +25580,21 @@ configCommand.command("check").description("Detect config drift vs the current d
|
|
|
25048
25580
|
return;
|
|
25049
25581
|
}
|
|
25050
25582
|
if (res.items.length === 0) {
|
|
25051
|
-
console.log(
|
|
25583
|
+
console.log(chalk24.green("\u2714 Config is in sync with the current schema."));
|
|
25052
25584
|
return;
|
|
25053
25585
|
}
|
|
25054
|
-
console.log(
|
|
25586
|
+
console.log(chalk24.bold("\nConfig drift:\n"));
|
|
25055
25587
|
printItems(res.items);
|
|
25056
25588
|
if (opts.fix && res.applied.length) {
|
|
25057
|
-
console.log(
|
|
25589
|
+
console.log(chalk24.green(`
|
|
25058
25590
|
\u2714 Applied ${res.applied.length} safe item(s): ${res.applied.join(", ")}`));
|
|
25059
25591
|
}
|
|
25060
25592
|
const judgment = res.remaining.filter((i) => i.kind === "changed-default" || i.kind === "invalid");
|
|
25061
25593
|
if (judgment.length) {
|
|
25062
|
-
console.log(
|
|
25594
|
+
console.log(chalk24.yellow(`
|
|
25063
25595
|
${judgment.length} item(s) need review \u2014 run the config-doctor skill, or \`squadrant config check --accept\` to keep your values.`));
|
|
25064
25596
|
} else if (res.stamped) {
|
|
25065
|
-
console.log(
|
|
25597
|
+
console.log(chalk24.green("\n\u2714 Config reconciled and stamped."));
|
|
25066
25598
|
}
|
|
25067
25599
|
});
|
|
25068
25600
|
configCommand.command("get").description("Read a config value by dotted key (e.g. defaults.effort)").argument("<key>", "dotted config key").action((key) => {
|
|
@@ -25070,7 +25602,7 @@ configCommand.command("get").description("Read a config value by dotted key (e.g
|
|
|
25070
25602
|
const value = runConfigGet(key);
|
|
25071
25603
|
console.log(typeof value === "string" ? value : JSON.stringify(value));
|
|
25072
25604
|
} catch (e) {
|
|
25073
|
-
console.error(
|
|
25605
|
+
console.error(chalk24.red(e.message));
|
|
25074
25606
|
process.exit(1);
|
|
25075
25607
|
}
|
|
25076
25608
|
});
|
|
@@ -25078,22 +25610,22 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
25078
25610
|
try {
|
|
25079
25611
|
runConfigSetAction({ key, value, noRestart: opts.restart === false });
|
|
25080
25612
|
} catch (e) {
|
|
25081
|
-
console.error(
|
|
25613
|
+
console.error(chalk24.red(e.message));
|
|
25082
25614
|
process.exit(1);
|
|
25083
25615
|
}
|
|
25084
25616
|
});
|
|
25085
25617
|
function readPkgVersion2() {
|
|
25086
|
-
const pkgPath =
|
|
25087
|
-
return JSON.parse(
|
|
25618
|
+
const pkgPath = join31(dirname8(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
25619
|
+
return JSON.parse(fs34.readFileSync(pkgPath, "utf-8")).version;
|
|
25088
25620
|
}
|
|
25089
25621
|
|
|
25090
25622
|
// packages/cli/src/commands/heal.ts
|
|
25091
25623
|
init_dist();
|
|
25092
25624
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
25093
|
-
import { join as
|
|
25625
|
+
import { join as join32 } from "path";
|
|
25094
25626
|
import { homedir as homedir17 } from "os";
|
|
25095
25627
|
import { Command as Command24 } from "commander";
|
|
25096
|
-
import
|
|
25628
|
+
import chalk25 from "chalk";
|
|
25097
25629
|
init_dist2();
|
|
25098
25630
|
init_dist2();
|
|
25099
25631
|
init_dist4();
|
|
@@ -25151,11 +25683,11 @@ async function runHealStatus(opts) {
|
|
|
25151
25683
|
return result.healthy ? 0 : 2;
|
|
25152
25684
|
}
|
|
25153
25685
|
if (result.healthy) {
|
|
25154
|
-
stdout.write(
|
|
25686
|
+
stdout.write(chalk25.green("\u2714 all components healthy\n"));
|
|
25155
25687
|
for (const c of result.components) {
|
|
25156
25688
|
if (c.kind === "delivery" && c.state === "stale") {
|
|
25157
|
-
const glyph =
|
|
25158
|
-
const stateColor2 =
|
|
25689
|
+
const glyph = chalk25.yellow("\u2022");
|
|
25690
|
+
const stateColor2 = chalk25.yellow;
|
|
25159
25691
|
stdout.write(` ${glyph} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${stateColor2(c.state.padEnd(8))} ${c.project}
|
|
25160
25692
|
`);
|
|
25161
25693
|
if (c.detail) {
|
|
@@ -25166,13 +25698,13 @@ async function runHealStatus(opts) {
|
|
|
25166
25698
|
}
|
|
25167
25699
|
return 0;
|
|
25168
25700
|
}
|
|
25169
|
-
stdout.write(
|
|
25701
|
+
stdout.write(chalk25.bold("Unhealthy components:\n\n"));
|
|
25170
25702
|
for (const c of result.components) {
|
|
25171
25703
|
const isUnhealthy = c.kind === "delivery" ? c.stuck === true : c.healCmd !== null;
|
|
25172
25704
|
const isAdvisoryDelivery = c.kind === "delivery" && c.state === "stale" && c.stuck !== true;
|
|
25173
25705
|
if (isUnhealthy || isAdvisoryDelivery) {
|
|
25174
|
-
const glyph = c.state === "stale" || isAdvisoryDelivery ?
|
|
25175
|
-
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;
|
|
25176
25708
|
stdout.write(` ${glyph} ${c.kind.padEnd(8)} ${c.ref.padEnd(16)} ${stateColor2(c.state.padEnd(8))} ${c.project}
|
|
25177
25709
|
`);
|
|
25178
25710
|
if (c.detail) {
|
|
@@ -25180,7 +25712,7 @@ async function runHealStatus(opts) {
|
|
|
25180
25712
|
`);
|
|
25181
25713
|
}
|
|
25182
25714
|
if (c.healCmd) {
|
|
25183
|
-
stdout.write(` heal: ${
|
|
25715
|
+
stdout.write(` heal: ${chalk25.cyan(c.healCmd)}
|
|
25184
25716
|
`);
|
|
25185
25717
|
}
|
|
25186
25718
|
}
|
|
@@ -25193,7 +25725,7 @@ async function runHealDaemon(opts) {
|
|
|
25193
25725
|
try {
|
|
25194
25726
|
const result = opts.ensureDaemon();
|
|
25195
25727
|
const noteSuffix = result?.note ? ` (${result.note})` : "";
|
|
25196
|
-
stdout.write(
|
|
25728
|
+
stdout.write(chalk25.green(`\u2714 daemon kickstart complete${noteSuffix}
|
|
25197
25729
|
`));
|
|
25198
25730
|
return 0;
|
|
25199
25731
|
} catch (e) {
|
|
@@ -25248,12 +25780,12 @@ async function runHealCaptain(projects, deps) {
|
|
|
25248
25780
|
for (const project of projects) {
|
|
25249
25781
|
const resolved = resolveLiveCaptain(records, project, deps.isPidAlive);
|
|
25250
25782
|
if (!resolved) {
|
|
25251
|
-
stdout.write(` ${
|
|
25783
|
+
stdout.write(` ${chalk25.yellow("\u2013")} ${project}: no live captain found \u2014 nothing to heal
|
|
25252
25784
|
`);
|
|
25253
25785
|
continue;
|
|
25254
25786
|
}
|
|
25255
25787
|
if (entryMatchesResolved(deps.getEntry(project), resolved)) {
|
|
25256
|
-
stdout.write(` ${
|
|
25788
|
+
stdout.write(` ${chalk25.green("\u2714")} ${project}: already healthy (pid=${resolved.pid})
|
|
25257
25789
|
`);
|
|
25258
25790
|
continue;
|
|
25259
25791
|
}
|
|
@@ -25283,16 +25815,16 @@ async function runHealCaptain(projects, deps) {
|
|
|
25283
25815
|
for (const { project, resolved } of targets) {
|
|
25284
25816
|
if (stillMissingSet.has(project)) {
|
|
25285
25817
|
stderr.write(
|
|
25286
|
-
` ${
|
|
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
|
|
25287
25819
|
`
|
|
25288
25820
|
);
|
|
25289
25821
|
} else {
|
|
25290
|
-
stdout.write(` ${
|
|
25822
|
+
stdout.write(` ${chalk25.green("\u2714")} ${project}: re-adopted captain pid=${resolved.pid} sessionId=${resolved.sessionId}
|
|
25291
25823
|
`);
|
|
25292
25824
|
}
|
|
25293
25825
|
}
|
|
25294
25826
|
if (stillMissingSet.size > 0) return 1;
|
|
25295
|
-
stdout.write(
|
|
25827
|
+
stdout.write(chalk25.green(`\u2714 healed ${targets.length} captain(s); daemon kickstarted
|
|
25296
25828
|
`));
|
|
25297
25829
|
return 0;
|
|
25298
25830
|
}
|
|
@@ -25330,8 +25862,8 @@ var healCommand = new Command24("heal").description("Targeted, idempotent remedi
|
|
|
25330
25862
|
}
|
|
25331
25863
|
const config = loadConfig();
|
|
25332
25864
|
const projects = opts.all ? Object.keys(config.projects) : [project];
|
|
25333
|
-
const stateRoot =
|
|
25334
|
-
const registry = new LivenessRegistry({ path:
|
|
25865
|
+
const stateRoot = join32(homedir17(), ".config", "squadrant", "state");
|
|
25866
|
+
const registry = new LivenessRegistry({ path: join32(stateRoot, "liveness.json") });
|
|
25335
25867
|
registry.load();
|
|
25336
25868
|
const code = await runHealCaptain(projects, {
|
|
25337
25869
|
liveness: readCmuxLiveness,
|
|
@@ -25357,14 +25889,14 @@ var healCommand = new Command24("heal").description("Targeted, idempotent remedi
|
|
|
25357
25889
|
|
|
25358
25890
|
// packages/cli/src/commands/group.ts
|
|
25359
25891
|
import { Command as Command26 } from "commander";
|
|
25360
|
-
import
|
|
25892
|
+
import chalk27 from "chalk";
|
|
25361
25893
|
|
|
25362
25894
|
// packages/cli/src/commands/dispatch.ts
|
|
25363
25895
|
init_dist();
|
|
25364
25896
|
init_dist2();
|
|
25365
25897
|
import { Command as Command25 } from "commander";
|
|
25366
25898
|
import { execSync as execSync13 } from "child_process";
|
|
25367
|
-
import
|
|
25899
|
+
import chalk26 from "chalk";
|
|
25368
25900
|
async function runDispatch(toProject, task, opts) {
|
|
25369
25901
|
const fromProject = resolveCurrentProject(loadConfig());
|
|
25370
25902
|
if (!fromProject) {
|
|
@@ -25389,11 +25921,11 @@ async function runDispatch(toProject, task, opts) {
|
|
|
25389
25921
|
async function dispatchAction(toProject, task, opts) {
|
|
25390
25922
|
try {
|
|
25391
25923
|
const result = await runDispatch(toProject, task, opts);
|
|
25392
|
-
console.log(
|
|
25393
|
-
console.log(
|
|
25394
|
-
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)."));
|
|
25395
25927
|
} catch (e) {
|
|
25396
|
-
console.error(
|
|
25928
|
+
console.error(chalk26.red(`\u2718 ${e.message}`));
|
|
25397
25929
|
process.exit(1);
|
|
25398
25930
|
}
|
|
25399
25931
|
}
|
|
@@ -25403,7 +25935,7 @@ var dispatchCommand = new Command25("dispatch").description("Dispatch a task to
|
|
|
25403
25935
|
init_dist2();
|
|
25404
25936
|
var groupCommand = new Command26("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
|
|
25405
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) => {
|
|
25406
|
-
console.error(
|
|
25938
|
+
console.error(chalk27.yellow(
|
|
25407
25939
|
`\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
|
|
25408
25940
|
));
|
|
25409
25941
|
await dispatchAction(toProject, task, opts);
|
|
@@ -25419,101 +25951,6 @@ import { join as join33, dirname as dirname9 } from "path";
|
|
|
25419
25951
|
import { homedir as homedir18 } from "os";
|
|
25420
25952
|
import { Command as Command27 } from "commander";
|
|
25421
25953
|
import chalk28 from "chalk";
|
|
25422
|
-
|
|
25423
|
-
// packages/cli/src/lib/captain-channel-factory.ts
|
|
25424
|
-
init_dist3();
|
|
25425
|
-
init_dist2();
|
|
25426
|
-
import { createServer as createServer6, connect as netConnect2 } from "net";
|
|
25427
|
-
import fs33 from "fs";
|
|
25428
|
-
import { join as join32 } from "path";
|
|
25429
|
-
import { randomUUID as randomUUID7 } from "crypto";
|
|
25430
|
-
import chalk27 from "chalk";
|
|
25431
|
-
var shared;
|
|
25432
|
-
var registryEntryPath = () => join32(CLAUDE_SESSIONS_DIR, `${process.pid}.json`);
|
|
25433
|
-
function unregisterSenderIdentity() {
|
|
25434
|
-
try {
|
|
25435
|
-
fs33.unlinkSync(registryEntryPath());
|
|
25436
|
-
} catch {
|
|
25437
|
-
}
|
|
25438
|
-
}
|
|
25439
|
-
function registerSenderIdentity(socketPath2) {
|
|
25440
|
-
try {
|
|
25441
|
-
unregisterSenderIdentity();
|
|
25442
|
-
fs33.mkdirSync(CLAUDE_SESSIONS_DIR, { recursive: true });
|
|
25443
|
-
fs33.writeFileSync(
|
|
25444
|
-
registryEntryPath(),
|
|
25445
|
-
JSON.stringify({
|
|
25446
|
-
pid: process.pid,
|
|
25447
|
-
sessionId: randomUUID7(),
|
|
25448
|
-
name: "squadrantd",
|
|
25449
|
-
messagingSocketPath: socketPath2,
|
|
25450
|
-
kind: "daemon",
|
|
25451
|
-
peerProtocol: 1
|
|
25452
|
-
})
|
|
25453
|
-
);
|
|
25454
|
-
process.on("exit", unregisterSenderIdentity);
|
|
25455
|
-
} catch {
|
|
25456
|
-
}
|
|
25457
|
-
}
|
|
25458
|
-
async function sharedReceiptListener() {
|
|
25459
|
-
if (shared) return shared;
|
|
25460
|
-
ensureSocksDir();
|
|
25461
|
-
const socketPath2 = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
|
|
25462
|
-
const listener = new ClaudeReceiptListener({
|
|
25463
|
-
socketPath: socketPath2,
|
|
25464
|
-
createServer: (h) => createServer6(h),
|
|
25465
|
-
// A UDS path is not cleaned up when a process is killed, so our own leftover
|
|
25466
|
-
// must never be the reason we refuse to start.
|
|
25467
|
-
unlinkStale: (p) => {
|
|
25468
|
-
try {
|
|
25469
|
-
fs33.unlinkSync(p);
|
|
25470
|
-
} catch {
|
|
25471
|
-
}
|
|
25472
|
-
},
|
|
25473
|
-
log: (m) => console.error(chalk27.dim(m))
|
|
25474
|
-
});
|
|
25475
|
-
await listener.start();
|
|
25476
|
-
registerSenderIdentity(socketPath2);
|
|
25477
|
-
shared = listener;
|
|
25478
|
-
return shared;
|
|
25479
|
-
}
|
|
25480
|
-
function captainSessionIdFor(taskId) {
|
|
25481
|
-
return readClaudeStatusBySocketPath(captainSocketPath(taskId))?.sessionId;
|
|
25482
|
-
}
|
|
25483
|
-
async function buildCaptainChannel() {
|
|
25484
|
-
const receipts = await sharedReceiptListener();
|
|
25485
|
-
return new ClaudePeerChannel({
|
|
25486
|
-
// The port's taskId IS the project name for captains.
|
|
25487
|
-
socketPathFor: (taskId) => captainSocketPath(taskId),
|
|
25488
|
-
sessionIdFor: captainSessionIdFor,
|
|
25489
|
-
// Captains are identified in the registry by their cwd (the project path).
|
|
25490
|
-
// Resolve by the socket path squadrant chose at launch, NOT by cwd: a captain
|
|
25491
|
-
// in /tmp/x registers cwd "/private/tmp/x" on macOS, so cwd matching silently
|
|
25492
|
-
// missed and every captain ping reported accepted-unconfirmed.
|
|
25493
|
-
statusFor: (taskId) => readClaudeStatusBySocketPath(captainSocketPath(taskId)),
|
|
25494
|
-
wire: (p, e) => writeLine(p, e, { connect: (path45) => netConnect2(path45) }),
|
|
25495
|
-
receipts,
|
|
25496
|
-
newMsgId: () => randomUUID7(),
|
|
25497
|
-
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
25498
|
-
log: (m) => console.error(chalk27.dim(m))
|
|
25499
|
-
});
|
|
25500
|
-
}
|
|
25501
|
-
async function buildCaptainChannels(opts) {
|
|
25502
|
-
const claude = await buildCaptainChannel();
|
|
25503
|
-
const opencode = new OpencodeHttpChannel({
|
|
25504
|
-
portFor: (project) => readCaptainAddress(opts.stateRoot, project)?.port,
|
|
25505
|
-
sessionFor: (project) => readCaptainAddress(opts.stateRoot, project)?.sessionId
|
|
25506
|
-
});
|
|
25507
|
-
return {
|
|
25508
|
-
channels: { claude, opencode },
|
|
25509
|
-
agentFor: (project) => resolveCaptainAgent(opts.stateRoot, project, opts.configAgent)
|
|
25510
|
-
};
|
|
25511
|
-
}
|
|
25512
|
-
function resolveCaptainAgent(stateRoot, project, configAgent) {
|
|
25513
|
-
return readCaptainAddress(stateRoot, project)?.agent ?? configAgent;
|
|
25514
|
-
}
|
|
25515
|
-
|
|
25516
|
-
// packages/cli/src/commands/ping.ts
|
|
25517
25954
|
function formatPingResult(project, outcome) {
|
|
25518
25955
|
if (!outcome) return `\u2714 Pinged '${project}'`;
|
|
25519
25956
|
switch (outcome.status) {
|
|
@@ -25624,8 +26061,8 @@ var cmuxCommand = new Command28("cmux").description("cmux integration helpers").
|
|
|
25624
26061
|
// packages/cli/src/commands/effort.ts
|
|
25625
26062
|
init_dist();
|
|
25626
26063
|
init_dist2();
|
|
25627
|
-
import
|
|
25628
|
-
import
|
|
26064
|
+
import fs35 from "fs";
|
|
26065
|
+
import path37 from "path";
|
|
25629
26066
|
import { Command as Command29 } from "commander";
|
|
25630
26067
|
import chalk30 from "chalk";
|
|
25631
26068
|
var VALID_EFFORTS = ["max", "balance", "low"];
|
|
@@ -25668,9 +26105,9 @@ function effortScopeLabel(projectName) {
|
|
|
25668
26105
|
}
|
|
25669
26106
|
function canonical(p) {
|
|
25670
26107
|
try {
|
|
25671
|
-
return
|
|
26108
|
+
return fs35.realpathSync(p);
|
|
25672
26109
|
} catch {
|
|
25673
|
-
return
|
|
26110
|
+
return path37.resolve(p);
|
|
25674
26111
|
}
|
|
25675
26112
|
}
|
|
25676
26113
|
async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(), append, scopeProject, projectConfigRoot) {
|
|
@@ -25720,7 +26157,7 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
|
|
|
25720
26157
|
const config = loadConfig();
|
|
25721
26158
|
const registry = new RuntimeRegistry2({ cmux: createCmuxDriver2() });
|
|
25722
26159
|
const driver = registry.global(config);
|
|
25723
|
-
const stateRoot =
|
|
26160
|
+
const stateRoot = path37.join(path37.dirname(DEFAULT_CONFIG_PATH), "state");
|
|
25724
26161
|
const append = (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
|
|
25725
26162
|
await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append, options.project);
|
|
25726
26163
|
} catch {
|
|
@@ -25730,19 +26167,19 @@ var effortCommand = new Command29("effort").description("Get or set the crew tok
|
|
|
25730
26167
|
|
|
25731
26168
|
// packages/cli/src/commands/tokens.ts
|
|
25732
26169
|
init_dist();
|
|
25733
|
-
import
|
|
25734
|
-
import
|
|
26170
|
+
import fs36 from "fs";
|
|
26171
|
+
import path38 from "path";
|
|
25735
26172
|
import os20 from "os";
|
|
25736
26173
|
import readline3 from "readline";
|
|
25737
26174
|
import { Command as Command30 } from "commander";
|
|
25738
26175
|
import chalk31 from "chalk";
|
|
25739
|
-
var CLAUDE_PROJECTS_DIR =
|
|
26176
|
+
var CLAUDE_PROJECTS_DIR = path38.join(os20.homedir(), ".claude", "projects");
|
|
25740
26177
|
function parseTranscriptLine(rawLine) {
|
|
25741
|
-
const
|
|
25742
|
-
if (!
|
|
26178
|
+
const line2 = rawLine.trim();
|
|
26179
|
+
if (!line2) return { timestamp: null, usage: null };
|
|
25743
26180
|
let obj;
|
|
25744
26181
|
try {
|
|
25745
|
-
obj = JSON.parse(
|
|
26182
|
+
obj = JSON.parse(line2);
|
|
25746
26183
|
} catch {
|
|
25747
26184
|
return { timestamp: null, usage: null };
|
|
25748
26185
|
}
|
|
@@ -25786,9 +26223,9 @@ function foldTranscriptLine(agg, rawLine, state) {
|
|
|
25786
26223
|
async function aggregateTranscriptFile(filePath) {
|
|
25787
26224
|
const agg = emptySessionAggregate();
|
|
25788
26225
|
const state = { lastCacheRead: null };
|
|
25789
|
-
const rl = readline3.createInterface({ input:
|
|
25790
|
-
for await (const
|
|
25791
|
-
foldTranscriptLine(agg,
|
|
26226
|
+
const rl = readline3.createInterface({ input: fs36.createReadStream(filePath), crlfDelay: Infinity });
|
|
26227
|
+
for await (const line2 of rl) {
|
|
26228
|
+
foldTranscriptLine(agg, line2, state);
|
|
25792
26229
|
}
|
|
25793
26230
|
return agg;
|
|
25794
26231
|
}
|
|
@@ -25854,22 +26291,22 @@ function buildRoleReport(role, sessions) {
|
|
|
25854
26291
|
}
|
|
25855
26292
|
async function readdirSafe(dir) {
|
|
25856
26293
|
try {
|
|
25857
|
-
return await
|
|
26294
|
+
return await fs36.promises.readdir(dir);
|
|
25858
26295
|
} catch {
|
|
25859
26296
|
return [];
|
|
25860
26297
|
}
|
|
25861
26298
|
}
|
|
25862
26299
|
async function listJsonlFiles(dir) {
|
|
25863
26300
|
const entries = await readdirSafe(dir);
|
|
25864
|
-
return entries.filter((e) => e.endsWith(".jsonl")).map((e) =>
|
|
26301
|
+
return entries.filter((e) => e.endsWith(".jsonl")).map((e) => path38.join(dir, e));
|
|
25865
26302
|
}
|
|
25866
26303
|
async function findTranscriptDirs(claudeProjectsDir, captainSlug) {
|
|
25867
26304
|
const entries = await readdirSafe(claudeProjectsDir);
|
|
25868
26305
|
const captainDirs = [];
|
|
25869
26306
|
const crewDirs = [];
|
|
25870
26307
|
for (const entry of entries) {
|
|
25871
|
-
if (entry === captainSlug) captainDirs.push(
|
|
25872
|
-
else if (isCrewDirName(entry, captainSlug)) crewDirs.push(
|
|
26308
|
+
if (entry === captainSlug) captainDirs.push(path38.join(claudeProjectsDir, entry));
|
|
26309
|
+
else if (isCrewDirName(entry, captainSlug)) crewDirs.push(path38.join(claudeProjectsDir, entry));
|
|
25873
26310
|
}
|
|
25874
26311
|
return { captainDirs, crewDirs };
|
|
25875
26312
|
}
|
|
@@ -26327,7 +26764,7 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
26327
26764
|
const { createInterface: createInterface2 } = await import("readline");
|
|
26328
26765
|
const lines = [];
|
|
26329
26766
|
const rl = createInterface2({ input: process.stdin });
|
|
26330
|
-
for await (const
|
|
26767
|
+
for await (const line2 of rl) lines.push(line2);
|
|
26331
26768
|
message = lines.join("\n").trimEnd();
|
|
26332
26769
|
if (!message) {
|
|
26333
26770
|
console.error(chalk32.red("no message provided (stdin was empty)"));
|
|
@@ -26352,11 +26789,12 @@ init_dist2();
|
|
|
26352
26789
|
init_dist3();
|
|
26353
26790
|
init_dist();
|
|
26354
26791
|
import { Command as Command33 } from "commander";
|
|
26792
|
+
import { join as join36 } from "path";
|
|
26355
26793
|
import { homedir as homedir20 } from "os";
|
|
26356
26794
|
|
|
26357
26795
|
// packages/cli/src/lib/captain-session-registry.ts
|
|
26358
|
-
import
|
|
26359
|
-
import
|
|
26796
|
+
import fs37 from "fs";
|
|
26797
|
+
import path39 from "path";
|
|
26360
26798
|
|
|
26361
26799
|
// packages/cli/src/lib/handoff-facts.ts
|
|
26362
26800
|
var STALE_FETCH_WARNING_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -26407,18 +26845,18 @@ function assembleHandoffFacts(live, claudeMem, gapSessions, checkpoint, now, ext
|
|
|
26407
26845
|
// packages/cli/src/lib/captain-session-registry.ts
|
|
26408
26846
|
var CAPTAIN_SESSION_REGISTRY_FILE = "captain-sessions.jsonl";
|
|
26409
26847
|
function appendCaptainSession(spokeVault, record) {
|
|
26410
|
-
|
|
26411
|
-
const file =
|
|
26412
|
-
|
|
26848
|
+
fs37.mkdirSync(spokeVault, { recursive: true });
|
|
26849
|
+
const file = path39.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
|
|
26850
|
+
fs37.appendFileSync(file, JSON.stringify(record) + "\n");
|
|
26413
26851
|
}
|
|
26414
26852
|
function readCaptainSessionRegistry(spokeVault) {
|
|
26415
|
-
const file =
|
|
26416
|
-
if (!
|
|
26853
|
+
const file = path39.join(spokeVault, CAPTAIN_SESSION_REGISTRY_FILE);
|
|
26854
|
+
if (!fs37.existsSync(file)) return [];
|
|
26417
26855
|
const records = [];
|
|
26418
|
-
for (const
|
|
26419
|
-
if (!
|
|
26856
|
+
for (const line2 of fs37.readFileSync(file, "utf-8").split("\n")) {
|
|
26857
|
+
if (!line2.trim()) continue;
|
|
26420
26858
|
try {
|
|
26421
|
-
records.push(JSON.parse(
|
|
26859
|
+
records.push(JSON.parse(line2));
|
|
26422
26860
|
} catch {
|
|
26423
26861
|
}
|
|
26424
26862
|
}
|
|
@@ -26730,9 +27168,25 @@ function recordCaptainSessionStart(payload) {
|
|
|
26730
27168
|
} catch {
|
|
26731
27169
|
}
|
|
26732
27170
|
}
|
|
27171
|
+
var RULES_HOOK_EVENTS = { "session-start": "SessionStart", "prompt-submit": "UserPromptSubmit" };
|
|
27172
|
+
async function rulesHookOutput(sub, payload, opts) {
|
|
27173
|
+
if (sub !== "session-start" && sub !== "prompt-submit") return "";
|
|
27174
|
+
if (opts.env.SQUADRANT_RULES_INJECT === "0") return "";
|
|
27175
|
+
const ctx = await withBudget(() => computeRulesInjection({
|
|
27176
|
+
event: sub,
|
|
27177
|
+
payload,
|
|
27178
|
+
env: opts.env,
|
|
27179
|
+
cwd: opts.cwd,
|
|
27180
|
+
project: opts.project,
|
|
27181
|
+
cfg: (opts.loadCfg ?? loadConfig)(),
|
|
27182
|
+
stateRoot: opts.stateRoot ?? join36(CONFIG_DIR, "state")
|
|
27183
|
+
}), opts.budgetMs ?? INJECT_BUDGET_MS);
|
|
27184
|
+
if (!ctx) return "";
|
|
27185
|
+
return JSON.stringify({ hookSpecificOutput: { hookEventName: RULES_HOOK_EVENTS[sub], additionalContext: ctx } });
|
|
27186
|
+
}
|
|
26733
27187
|
function hooksCommand() {
|
|
26734
27188
|
const hooks = new Command33("hooks").description("(internal) receive lifecycle hook events from agent processes");
|
|
26735
|
-
hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
|
|
27189
|
+
hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").option("--project <name>", "project for rules injection (default: SQUADRANT_CREW_PROJECT, then cwd)").action(async (sub, opts) => {
|
|
26736
27190
|
let stdin = "";
|
|
26737
27191
|
try {
|
|
26738
27192
|
for await (const chunk of process.stdin) stdin += chunk;
|
|
@@ -26745,6 +27199,8 @@ function hooksCommand() {
|
|
|
26745
27199
|
} catch {
|
|
26746
27200
|
}
|
|
26747
27201
|
}
|
|
27202
|
+
const rulesOut = await rulesHookOutput(sub, payload, { env: process.env, cwd: process.cwd(), project: opts.project });
|
|
27203
|
+
if (rulesOut) await new Promise((resolve4) => process.stdout.write(rulesOut, () => resolve4()));
|
|
26748
27204
|
if (sub === "session-start" && process.env.SQUADRANT_ROLE === "captain") {
|
|
26749
27205
|
recordCaptainSessionStart(payload);
|
|
26750
27206
|
}
|
|
@@ -26801,13 +27257,13 @@ function hooksCommand() {
|
|
|
26801
27257
|
// packages/cli/src/commands/work.ts
|
|
26802
27258
|
init_dist();
|
|
26803
27259
|
init_dist2();
|
|
26804
|
-
import
|
|
27260
|
+
import path40 from "path";
|
|
26805
27261
|
import { Command as Command34 } from "commander";
|
|
26806
27262
|
import chalk34 from "chalk";
|
|
26807
27263
|
function detectCurrentProject(config, cwd = process.cwd()) {
|
|
26808
27264
|
for (const [name, proj] of Object.entries(config.projects)) {
|
|
26809
27265
|
const projPath = resolveHome(proj.path);
|
|
26810
|
-
if (cwd === projPath || cwd.startsWith(projPath +
|
|
27266
|
+
if (cwd === projPath || cwd.startsWith(projPath + path40.sep)) return name;
|
|
26811
27267
|
}
|
|
26812
27268
|
return void 0;
|
|
26813
27269
|
}
|
|
@@ -26856,8 +27312,8 @@ function stateColor(state) {
|
|
|
26856
27312
|
}
|
|
26857
27313
|
function printItem(item, indent) {
|
|
26858
27314
|
const color = stateColor(item.state);
|
|
26859
|
-
const
|
|
26860
|
-
console.log(TERMINAL_WORK_STATES.has(item.state) ? chalk34.dim(
|
|
27315
|
+
const line2 = " ".repeat(indent) + `${chalk34.dim(item.id)} ${item.title} ${color(`[${item.state}]`)}` + (indent === 0 ? chalk34.dim(` (${item.project})`) : "");
|
|
27316
|
+
console.log(TERMINAL_WORK_STATES.has(item.state) ? chalk34.dim(line2) : line2);
|
|
26861
27317
|
}
|
|
26862
27318
|
function printTree(items) {
|
|
26863
27319
|
const { roots, childrenOf } = groupByParent(items);
|
|
@@ -26939,14 +27395,14 @@ var workCommand = new Command34("work").description("Track your own in-flight wo
|
|
|
26939
27395
|
// packages/cli/src/commands/handoff.ts
|
|
26940
27396
|
init_dist();
|
|
26941
27397
|
import { Command as Command35 } from "commander";
|
|
26942
|
-
import
|
|
27398
|
+
import path43 from "path";
|
|
26943
27399
|
import os21 from "os";
|
|
26944
27400
|
|
|
26945
27401
|
// packages/cli/src/lib/handoff-live-repo.ts
|
|
26946
27402
|
init_dist();
|
|
26947
27403
|
import { execFileSync as execFileSync13 } from "child_process";
|
|
26948
|
-
import
|
|
26949
|
-
import
|
|
27404
|
+
import fs38 from "fs";
|
|
27405
|
+
import path41 from "path";
|
|
26950
27406
|
|
|
26951
27407
|
// packages/cli/src/lib/handoff-branch-state.ts
|
|
26952
27408
|
function tryRun(runner, cmd, args, cwd) {
|
|
@@ -27085,7 +27541,7 @@ function localAheadOfBase(runner, projectPath, base) {
|
|
|
27085
27541
|
}
|
|
27086
27542
|
function readFetchAgeMs(projectPath, now) {
|
|
27087
27543
|
try {
|
|
27088
|
-
const stat2 =
|
|
27544
|
+
const stat2 = fs38.statSync(path41.join(projectPath, ".git", "FETCH_HEAD"));
|
|
27089
27545
|
return Math.max(0, now - stat2.mtime.getTime());
|
|
27090
27546
|
} catch {
|
|
27091
27547
|
return null;
|
|
@@ -27156,7 +27612,7 @@ function gatherLiveRepoState(projectPath, fallbackBaseBranch, tasks, runner = de
|
|
|
27156
27612
|
|
|
27157
27613
|
// packages/cli/src/lib/handoff-claude-mem.ts
|
|
27158
27614
|
import { createRequire } from "module";
|
|
27159
|
-
import
|
|
27615
|
+
import fs39 from "fs";
|
|
27160
27616
|
var { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
|
|
27161
27617
|
var CLAUDE_MEM_RECENCY_LIMIT = 20;
|
|
27162
27618
|
function decisionText(row) {
|
|
@@ -27170,7 +27626,7 @@ function decisionText(row) {
|
|
|
27170
27626
|
return row.narrative ?? "";
|
|
27171
27627
|
}
|
|
27172
27628
|
function queryClaudeMem(dbPath, project) {
|
|
27173
|
-
if (!
|
|
27629
|
+
if (!fs39.existsSync(dbPath)) return null;
|
|
27174
27630
|
let db;
|
|
27175
27631
|
try {
|
|
27176
27632
|
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
@@ -27213,7 +27669,7 @@ function queryClaudeMem(dbPath, project) {
|
|
|
27213
27669
|
}
|
|
27214
27670
|
|
|
27215
27671
|
// packages/cli/src/lib/handoff-transcript.ts
|
|
27216
|
-
import
|
|
27672
|
+
import fs40 from "fs";
|
|
27217
27673
|
var TRANSCRIPT_BYTE_CAP = 2e5;
|
|
27218
27674
|
var DIGEST_BYTE_CAP = 8 * 1024;
|
|
27219
27675
|
var PROMPT_TRUNCATE_CHARS = 300;
|
|
@@ -27276,11 +27732,11 @@ function extractMessages(tailText) {
|
|
|
27276
27732
|
const collectRefs = (text) => {
|
|
27277
27733
|
for (const ref of findRefs(text)) refs.add(ref);
|
|
27278
27734
|
};
|
|
27279
|
-
for (const
|
|
27280
|
-
if (!
|
|
27735
|
+
for (const line2 of tailText.split("\n")) {
|
|
27736
|
+
if (!line2.trim()) continue;
|
|
27281
27737
|
let obj;
|
|
27282
27738
|
try {
|
|
27283
|
-
obj = JSON.parse(
|
|
27739
|
+
obj = JSON.parse(line2);
|
|
27284
27740
|
} catch {
|
|
27285
27741
|
continue;
|
|
27286
27742
|
}
|
|
@@ -27322,27 +27778,27 @@ function extractMessages(tailText) {
|
|
|
27322
27778
|
return { lastUserMessage, lastAssistantText, digest };
|
|
27323
27779
|
}
|
|
27324
27780
|
function extractTranscriptTail(transcriptPath, byteCap = TRANSCRIPT_BYTE_CAP) {
|
|
27325
|
-
if (!
|
|
27326
|
-
const content =
|
|
27781
|
+
if (!fs40.existsSync(transcriptPath)) return null;
|
|
27782
|
+
const content = fs40.readFileSync(transcriptPath, "utf-8");
|
|
27327
27783
|
const { lastUserMessage, lastAssistantText, digest } = extractMessages(tailOf(content, byteCap));
|
|
27328
|
-
const mtimeIso =
|
|
27784
|
+
const mtimeIso = fs40.statSync(transcriptPath).mtime.toISOString();
|
|
27329
27785
|
return { path: transcriptPath, mtimeIso, lastUserMessage, lastAssistantText, digest };
|
|
27330
27786
|
}
|
|
27331
27787
|
|
|
27332
27788
|
// packages/cli/src/lib/handoff-archive.ts
|
|
27333
|
-
import
|
|
27334
|
-
import
|
|
27789
|
+
import fs41 from "fs";
|
|
27790
|
+
import path42 from "path";
|
|
27335
27791
|
function readNewestArchivedHandoff(spokeVault, now) {
|
|
27336
|
-
const dir =
|
|
27337
|
-
if (!
|
|
27338
|
-
const candidates =
|
|
27339
|
-
const full =
|
|
27340
|
-
return { name: e.name, full, mtime:
|
|
27792
|
+
const dir = path42.join(spokeVault, "handoffs");
|
|
27793
|
+
if (!fs41.existsSync(dir)) return null;
|
|
27794
|
+
const candidates = fs41.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => {
|
|
27795
|
+
const full = path42.join(dir, e.name);
|
|
27796
|
+
return { name: e.name, full, mtime: fs41.statSync(full).mtime };
|
|
27341
27797
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
27342
27798
|
for (const candidate of candidates) {
|
|
27343
27799
|
let content;
|
|
27344
27800
|
try {
|
|
27345
|
-
content = JSON.parse(
|
|
27801
|
+
content = JSON.parse(fs41.readFileSync(candidate.full, "utf-8"));
|
|
27346
27802
|
} catch {
|
|
27347
27803
|
continue;
|
|
27348
27804
|
}
|
|
@@ -27352,7 +27808,7 @@ function readNewestArchivedHandoff(spokeVault, now) {
|
|
|
27352
27808
|
}
|
|
27353
27809
|
|
|
27354
27810
|
// packages/cli/src/commands/handoff.ts
|
|
27355
|
-
var CLAUDE_MEM_DB_PATH =
|
|
27811
|
+
var CLAUDE_MEM_DB_PATH = path43.join(os21.homedir(), ".claude-mem", "claude-mem.db");
|
|
27356
27812
|
async function defaultFetchTasks(project) {
|
|
27357
27813
|
return await squadrantdCall({ kind: "list", project });
|
|
27358
27814
|
}
|
|
@@ -27420,9 +27876,9 @@ function filterSessions(rows, f) {
|
|
|
27420
27876
|
if (f.agent && r.agent !== f.agent) return false;
|
|
27421
27877
|
if (f.liveOnly && r.status === "stale") return false;
|
|
27422
27878
|
if (f.project) {
|
|
27423
|
-
const
|
|
27879
|
+
const path46 = f.projects?.[f.project];
|
|
27424
27880
|
const byRecord = r.project === f.project;
|
|
27425
|
-
const byCwd = !!
|
|
27881
|
+
const byCwd = !!path46 && !!r.cwd && (r.cwd === normalized(path46) || r.cwd.startsWith(normalized(path46) + "/"));
|
|
27426
27882
|
if (!byRecord && !byCwd) return false;
|
|
27427
27883
|
}
|
|
27428
27884
|
return true;
|
|
@@ -27442,8 +27898,8 @@ function formatSessionTable(rows) {
|
|
|
27442
27898
|
]);
|
|
27443
27899
|
const all = [header, ...body];
|
|
27444
27900
|
const widths = header.map((_, i) => Math.max(...all.map((row) => row[i].length)));
|
|
27445
|
-
const
|
|
27446
|
-
return [
|
|
27901
|
+
const line2 = (row) => row.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
27902
|
+
return [line2(header), ...body.map(line2)].join("\n");
|
|
27447
27903
|
}
|
|
27448
27904
|
async function collectSessions(drivers) {
|
|
27449
27905
|
const rows = [];
|
|
@@ -27513,7 +27969,7 @@ init_dist();
|
|
|
27513
27969
|
init_dist2();
|
|
27514
27970
|
init_dist3();
|
|
27515
27971
|
import os22 from "os";
|
|
27516
|
-
import
|
|
27972
|
+
import path44 from "path";
|
|
27517
27973
|
import { Command as Command37 } from "commander";
|
|
27518
27974
|
import chalk36 from "chalk";
|
|
27519
27975
|
|
|
@@ -27631,7 +28087,7 @@ function buildWhoamiDeps(opts = {}) {
|
|
|
27631
28087
|
const projects = Object.fromEntries(
|
|
27632
28088
|
Object.entries(config.projects).map(([n, p]) => [n, resolveHome(p.path)])
|
|
27633
28089
|
);
|
|
27634
|
-
const root = stateRoot ??
|
|
28090
|
+
const root = stateRoot ?? path44.join(os22.homedir(), ".config", "squadrant", "state");
|
|
27635
28091
|
const store = createStore(root);
|
|
27636
28092
|
const merged = {
|
|
27637
28093
|
env: process.env,
|
|
@@ -27683,8 +28139,8 @@ var whoamiCommand = new Command37("whoami").description("Show the agent session
|
|
|
27683
28139
|
// packages/cli/src/commands/knowledge.ts
|
|
27684
28140
|
init_dist();
|
|
27685
28141
|
init_dist2();
|
|
27686
|
-
import
|
|
27687
|
-
import
|
|
28142
|
+
import fs42 from "fs";
|
|
28143
|
+
import path45 from "path";
|
|
27688
28144
|
import { Command as Command38 } from "commander";
|
|
27689
28145
|
import chalk37 from "chalk";
|
|
27690
28146
|
var KNOWLEDGE_PRIVACY_NOTICE = "Sources are sent to the extraction crew's model; mark `sensitivity: local-only` to keep a source on local models only.";
|
|
@@ -27697,18 +28153,18 @@ function runKnowledgeInit(kb, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
27697
28153
|
const hub = resolveHome(cfg.hubVault);
|
|
27698
28154
|
const dir = kbDir(hub, kb);
|
|
27699
28155
|
const created = [];
|
|
27700
|
-
for (const d of [
|
|
27701
|
-
if (!
|
|
27702
|
-
|
|
27703
|
-
created.push(
|
|
28156
|
+
for (const d of [path45.join(dir, "raw"), kbRulesDir(hub, kb), path45.join(kbRulesDir(hub, kb), PROPOSED_DIR)]) {
|
|
28157
|
+
if (!fs42.existsSync(d)) {
|
|
28158
|
+
fs42.mkdirSync(d, { recursive: true });
|
|
28159
|
+
created.push(path45.relative(dir, d));
|
|
27704
28160
|
}
|
|
27705
28161
|
}
|
|
27706
|
-
const sourcesFile =
|
|
27707
|
-
if (!
|
|
27708
|
-
|
|
28162
|
+
const sourcesFile = path45.join(dir, "sources.yaml");
|
|
28163
|
+
if (!fs42.existsSync(sourcesFile)) {
|
|
28164
|
+
fs42.writeFileSync(sourcesFile, SOURCES_TEMPLATE);
|
|
27709
28165
|
created.push("sources.yaml");
|
|
27710
28166
|
}
|
|
27711
|
-
if (!
|
|
28167
|
+
if (!fs42.existsSync(path45.join(dir, "index.json"))) {
|
|
27712
28168
|
writeIndex(hub, kb, compileIndex(kb, []));
|
|
27713
28169
|
created.push("index.json");
|
|
27714
28170
|
}
|
|
@@ -27723,7 +28179,7 @@ function runKnowledgeSubscribe(kb, project, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
27723
28179
|
const cfg = loadConfig(configPath);
|
|
27724
28180
|
const pc = cfg.projects[project];
|
|
27725
28181
|
if (!pc) throw new Error(`Unknown project '${project}'`);
|
|
27726
|
-
if (!
|
|
28182
|
+
if (!fs42.existsSync(kbDir(resolveHome(cfg.hubVault), kb))) {
|
|
27727
28183
|
throw new Error(`Knowledge base '${kb}' does not exist. Run: squadrant knowledge init ${kb}`);
|
|
27728
28184
|
}
|
|
27729
28185
|
const list = pc.knowledge ?? [];
|
|
@@ -27761,9 +28217,9 @@ function runKnowledgeReindex(kb, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
27761
28217
|
assertKbName(kb);
|
|
27762
28218
|
const cfg = loadConfig(configPath);
|
|
27763
28219
|
const hub = resolveHome(cfg.hubVault);
|
|
27764
|
-
if (!
|
|
28220
|
+
if (!fs42.existsSync(kbDir(hub, kb))) throw new Error(`Knowledge base '${kb}' does not exist. Run: squadrant knowledge init ${kb}`);
|
|
27765
28221
|
const { rules: all, errors } = loadKbRules(cfg, kb, { includeProposed: true });
|
|
27766
|
-
const proposedRoot =
|
|
28222
|
+
const proposedRoot = path45.join(kbRulesDir(hub, kb), PROPOSED_DIR) + path45.sep;
|
|
27767
28223
|
const live = all.filter((r) => !r.file.startsWith(proposedRoot));
|
|
27768
28224
|
const file = writeIndex(hub, kb, compileIndex(kb, live));
|
|
27769
28225
|
return { file, count: live.length, proposed: all.length - live.length, errors: errors.length, invalid: errors };
|
|
@@ -27772,7 +28228,7 @@ function runKnowledgeValidate(kb, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
27772
28228
|
assertKbName(kb);
|
|
27773
28229
|
const cfg = loadConfig(configPath);
|
|
27774
28230
|
const hub = resolveHome(cfg.hubVault);
|
|
27775
|
-
if (!
|
|
28231
|
+
if (!fs42.existsSync(kbDir(hub, kb))) throw new Error(`Knowledge base '${kb}' does not exist. Run: squadrant knowledge init ${kb}`);
|
|
27776
28232
|
const { sources, errors } = loadSources(hub, kb);
|
|
27777
28233
|
const res = validateKb({ rulesDir: kbRulesDir(hub, kb), kbRoot: kbDir(hub, kb), sources, domains: cfg.knowledge?.[kb]?.domains });
|
|
27778
28234
|
const srcChecks = errors.map((e) => ({ level: "fail", message: `sources.yaml: ${e}` }));
|
|
@@ -27864,10 +28320,10 @@ function runRulesShow(id, opts, configPath = DEFAULT_CONFIG_PATH) {
|
|
|
27864
28320
|
var rulesCommand = new Command39("rules").description("Look up rules from subscribed knowledge bases (#893)");
|
|
27865
28321
|
rulesCommand.command("search <query...>").description("Search rules by keyword (ranked; best match first)").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").option("--limit <n>", `max results (default ${DEFAULT_SEARCH_LIMIT})`).option("--brief", "one line per rule: [score] MODALITY id: statement").option("--ids-only", "print only rule ids").action((query, opts) => {
|
|
27866
28322
|
const hits = runRulesSearch(query.join(" "), opts);
|
|
27867
|
-
for (const
|
|
28323
|
+
for (const line2 of renderSearch(hits, opts)) console.log(line2);
|
|
27868
28324
|
});
|
|
27869
28325
|
rulesCommand.command("list").description("List every resolved rule for the project").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").option("--brief", "one line per rule: MODALITY id: statement").action((opts) => {
|
|
27870
|
-
for (const
|
|
28326
|
+
for (const line2 of renderList2(runRulesList(opts), opts)) console.log(line2);
|
|
27871
28327
|
});
|
|
27872
28328
|
rulesCommand.command("show <ids...>").description("Show one or more rules in full").option("--project <name>", "project (default: from cwd or SQUADRANT_CREW_PROJECT)").option("--all", "include proposed and retired rules").action((ids, opts) => {
|
|
27873
28329
|
for (const id of ids) {
|
|
@@ -27883,14 +28339,14 @@ init_dist();
|
|
|
27883
28339
|
init_dist();
|
|
27884
28340
|
init_dist();
|
|
27885
28341
|
var __dirname = dirname12(fileURLToPath6(import.meta.url));
|
|
27886
|
-
var pkg = JSON.parse(readFileSync21(
|
|
28342
|
+
var pkg = JSON.parse(readFileSync21(join37(__dirname, "..", "package.json"), "utf-8"));
|
|
27887
28343
|
ensureRuntimeSynced({
|
|
27888
|
-
sourceRoot:
|
|
27889
|
-
runtimeRoot:
|
|
28344
|
+
sourceRoot: join37(__dirname, ".."),
|
|
28345
|
+
runtimeRoot: join37(homedir21(), ".config", "squadrant")
|
|
27890
28346
|
});
|
|
27891
28347
|
if (!process.env.VITEST) {
|
|
27892
28348
|
try {
|
|
27893
|
-
syncShippedOpencodeSkills({ pkgRoot:
|
|
28349
|
+
syncShippedOpencodeSkills({ pkgRoot: join37(__dirname, "..") });
|
|
27894
28350
|
} catch (err) {
|
|
27895
28351
|
process.stderr.write(
|
|
27896
28352
|
`squadrant: opencode skills sync skipped: ${err.message}
|
|
@@ -27900,7 +28356,7 @@ if (!process.env.VITEST) {
|
|
|
27900
28356
|
}
|
|
27901
28357
|
if (process.argv[2] !== "config") {
|
|
27902
28358
|
try {
|
|
27903
|
-
const cfgPath =
|
|
28359
|
+
const cfgPath = join37(homedir21(), ".config", "squadrant", "config.json");
|
|
27904
28360
|
if (existsSync14(cfgPath)) {
|
|
27905
28361
|
const cfg = JSON.parse(readConfigFileSync(cfgPath));
|
|
27906
28362
|
if (needsCheck(cfg, pkg.version)) {
|