squadrant 0.16.4 → 0.16.6
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 +698 -309
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +160 -38
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/plugin/skills/captain-ops/SKILL.md +23 -0
- package/plugin/skills/explainer-reel/SKILL.md +160 -0
- package/plugin/skills/explainer-reel/assets/scene-kit.js +196 -0
- package/plugin/skills/explainer-reel/assets/swimlane-preset.html +391 -0
- package/plugin/skills/explainer-reel/assets/theme.css +99 -0
- package/plugin/skills/explainer-reel/examples/jwt-reel.gif +0 -0
- package/plugin/skills/explainer-reel/examples/jwt-reel.html +233 -0
- package/plugin/skills/explainer-reel/references/component-library.md +75 -0
- package/plugin/skills/explainer-reel/references/design-tokens.md +72 -0
- package/plugin/skills/explainer-reel/references/interactive-mode.md +62 -0
- package/plugin/skills/explainer-reel/scripts/README.md +30 -0
- package/plugin/skills/explainer-reel/scripts/contact-sheet.sh +20 -0
- package/plugin/skills/explainer-reel/scripts/render-gif.sh +44 -0
- package/plugin/skills/explainer-reel/scripts/seek-shot.sh +24 -0
package/dist/squadrantd.js
CHANGED
|
@@ -490,6 +490,9 @@ function stampAttempt(rec, patch, now) {
|
|
|
490
490
|
attempts.push(last);
|
|
491
491
|
return { ...rec, attempts };
|
|
492
492
|
}
|
|
493
|
+
function isStickyAttention(state) {
|
|
494
|
+
return state === "blocked" || state === "review";
|
|
495
|
+
}
|
|
493
496
|
function nextPendingTool(current, ev, now) {
|
|
494
497
|
if (ev.note === "agent.hook.PreToolUse")
|
|
495
498
|
return { name: ev.tool ?? "tool", since: now };
|
|
@@ -497,6 +500,11 @@ function nextPendingTool(current, ev, now) {
|
|
|
497
500
|
return void 0;
|
|
498
501
|
return current;
|
|
499
502
|
}
|
|
503
|
+
function nextPendingMonitor(current, ev, now) {
|
|
504
|
+
if (ev.note === "agent.hook.PreToolUse" && ev.tool === "Monitor")
|
|
505
|
+
return { since: now };
|
|
506
|
+
return current;
|
|
507
|
+
}
|
|
500
508
|
function reduce(rec, ev, now) {
|
|
501
509
|
if (ev.type === "task.reopened") {
|
|
502
510
|
return { ...rec, state: "working", question: void 0, error: void 0, lastHeartbeat: now, lastEvent: ev.type };
|
|
@@ -513,20 +521,23 @@ function reduce(rec, ev, now) {
|
|
|
513
521
|
sessionId: ev.sessionId ?? rec.sessionId,
|
|
514
522
|
question: void 0,
|
|
515
523
|
// resuming after a blocked→reply clears the question
|
|
516
|
-
pendingTool: void 0
|
|
524
|
+
pendingTool: void 0,
|
|
517
525
|
// #354: a new turn closes any prior tool window
|
|
526
|
+
pendingMonitor: void 0
|
|
527
|
+
// #594a: same reset — a new turn moots any prior watch
|
|
518
528
|
};
|
|
519
529
|
case "task.progress": {
|
|
520
530
|
const pendingTool = nextPendingTool(rec.pendingTool, ev, now);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
531
|
+
const pendingMonitor = nextPendingMonitor(rec.pendingMonitor, ev, now);
|
|
532
|
+
if (isStickyAttention(rec.state))
|
|
533
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev.type, pendingTool, pendingMonitor };
|
|
534
|
+
const b = { ...base, pendingTool, pendingMonitor };
|
|
524
535
|
if (rec.state === "awaiting-input" || rec.state === "stalled")
|
|
525
536
|
return { ...stampAttempt(b, {}, now), state: "working" };
|
|
526
537
|
return stampAttempt(b, {}, now);
|
|
527
538
|
}
|
|
528
539
|
case "heartbeat":
|
|
529
|
-
if (rec.state
|
|
540
|
+
if (isStickyAttention(rec.state))
|
|
530
541
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
531
542
|
if (rec.state === "awaiting-input")
|
|
532
543
|
return { ...base, state: "working" };
|
|
@@ -534,8 +545,13 @@ function reduce(rec, ev, now) {
|
|
|
534
545
|
case "task.blocked":
|
|
535
546
|
if (rec.state === "blocked")
|
|
536
547
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
537
|
-
return { ...base, state: "blocked", question: ev.question, pendingTool: void 0 };
|
|
548
|
+
return { ...base, state: "blocked", question: ev.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
549
|
+
case "task.review":
|
|
550
|
+
return { ...base, state: "review", reviewNote: ev.message, pendingTool: void 0, pendingMonitor: void 0 };
|
|
538
551
|
case "task.done":
|
|
552
|
+
if (rec.state === "review" && ev.source !== "approve") {
|
|
553
|
+
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
554
|
+
}
|
|
539
555
|
return { ...base, state: "done", resultRef: ev.resultRef, parseWarning: ev.parseWarning };
|
|
540
556
|
case "task.failed":
|
|
541
557
|
return { ...base, state: "failed", error: ev.error, exitCode: ev.exitCode };
|
|
@@ -546,19 +562,19 @@ function reduce(rec, ev, now) {
|
|
|
546
562
|
case "task.session":
|
|
547
563
|
return stampAttempt(base, { resumeRef: ev.resumeRef }, now);
|
|
548
564
|
case "task.turn.started":
|
|
549
|
-
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0 };
|
|
565
|
+
return { ...stampAttempt(base, {}, now), state: "working", pendingTool: void 0, pendingMonitor: void 0 };
|
|
550
566
|
case "task.turn.completed":
|
|
551
|
-
if (rec.state
|
|
567
|
+
if (isStickyAttention(rec.state))
|
|
552
568
|
return { ...rec, lastHeartbeat: now, lastEvent: ev.type };
|
|
553
|
-
if (rec.pendingTool)
|
|
569
|
+
if (rec.pendingTool || rec.pendingMonitor)
|
|
554
570
|
return stampAttempt(base, {}, now);
|
|
555
|
-
return { ...stampAttempt(base, {}, now), state: "awaiting-input", pendingTool: void 0 };
|
|
571
|
+
return { ...stampAttempt(base, {}, now), state: "awaiting-input", pendingTool: void 0, pendingMonitor: void 0 };
|
|
556
572
|
case "task.delta":
|
|
557
573
|
return stampAttempt(base, {}, now);
|
|
558
574
|
// heartbeat-only
|
|
559
575
|
case "task.input.requested":
|
|
560
576
|
case "task.approval.requested":
|
|
561
|
-
return { ...stampAttempt(base, {}, now), state: "blocked", question: ev.question, pendingTool: void 0 };
|
|
577
|
+
return { ...stampAttempt(base, {}, now), state: "blocked", question: ev.question, pendingTool: void 0, pendingMonitor: void 0 };
|
|
562
578
|
case "task.reattached":
|
|
563
579
|
return stampAttempt(base, {}, now);
|
|
564
580
|
case "task.first-turn.confirmed":
|
|
@@ -579,15 +595,22 @@ function reduce(rec, ev, now) {
|
|
|
579
595
|
|
|
580
596
|
// packages/core/dist/watchdog.js
|
|
581
597
|
var TOOL_STALL_BUDGET_MS = 10 * 60 * 1e3;
|
|
582
|
-
|
|
598
|
+
var MONITOR_STALL_BUDGET_MS = 60 * 60 * 1e3;
|
|
599
|
+
function evaluateStall(rec, now, toolStallMs = TOOL_STALL_BUDGET_MS, monitorStallMs = MONITOR_STALL_BUDGET_MS) {
|
|
583
600
|
if (rec.state !== "working")
|
|
584
601
|
return null;
|
|
585
602
|
if (rec.mode === "interactive") {
|
|
586
|
-
if (
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return
|
|
590
|
-
|
|
603
|
+
if (rec.pendingTool) {
|
|
604
|
+
if (now - rec.pendingTool.since <= toolStallMs)
|
|
605
|
+
return null;
|
|
606
|
+
return { ...rec, state: "stalled", lastEvent: "watchdog.tool-stall" };
|
|
607
|
+
}
|
|
608
|
+
if (rec.pendingMonitor) {
|
|
609
|
+
if (now - rec.pendingMonitor.since <= monitorStallMs)
|
|
610
|
+
return null;
|
|
611
|
+
return { ...rec, state: "stalled", lastEvent: "watchdog.monitor-stall" };
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
591
614
|
}
|
|
592
615
|
const liveness = rec.attempts.at(-1)?.lastHeartbeatAt ?? rec.lastHeartbeat;
|
|
593
616
|
if (now - liveness <= rec.heartbeatBudgetMs)
|
|
@@ -597,7 +620,7 @@ function evaluateStall(rec, now, toolStallMs = TOOL_STALL_BUDGET_MS) {
|
|
|
597
620
|
function recoverStall(rec, now) {
|
|
598
621
|
if (rec.state !== "stalled")
|
|
599
622
|
return null;
|
|
600
|
-
return { ...rec, state: "working", lastHeartbeat: now, lastEvent: "watchdog.recover", pendingTool: void 0 };
|
|
623
|
+
return { ...rec, state: "working", lastHeartbeat: now, lastEvent: "watchdog.recover", pendingTool: void 0, pendingMonitor: void 0 };
|
|
601
624
|
}
|
|
602
625
|
|
|
603
626
|
// packages/core/dist/daemon/reduce.js
|
|
@@ -606,8 +629,8 @@ var DEFAULT_FIRST_TURN_RESEND_COOLDOWN_MS = 6e4;
|
|
|
606
629
|
var DEFAULT_TASK_TIMEOUT_MS = 8 * 60 * 60 * 1e3;
|
|
607
630
|
var TERMINAL_RECORD_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
608
631
|
var TERMINAL_RECORD_KEEP_PER_PROJECT = 20;
|
|
609
|
-
var ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "failed", "stalled", "awaiting-input"]);
|
|
610
|
-
var REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked"]);
|
|
632
|
+
var ATTENTION_STATES = /* @__PURE__ */ new Set(["done", "blocked", "review", "failed", "stalled", "awaiting-input"]);
|
|
633
|
+
var REAPABLE_SURFACE_STATES = /* @__PURE__ */ new Set(["working", "stalled", "awaiting-input", "blocked", "review"]);
|
|
611
634
|
var IDLE_DEBOUNCE_MS = 12e3;
|
|
612
635
|
function shortId(id) {
|
|
613
636
|
return id.slice(0, 8);
|
|
@@ -629,6 +652,10 @@ function formatMessage(rec, event) {
|
|
|
629
652
|
}
|
|
630
653
|
case "blocked":
|
|
631
654
|
return `CREW BLOCKED ${tag}: ${(rec.question ?? "(no question)").trim()}`;
|
|
655
|
+
case "review": {
|
|
656
|
+
const note = (rec.reviewNote ?? "").trim();
|
|
657
|
+
return `CREW REVIEW ${tag}: ${note || "ready for review"} \u2014 run 'squadrant diff ${rec.project} ${rec.name ?? rec.id}' then 'squadrant crew approve' or send feedback.`;
|
|
658
|
+
}
|
|
632
659
|
case "failed":
|
|
633
660
|
return `CREW FAILED ${tag}: ${(rec.error ?? "(no error)").trim()}`;
|
|
634
661
|
case "stalled": {
|
|
@@ -699,6 +726,7 @@ var KNOWN_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
699
726
|
"task.progress",
|
|
700
727
|
"heartbeat",
|
|
701
728
|
"task.blocked",
|
|
729
|
+
"task.review",
|
|
702
730
|
"task.done",
|
|
703
731
|
"task.failed",
|
|
704
732
|
"task.session",
|
|
@@ -899,7 +927,7 @@ function createDaemon(deps) {
|
|
|
899
927
|
store.delete(r.project, r.id);
|
|
900
928
|
continue;
|
|
901
929
|
}
|
|
902
|
-
if (!TERMINAL_STATES.has(r.state)) {
|
|
930
|
+
if (!TERMINAL_STATES.has(r.state) && !isStickyAttention(r.state)) {
|
|
903
931
|
const ceiling = deps.taskTimeoutMs ?? DEFAULT_TASK_TIMEOUT_MS;
|
|
904
932
|
if (t - r.createdAt > ceiling) {
|
|
905
933
|
const prevState = r.state;
|
|
@@ -944,7 +972,7 @@ function createDaemon(deps) {
|
|
|
944
972
|
const idle = evaluateStall(r, t);
|
|
945
973
|
if (idle) {
|
|
946
974
|
store.put(idle);
|
|
947
|
-
const synthEvent = idle.pendingTool ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: idle.pendingTool.name, elapsedMs: t - idle.pendingTool.since } : { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs };
|
|
975
|
+
const synthEvent = idle.pendingTool ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: idle.pendingTool.name, elapsedMs: t - idle.pendingTool.since } : idle.pendingMonitor ? { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs, tool: "Monitor", elapsedMs: t - idle.pendingMonitor.since } : { type: "task.stalled", id: r.id, heartbeatBudgetMs: r.heartbeatBudgetMs };
|
|
948
976
|
firePush(deps, r.project, r.state, idle, synthEvent, lastCaptainTurnAt.get(r.id));
|
|
949
977
|
continue;
|
|
950
978
|
}
|
|
@@ -2074,9 +2102,11 @@ function buildSurfaceProbe(ctx, probes, daemonCmux) {
|
|
|
2074
2102
|
// packages/core/dist/delivery/defer-delivery.js
|
|
2075
2103
|
var DeferDelivery = class extends Error {
|
|
2076
2104
|
draft;
|
|
2077
|
-
|
|
2105
|
+
reason;
|
|
2106
|
+
constructor(draft = null, reason = "draft") {
|
|
2078
2107
|
super("deferred: captain composing");
|
|
2079
2108
|
this.draft = draft;
|
|
2109
|
+
this.reason = reason;
|
|
2080
2110
|
this.name = "DeferDelivery";
|
|
2081
2111
|
}
|
|
2082
2112
|
};
|
|
@@ -2094,6 +2124,7 @@ var CaptainDelivery = class {
|
|
|
2094
2124
|
deferCounts = /* @__PURE__ */ new Map();
|
|
2095
2125
|
lastContent = /* @__PURE__ */ new Map();
|
|
2096
2126
|
stableCounts = /* @__PURE__ */ new Map();
|
|
2127
|
+
lastReason = /* @__PURE__ */ new Map();
|
|
2097
2128
|
constructor(opts) {
|
|
2098
2129
|
this.opts = opts;
|
|
2099
2130
|
}
|
|
@@ -2116,29 +2147,40 @@ var CaptainDelivery = class {
|
|
|
2116
2147
|
this.deferCounts.delete(seq);
|
|
2117
2148
|
this.stableCounts.delete(seq);
|
|
2118
2149
|
this.lastContent.delete(seq);
|
|
2150
|
+
this.lastReason.delete(seq);
|
|
2119
2151
|
return { delivered: true };
|
|
2120
2152
|
} catch (e) {
|
|
2121
2153
|
if (e instanceof DeferDelivery) {
|
|
2122
2154
|
this.deferCounts.set(seq, deferCount + 1);
|
|
2123
2155
|
const content = e.draft;
|
|
2156
|
+
let stableCount;
|
|
2124
2157
|
if (content && content === this.lastContent.get(seq)) {
|
|
2125
|
-
|
|
2158
|
+
stableCount = (this.stableCounts.get(seq) ?? 0) + 1;
|
|
2159
|
+
this.stableCounts.set(seq, stableCount);
|
|
2126
2160
|
} else {
|
|
2161
|
+
stableCount = 0;
|
|
2127
2162
|
this.stableCounts.set(seq, 0);
|
|
2128
2163
|
}
|
|
2129
2164
|
this.lastContent.set(seq, content);
|
|
2130
|
-
|
|
2165
|
+
const reason = e.reason !== "draft" ? e.reason : stableCount >= this.opts.stableProbePolls ? "stable" : "draft";
|
|
2166
|
+
this.lastReason.set(seq, reason);
|
|
2167
|
+
return { deferred: true, reason };
|
|
2131
2168
|
}
|
|
2132
|
-
|
|
2169
|
+
this.lastReason.set(seq, "unknown");
|
|
2170
|
+
return { deferred: true, reason: "unknown" };
|
|
2133
2171
|
}
|
|
2134
2172
|
}
|
|
2135
2173
|
/** Read-only. Never mutates — safe to poll from the snapshot assembler every tick. */
|
|
2136
2174
|
stats() {
|
|
2137
2175
|
let maxDeferCount = 0;
|
|
2138
|
-
|
|
2139
|
-
|
|
2176
|
+
let reason;
|
|
2177
|
+
for (const [seq, c] of this.deferCounts) {
|
|
2178
|
+
if (c > maxDeferCount) {
|
|
2140
2179
|
maxDeferCount = c;
|
|
2141
|
-
|
|
2180
|
+
reason = this.lastReason.get(seq);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
return { maxDeferCount, stuck: maxDeferCount >= this.opts.maxDefers, reason };
|
|
2142
2184
|
}
|
|
2143
2185
|
};
|
|
2144
2186
|
|
|
@@ -2238,6 +2280,10 @@ function createDelivery(ctx, daemonCmux) {
|
|
|
2238
2280
|
const notifyFault = ctx.notifyFault ?? (() => {
|
|
2239
2281
|
});
|
|
2240
2282
|
const defaultNotify = async (args) => {
|
|
2283
|
+
const fresh = store.get(args.project, args.record.id);
|
|
2284
|
+
if (fresh && TERMINAL_STATES.has(fresh.state) && fresh.state !== args.record.state) {
|
|
2285
|
+
return;
|
|
2286
|
+
}
|
|
2241
2287
|
try {
|
|
2242
2288
|
await appendToMailbox({
|
|
2243
2289
|
stateRoot,
|
|
@@ -2327,16 +2373,19 @@ function createDelivery(ctx, daemonCmux) {
|
|
|
2327
2373
|
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=delivered`);
|
|
2328
2374
|
await writeCursor({ stateRoot, project, subscriber: CURSOR_SUBSCRIBER, lastAckedSeq: entry.seq });
|
|
2329
2375
|
} else {
|
|
2330
|
-
|
|
2376
|
+
const { maxDeferCount } = d.stats();
|
|
2377
|
+
if (maxDeferCount === 1 || maxDeferCount % 30 === 0) {
|
|
2378
|
+
log(`delivery seq=${entry.seq} kind=${entry.kind} outcome=deferred project=${project} reason=${result.reason}`);
|
|
2379
|
+
}
|
|
2331
2380
|
break;
|
|
2332
2381
|
}
|
|
2333
2382
|
}
|
|
2334
2383
|
const stuck = d.stats().stuck;
|
|
2335
2384
|
if (stuck && !stuckNotified.has(project)) {
|
|
2336
2385
|
stuckNotified.add(project);
|
|
2337
|
-
const { maxDeferCount } = d.stats();
|
|
2338
|
-
log(`delivery stuck project=${project} deferCount=${maxDeferCount}`);
|
|
2339
|
-
const text = `\u26A0\uFE0F DELIVERY STUCK: an in-progress draft (or ghost text) in your input box has blocked pending notification(s) for ${maxDeferCount}+ retries. Your input is never touched \u2014 this keeps retrying safely and will deliver automatically once you submit or clear it.`;
|
|
2386
|
+
const { maxDeferCount, reason } = d.stats();
|
|
2387
|
+
log(`delivery stuck project=${project} deferCount=${maxDeferCount} reason=${reason ?? "unknown"}`);
|
|
2388
|
+
const text = reason === "modal" ? `\u26A0\uFE0F DELIVERY STUCK: a modal question is open in your captain pane and has blocked pending notification(s) for ${maxDeferCount}+ retries. This keeps retrying safely and will deliver automatically once you answer or dismiss it.` : `\u26A0\uFE0F DELIVERY STUCK: an in-progress draft (or ghost text) in your input box has blocked pending notification(s) for ${maxDeferCount}+ retries. Your input is never touched \u2014 this keeps retrying safely and will deliver automatically once you submit or clear it.`;
|
|
2340
2389
|
appendCaptainMessage({ stateRoot, project, text, source: "daemon" }).catch((e) => log(`delivery stuck alert failed project=${project}: ${e.message}`));
|
|
2341
2390
|
Promise.resolve(notifyFault(project, text)).catch((e) => log(`delivery stuck fault-notify failed project=${project}: ${e.message}`));
|
|
2342
2391
|
telegramBridge?.pushRaw(project, text);
|
|
@@ -2841,8 +2890,12 @@ var REGISTRY = {
|
|
|
2841
2890
|
build: (a) => a[0] ? ok("crews", ["crew", "list", a[0]]) : usage("crews", "usage: /crews <project>")
|
|
2842
2891
|
},
|
|
2843
2892
|
launch: {
|
|
2893
|
+
// --headless (#586, same reason as #520 on the boot-if-down path): runCommand
|
|
2894
|
+
// execs this argv from the daemon, which has no CMUX_WORKSPACE_ID and no
|
|
2895
|
+
// terminal — a plain `launch` would open the cmux GUI app and exit 0 before
|
|
2896
|
+
// the workspace is ever launched.
|
|
2844
2897
|
usage: "/launch <project>",
|
|
2845
|
-
build: (a) => a[0] ? ok("launch", ["launch", a[0]]) : usage("launch", "usage: /launch <project>")
|
|
2898
|
+
build: (a) => a[0] ? ok("launch", ["launch", a[0], "--headless"]) : usage("launch", "usage: /launch <project>")
|
|
2846
2899
|
},
|
|
2847
2900
|
effort: {
|
|
2848
2901
|
usage: "/effort [max|balance|low]",
|
|
@@ -3027,6 +3080,9 @@ ${ev.message}` : "");
|
|
|
3027
3080
|
case "task.blocked":
|
|
3028
3081
|
return `\u{1F6A7} [${project}] CREW BLOCKED \xB7 ${ev.id}
|
|
3029
3082
|
${ev.question}`;
|
|
3083
|
+
case "task.review":
|
|
3084
|
+
return `\u{1F440} [${project}] CREW REVIEW \xB7 ${ev.id}` + (ev.message ? `
|
|
3085
|
+
${ev.message}` : "");
|
|
3030
3086
|
case "task.idle":
|
|
3031
3087
|
return `\u{1F4A4} [${project}] CREW IDLE \xB7 ${ev.id}`;
|
|
3032
3088
|
case "task.failed":
|
|
@@ -3223,6 +3279,7 @@ var DONE_ONLY = /* @__PURE__ */ new Set(["task.done", "task.failed"]);
|
|
|
3223
3279
|
var ALERTS = /* @__PURE__ */ new Set([
|
|
3224
3280
|
...DONE_ONLY,
|
|
3225
3281
|
"task.blocked",
|
|
3282
|
+
"task.review",
|
|
3226
3283
|
"task.approval.requested",
|
|
3227
3284
|
"task.input.requested",
|
|
3228
3285
|
"task.timeout"
|
|
@@ -4658,6 +4715,18 @@ function cmux(args) {
|
|
|
4658
4715
|
);
|
|
4659
4716
|
});
|
|
4660
4717
|
}
|
|
4718
|
+
function cmuxStdin(args, input) {
|
|
4719
|
+
return new Promise((resolve2, reject) => {
|
|
4720
|
+
const child = execFile2(resolveCmuxBin(), args, { encoding: "utf-8", timeout: CMUX_TIMEOUT, env: { ...process.env, CMUX_QUIET: "1" } }, (err, stdout) => {
|
|
4721
|
+
if (err) {
|
|
4722
|
+
reject(err.code === "ETIMEDOUT" ? new CmuxTimeoutError(args.join(" ")) : err);
|
|
4723
|
+
return;
|
|
4724
|
+
}
|
|
4725
|
+
resolve2(stdout.trim());
|
|
4726
|
+
});
|
|
4727
|
+
child.stdin.end(input);
|
|
4728
|
+
});
|
|
4729
|
+
}
|
|
4661
4730
|
function parseList(output) {
|
|
4662
4731
|
let parsed;
|
|
4663
4732
|
try {
|
|
@@ -5016,9 +5085,9 @@ function createCmuxDriver() {
|
|
|
5016
5085
|
}
|
|
5017
5086
|
const draft = parseDraftFromScreen(screen);
|
|
5018
5087
|
if (draft === null)
|
|
5019
|
-
throw new DeferDelivery(null);
|
|
5088
|
+
throw new DeferDelivery(null, "no-box");
|
|
5020
5089
|
if (hasModalOptionList(screen))
|
|
5021
|
-
throw new DeferDelivery(null);
|
|
5090
|
+
throw new DeferDelivery(null, "modal");
|
|
5022
5091
|
if (draft === "") {
|
|
5023
5092
|
await deliver();
|
|
5024
5093
|
return;
|
|
@@ -5059,6 +5128,37 @@ function createCmuxDriver() {
|
|
|
5059
5128
|
}
|
|
5060
5129
|
throw new DeferDelivery(draft);
|
|
5061
5130
|
},
|
|
5131
|
+
async showDiff(opts) {
|
|
5132
|
+
const source = opts.source ?? "branch";
|
|
5133
|
+
const args = ["diff"];
|
|
5134
|
+
if (source === "staged") {
|
|
5135
|
+
args.push("--staged");
|
|
5136
|
+
} else if (source === "unstaged") {
|
|
5137
|
+
args.push("--unstaged");
|
|
5138
|
+
} else {
|
|
5139
|
+
args.push("--branch", "--base", opts.base);
|
|
5140
|
+
if (opts.lastTurn)
|
|
5141
|
+
args.push("--last-turn");
|
|
5142
|
+
}
|
|
5143
|
+
args.push("--cwd", opts.cwd, "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split");
|
|
5144
|
+
if (opts.title)
|
|
5145
|
+
args.push("--title", opts.title);
|
|
5146
|
+
if (opts.focus === false)
|
|
5147
|
+
args.push("--no-focus");
|
|
5148
|
+
else
|
|
5149
|
+
args.push("--focus", "true");
|
|
5150
|
+
await cmux(args);
|
|
5151
|
+
},
|
|
5152
|
+
async showPatch(opts) {
|
|
5153
|
+
const args = ["diff", "-", "--workspace", opts.workspaceId, "--layout", opts.layout ?? "split"];
|
|
5154
|
+
if (opts.title)
|
|
5155
|
+
args.push("--title", opts.title);
|
|
5156
|
+
if (opts.focus === false)
|
|
5157
|
+
args.push("--no-focus");
|
|
5158
|
+
else
|
|
5159
|
+
args.push("--focus", "true");
|
|
5160
|
+
await cmuxStdin(args, opts.patch);
|
|
5161
|
+
},
|
|
5062
5162
|
async listSurfaces(workspaceId) {
|
|
5063
5163
|
let output;
|
|
5064
5164
|
try {
|
|
@@ -5648,6 +5748,7 @@ function installClaudeHooks(opts = {}) {
|
|
|
5648
5748
|
});
|
|
5649
5749
|
let settings = {};
|
|
5650
5750
|
const raw = readFile6(settingsPath);
|
|
5751
|
+
const hadExistingSettings = raw !== void 0;
|
|
5651
5752
|
if (raw) {
|
|
5652
5753
|
try {
|
|
5653
5754
|
settings = JSON.parse(raw);
|
|
@@ -5660,6 +5761,7 @@ function installClaudeHooks(opts = {}) {
|
|
|
5660
5761
|
}
|
|
5661
5762
|
const hooks = settings.hooks;
|
|
5662
5763
|
let changed = false;
|
|
5764
|
+
const repaired = [];
|
|
5663
5765
|
for (const [eventName, sub, matcher] of CLAUDE_HOOK_EVENTS) {
|
|
5664
5766
|
if (!Array.isArray(hooks[eventName])) {
|
|
5665
5767
|
hooks[eventName] = [];
|
|
@@ -5671,6 +5773,26 @@ function installClaudeHooks(opts = {}) {
|
|
|
5671
5773
|
if (!alreadyPresent) {
|
|
5672
5774
|
entries.push({ matcher: hookMatcher, hooks: [{ type: "command", command, timeout: 10 }] });
|
|
5673
5775
|
changed = true;
|
|
5776
|
+
repaired.push(`${eventName}/${sub}`);
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
if (repaired.length > 0 && hadExistingSettings) {
|
|
5780
|
+
log(`native-hook: repaired ${repaired.length} missing squadrant hook(s) in ${settingsPath} [${repaired.join(", ")}] \u2014 WARNING: blocked-signalling or lifecycle tracking may have been broken until this run`);
|
|
5781
|
+
}
|
|
5782
|
+
if (opts.claudeEnv && Object.keys(opts.claudeEnv).length > 0) {
|
|
5783
|
+
if (typeof settings.env !== "object" || settings.env === null || Array.isArray(settings.env)) {
|
|
5784
|
+
settings.env = {};
|
|
5785
|
+
}
|
|
5786
|
+
const env = settings.env;
|
|
5787
|
+
for (const [key, value] of Object.entries(opts.claudeEnv)) {
|
|
5788
|
+
if (key in env) {
|
|
5789
|
+
if (env[key] !== value) {
|
|
5790
|
+
log(`native-hook: claudeEnv key '${key}' already set to '${String(env[key])}' in ${settingsPath} \u2014 not overwriting with '${value}'`);
|
|
5791
|
+
}
|
|
5792
|
+
continue;
|
|
5793
|
+
}
|
|
5794
|
+
env[key] = value;
|
|
5795
|
+
changed = true;
|
|
5674
5796
|
}
|
|
5675
5797
|
}
|
|
5676
5798
|
if (changed) {
|
|
@@ -5707,9 +5829,9 @@ var NativeHookSource = class {
|
|
|
5707
5829
|
cache = /* @__PURE__ */ new Map();
|
|
5708
5830
|
active = false;
|
|
5709
5831
|
constructor(opts = {}) {
|
|
5710
|
-
this.hookInstall = opts.hookInstall ?? {};
|
|
5711
5832
|
this.log = opts.log ?? (() => {
|
|
5712
5833
|
});
|
|
5834
|
+
this.hookInstall = { log: this.log, ...opts.hookInstall };
|
|
5713
5835
|
}
|
|
5714
5836
|
start(deps) {
|
|
5715
5837
|
this.deps = deps;
|
|
@@ -6021,7 +6143,7 @@ function startSquadrantd(opts = {}) {
|
|
|
6021
6143
|
log
|
|
6022
6144
|
});
|
|
6023
6145
|
const cmuxStoreSource = new CmuxStoreSource({ log });
|
|
6024
|
-
const nativeHookSource = new NativeHookSource({ log });
|
|
6146
|
+
const nativeHookSource = new NativeHookSource({ log, hookInstall: { claudeEnv: loadConfig().defaults.claudeEnv } });
|
|
6025
6147
|
ctx.codexDriver = codexDriver;
|
|
6026
6148
|
ctx.opencodeBridge = opencodeBridge;
|
|
6027
6149
|
ctx.cmuxEventsBridge = cmuxEventsBridge;
|