squadrant 0.12.1 → 0.13.1
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 +764 -79
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +582 -10
- package/dist/squadrantd.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -622,8 +622,41 @@ function resolveWorktreeBase(repoRoot, fallback = "develop") {
|
|
|
622
622
|
return fallback;
|
|
623
623
|
}
|
|
624
624
|
function addWorktree(spec) {
|
|
625
|
-
const
|
|
626
|
-
|
|
625
|
+
const originalBranch = crewBranch(spec.name);
|
|
626
|
+
let targetName = spec.name;
|
|
627
|
+
let targetBranch = originalBranch;
|
|
628
|
+
let branchExists = false;
|
|
629
|
+
try {
|
|
630
|
+
execFileSync2("git", ["-C", spec.repoRoot, "show-ref", "--verify", "--quiet", `refs/heads/${originalBranch}`], { stdio: "pipe" });
|
|
631
|
+
branchExists = true;
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
634
|
+
if (branchExists) {
|
|
635
|
+
const log = execFileSync2("git", ["-C", spec.repoRoot, "log", "--oneline", `${spec.base}..${originalBranch}`], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
636
|
+
if (!log) {
|
|
637
|
+
execFileSync2("git", ["-C", spec.repoRoot, "branch", "-D", originalBranch], { stdio: "pipe" });
|
|
638
|
+
} else {
|
|
639
|
+
let suffix = 2;
|
|
640
|
+
while (true) {
|
|
641
|
+
const candidate = `${spec.name}-${suffix}`;
|
|
642
|
+
const candidateBranch = crewBranch(candidate);
|
|
643
|
+
let candidateExists = false;
|
|
644
|
+
try {
|
|
645
|
+
execFileSync2("git", ["-C", spec.repoRoot, "show-ref", "--verify", "--quiet", `refs/heads/${candidateBranch}`], { stdio: "pipe" });
|
|
646
|
+
candidateExists = true;
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
if (!candidateExists) {
|
|
650
|
+
targetName = candidate;
|
|
651
|
+
targetBranch = candidateBranch;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
suffix++;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
const wt = worktreePath(spec.repoRoot, spec.worktreeDir, spec.project, targetName);
|
|
659
|
+
execFileSync2("git", ["-C", spec.repoRoot, "worktree", "add", wt, "-b", targetBranch, spec.base], { stdio: "pipe" });
|
|
627
660
|
return wt;
|
|
628
661
|
}
|
|
629
662
|
function removeWorktree(repoRoot, wtPath) {
|
|
@@ -2700,6 +2733,12 @@ var init_crew_spawn = __esm({
|
|
|
2700
2733
|
}
|
|
2701
2734
|
});
|
|
2702
2735
|
|
|
2736
|
+
// packages/core/dist/lifecycle-source.js
|
|
2737
|
+
var init_lifecycle_source = __esm({
|
|
2738
|
+
"packages/core/dist/lifecycle-source.js"() {
|
|
2739
|
+
}
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2703
2742
|
// packages/core/dist/index.js
|
|
2704
2743
|
var init_dist2 = __esm({
|
|
2705
2744
|
"packages/core/dist/index.js"() {
|
|
@@ -2732,6 +2771,7 @@ var init_dist2 = __esm({
|
|
|
2732
2771
|
init_launch_workspace();
|
|
2733
2772
|
init_side_session();
|
|
2734
2773
|
init_crew_spawn();
|
|
2774
|
+
init_lifecycle_source();
|
|
2735
2775
|
}
|
|
2736
2776
|
});
|
|
2737
2777
|
|
|
@@ -3561,17 +3601,361 @@ var init_daemon_cmux = __esm({
|
|
|
3561
3601
|
}
|
|
3562
3602
|
});
|
|
3563
3603
|
|
|
3604
|
+
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
3605
|
+
import { join as join13 } from "path";
|
|
3606
|
+
import { homedir as homedir8 } from "os";
|
|
3607
|
+
import { watch, readdirSync as readdirSync3, readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3608
|
+
function parseLifecycleState(s) {
|
|
3609
|
+
if (s === "running" || s === "idle" || s === "needsInput" || s === "unknown") {
|
|
3610
|
+
return s;
|
|
3611
|
+
}
|
|
3612
|
+
return "unknown";
|
|
3613
|
+
}
|
|
3614
|
+
function defaultIsPidAlive(pid) {
|
|
3615
|
+
try {
|
|
3616
|
+
process.kill(pid, 0);
|
|
3617
|
+
return true;
|
|
3618
|
+
} catch {
|
|
3619
|
+
return false;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
function defaultListFiles(dir) {
|
|
3623
|
+
try {
|
|
3624
|
+
return readdirSync3(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
3625
|
+
} catch {
|
|
3626
|
+
return [];
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
function defaultReadFile(path29) {
|
|
3630
|
+
try {
|
|
3631
|
+
return readFileSync8(path29, "utf-8");
|
|
3632
|
+
} catch {
|
|
3633
|
+
return void 0;
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
function defaultWatchDir(dir, cb) {
|
|
3637
|
+
const w = watch(dir, (_event, filename) => {
|
|
3638
|
+
if (typeof filename === "string" && filename.endsWith("-hook-sessions.json")) {
|
|
3639
|
+
cb();
|
|
3640
|
+
}
|
|
3641
|
+
});
|
|
3642
|
+
return () => w.close();
|
|
3643
|
+
}
|
|
3644
|
+
var CmuxStoreSource;
|
|
3645
|
+
var init_cmux_store_source = __esm({
|
|
3646
|
+
"packages/workspaces/dist/cmux-daemon/cmux-store-source.js"() {
|
|
3647
|
+
CmuxStoreSource = class {
|
|
3648
|
+
name = "cmux-store";
|
|
3649
|
+
stateDir;
|
|
3650
|
+
debounceMs;
|
|
3651
|
+
isPidAlive;
|
|
3652
|
+
listFiles;
|
|
3653
|
+
readFile;
|
|
3654
|
+
fileExists;
|
|
3655
|
+
watchDir;
|
|
3656
|
+
scheduleTimer;
|
|
3657
|
+
cancelTimer;
|
|
3658
|
+
log;
|
|
3659
|
+
deps;
|
|
3660
|
+
stopWatcher;
|
|
3661
|
+
debounceTimer;
|
|
3662
|
+
/** taskId → last reported snapshot (for snapshot() liveness floor). */
|
|
3663
|
+
cache = /* @__PURE__ */ new Map();
|
|
3664
|
+
constructor(opts = {}) {
|
|
3665
|
+
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join13(homedir8(), ".cmuxterm");
|
|
3666
|
+
this.debounceMs = opts.debounceMs ?? 50;
|
|
3667
|
+
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
3668
|
+
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
3669
|
+
this.readFile = opts.readFile ?? defaultReadFile;
|
|
3670
|
+
this.fileExists = opts.fileExists ?? existsSync10;
|
|
3671
|
+
this.watchDir = opts.watchDir ?? defaultWatchDir;
|
|
3672
|
+
this.scheduleTimer = opts.scheduleTimer ?? setTimeout;
|
|
3673
|
+
this.cancelTimer = opts.cancelTimer ?? clearTimeout;
|
|
3674
|
+
this.log = opts.log ?? (() => {
|
|
3675
|
+
});
|
|
3676
|
+
}
|
|
3677
|
+
start(deps) {
|
|
3678
|
+
this.deps = deps;
|
|
3679
|
+
this.scan();
|
|
3680
|
+
try {
|
|
3681
|
+
this.stopWatcher = this.watchDir(this.stateDir, () => this.scheduleDebounced());
|
|
3682
|
+
} catch (e) {
|
|
3683
|
+
this.log(`cmux-store: failed to watch ${this.stateDir}: ${e.message}`);
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
stop() {
|
|
3687
|
+
if (this.debounceTimer !== void 0) {
|
|
3688
|
+
this.cancelTimer(this.debounceTimer);
|
|
3689
|
+
this.debounceTimer = void 0;
|
|
3690
|
+
}
|
|
3691
|
+
this.stopWatcher?.();
|
|
3692
|
+
this.stopWatcher = void 0;
|
|
3693
|
+
this.deps = void 0;
|
|
3694
|
+
this.cache.clear();
|
|
3695
|
+
}
|
|
3696
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor). */
|
|
3697
|
+
snapshot(taskId) {
|
|
3698
|
+
return this.cache.get(taskId);
|
|
3699
|
+
}
|
|
3700
|
+
// ── private ─────────────────────────────────────────────────────────────────
|
|
3701
|
+
scheduleDebounced() {
|
|
3702
|
+
if (this.debounceTimer !== void 0) {
|
|
3703
|
+
this.cancelTimer(this.debounceTimer);
|
|
3704
|
+
}
|
|
3705
|
+
this.debounceTimer = this.scheduleTimer(() => {
|
|
3706
|
+
this.debounceTimer = void 0;
|
|
3707
|
+
this.scan();
|
|
3708
|
+
}, this.debounceMs);
|
|
3709
|
+
}
|
|
3710
|
+
scan() {
|
|
3711
|
+
if (!this.deps)
|
|
3712
|
+
return;
|
|
3713
|
+
for (const filename of this.listFiles(this.stateDir)) {
|
|
3714
|
+
this.scanFile(filename);
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
scanFile(filename) {
|
|
3718
|
+
const deps = this.deps;
|
|
3719
|
+
const filePath = join13(this.stateDir, filename);
|
|
3720
|
+
const lockPath = `${filePath}.lock`;
|
|
3721
|
+
if (this.fileExists(lockPath)) {
|
|
3722
|
+
this.log(`cmux-store: skipping ${filename} (locked)`);
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
const raw = this.readFile(filePath);
|
|
3726
|
+
if (!raw)
|
|
3727
|
+
return;
|
|
3728
|
+
let parsed;
|
|
3729
|
+
try {
|
|
3730
|
+
parsed = JSON.parse(raw);
|
|
3731
|
+
} catch {
|
|
3732
|
+
this.log(`cmux-store: failed to parse ${filename}`);
|
|
3733
|
+
return;
|
|
3734
|
+
}
|
|
3735
|
+
for (const session of Object.values(parsed.sessions ?? {})) {
|
|
3736
|
+
this.processSession(session, deps);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
processSession(session, deps) {
|
|
3740
|
+
if (!session.sessionId || !session.cwd || typeof session.pid !== "number")
|
|
3741
|
+
return;
|
|
3742
|
+
const hint = {
|
|
3743
|
+
cwd: session.cwd,
|
|
3744
|
+
pid: session.pid,
|
|
3745
|
+
sessionId: session.sessionId
|
|
3746
|
+
};
|
|
3747
|
+
const resolved = deps.resolve(hint);
|
|
3748
|
+
if (!resolved)
|
|
3749
|
+
return;
|
|
3750
|
+
let alive = this.isPidAlive(session.pid);
|
|
3751
|
+
if (!alive && session.isRestorable === true && session.agentLifecycle === "idle") {
|
|
3752
|
+
alive = true;
|
|
3753
|
+
}
|
|
3754
|
+
const snap = {
|
|
3755
|
+
taskId: resolved.id,
|
|
3756
|
+
state: parseLifecycleState(session.agentLifecycle),
|
|
3757
|
+
alive,
|
|
3758
|
+
// "agent": the store carries the agent's own reported lifecycle state,
|
|
3759
|
+
// not a scan inference. needsInput from the store is authoritative.
|
|
3760
|
+
origin: "agent",
|
|
3761
|
+
at: Math.floor((session.updatedAt ?? 0) * 1e3),
|
|
3762
|
+
pid: session.pid,
|
|
3763
|
+
...session.lastBody ? { detail: { note: session.lastBody } } : {}
|
|
3764
|
+
};
|
|
3765
|
+
this.cache.set(resolved.id, snap);
|
|
3766
|
+
deps.report(snap);
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3770
|
+
});
|
|
3771
|
+
|
|
3772
|
+
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
3773
|
+
import { join as join14 } from "path";
|
|
3774
|
+
import { homedir as homedir9 } from "os";
|
|
3775
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
3776
|
+
function installClaudeHooks(opts = {}) {
|
|
3777
|
+
const settingsPath = opts.settingsPath ?? join14(homedir9(), ".claude", "settings.json");
|
|
3778
|
+
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
3779
|
+
const readFile6 = opts.readFile ?? defaultReadFile2;
|
|
3780
|
+
const writeFile5 = opts.writeFile ?? defaultWriteFile;
|
|
3781
|
+
const log = opts.log ?? (() => {
|
|
3782
|
+
});
|
|
3783
|
+
let settings = {};
|
|
3784
|
+
const raw = readFile6(settingsPath);
|
|
3785
|
+
if (raw) {
|
|
3786
|
+
try {
|
|
3787
|
+
settings = JSON.parse(raw);
|
|
3788
|
+
} catch {
|
|
3789
|
+
log(`native-hook: failed to parse ${settingsPath} \u2014 hooks section will be reset`);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
if (typeof settings.hooks !== "object" || settings.hooks === null || Array.isArray(settings.hooks)) {
|
|
3793
|
+
settings.hooks = {};
|
|
3794
|
+
}
|
|
3795
|
+
const hooks = settings.hooks;
|
|
3796
|
+
let changed = false;
|
|
3797
|
+
for (const [eventName, sub, matcher] of CLAUDE_HOOK_EVENTS) {
|
|
3798
|
+
if (!Array.isArray(hooks[eventName])) {
|
|
3799
|
+
hooks[eventName] = [];
|
|
3800
|
+
}
|
|
3801
|
+
const entries = hooks[eventName];
|
|
3802
|
+
const command = `${hookCmd} claude ${sub}`;
|
|
3803
|
+
const hookMatcher = matcher ?? "";
|
|
3804
|
+
const alreadyPresent = entries.some((m) => Array.isArray(m.hooks) && m.hooks.some((h) => typeof h.command === "string" && h.command === command));
|
|
3805
|
+
if (!alreadyPresent) {
|
|
3806
|
+
entries.push({ matcher: hookMatcher, hooks: [{ type: "command", command, timeout: 10 }] });
|
|
3807
|
+
changed = true;
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
if (changed) {
|
|
3811
|
+
writeFile5(settingsPath, JSON.stringify(settings, null, 2));
|
|
3812
|
+
}
|
|
3813
|
+
return settingsPath;
|
|
3814
|
+
}
|
|
3815
|
+
function mapSubToLifecycle(sub) {
|
|
3816
|
+
switch (sub) {
|
|
3817
|
+
case "session-start":
|
|
3818
|
+
return "running";
|
|
3819
|
+
case "prompt-submit":
|
|
3820
|
+
return "running";
|
|
3821
|
+
case "pre-tool-use":
|
|
3822
|
+
return "running";
|
|
3823
|
+
case "stop":
|
|
3824
|
+
return "idle";
|
|
3825
|
+
case "notification":
|
|
3826
|
+
return "needsInput";
|
|
3827
|
+
case "ask-question":
|
|
3828
|
+
return "needsInput";
|
|
3829
|
+
case "session-end":
|
|
3830
|
+
return "session-end";
|
|
3831
|
+
default:
|
|
3832
|
+
return null;
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
function extractDetail(sub, payload) {
|
|
3836
|
+
if (!payload || typeof payload !== "object")
|
|
3837
|
+
return void 0;
|
|
3838
|
+
const p = payload;
|
|
3839
|
+
if (sub === "notification") {
|
|
3840
|
+
const note = typeof p.message === "string" ? p.message : void 0;
|
|
3841
|
+
return note ? { note } : void 0;
|
|
3842
|
+
}
|
|
3843
|
+
if (sub === "pre-tool-use") {
|
|
3844
|
+
const tool = typeof p.tool_name === "string" ? p.tool_name : void 0;
|
|
3845
|
+
return tool ? { tool } : void 0;
|
|
3846
|
+
}
|
|
3847
|
+
return void 0;
|
|
3848
|
+
}
|
|
3849
|
+
function defaultReadFile2(path29) {
|
|
3850
|
+
try {
|
|
3851
|
+
return readFileSync9(path29, "utf-8");
|
|
3852
|
+
} catch {
|
|
3853
|
+
return void 0;
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
function defaultWriteFile(path29, content) {
|
|
3857
|
+
mkdirSync6(path29.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
3858
|
+
writeFileSync7(path29, content, "utf-8");
|
|
3859
|
+
}
|
|
3860
|
+
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, NativeHookSource;
|
|
3861
|
+
var init_native_hook_source = __esm({
|
|
3862
|
+
"packages/workspaces/dist/native-hooks/native-hook-source.js"() {
|
|
3863
|
+
CLAUDE_HOOK_EVENTS = [
|
|
3864
|
+
["SessionStart", "session-start"],
|
|
3865
|
+
["UserPromptSubmit", "prompt-submit"],
|
|
3866
|
+
["PreToolUse", "pre-tool-use"],
|
|
3867
|
+
["Stop", "stop"],
|
|
3868
|
+
["Notification", "notification"],
|
|
3869
|
+
["PreToolUse", "ask-question", "AskUserQuestion"],
|
|
3870
|
+
["SessionEnd", "session-end"]
|
|
3871
|
+
];
|
|
3872
|
+
DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
3873
|
+
NativeHookSource = class {
|
|
3874
|
+
name = "native-hook";
|
|
3875
|
+
hookInstall;
|
|
3876
|
+
log;
|
|
3877
|
+
deps;
|
|
3878
|
+
/** taskId → last-reported snapshot, for snapshot() liveness floor. */
|
|
3879
|
+
cache = /* @__PURE__ */ new Map();
|
|
3880
|
+
constructor(opts = {}) {
|
|
3881
|
+
this.hookInstall = opts.hookInstall ?? {};
|
|
3882
|
+
this.log = opts.log ?? (() => {
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3885
|
+
start(deps) {
|
|
3886
|
+
this.deps = deps;
|
|
3887
|
+
}
|
|
3888
|
+
stop() {
|
|
3889
|
+
this.deps = void 0;
|
|
3890
|
+
this.cache.clear();
|
|
3891
|
+
}
|
|
3892
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor poll). */
|
|
3893
|
+
snapshot(taskId) {
|
|
3894
|
+
return this.cache.get(taskId);
|
|
3895
|
+
}
|
|
3896
|
+
/**
|
|
3897
|
+
* Install squadrant-owned hooks into ~/.claude/settings.json.
|
|
3898
|
+
* Idempotent — safe to call on every project init or crew spawn.
|
|
3899
|
+
* Returns the path to the settings file.
|
|
3900
|
+
*/
|
|
3901
|
+
install() {
|
|
3902
|
+
return installClaudeHooks(this.hookInstall);
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Receive a lifecycle hook event from the daemon and report a LifecycleSnapshot.
|
|
3906
|
+
*
|
|
3907
|
+
* The daemon's 'squadrant hooks claude <sub>' CLI subcommand calls this after
|
|
3908
|
+
* reading SQUADRANT_CREW_TASK_ID from the hook's process environment — the only
|
|
3909
|
+
* collision-proof correlation key (blueprint §2.2 priority 1).
|
|
3910
|
+
*
|
|
3911
|
+
* @param sub Sub-event alias: "session-start" | "prompt-submit" | "stop" | …
|
|
3912
|
+
* @param taskId SQUADRANT_CREW_TASK_ID extracted from the hook process env.
|
|
3913
|
+
* @param pid Optional: OS pid from the hook's process env or argv.
|
|
3914
|
+
* @param payload Optional: parsed JSON payload from hook stdin (best-effort detail).
|
|
3915
|
+
*/
|
|
3916
|
+
handleHook(sub, taskId, pid, payload) {
|
|
3917
|
+
if (!this.deps)
|
|
3918
|
+
return;
|
|
3919
|
+
const mapped = mapSubToLifecycle(sub);
|
|
3920
|
+
if (mapped === null) {
|
|
3921
|
+
this.log(`native-hook: unknown sub '${sub}' for task ${taskId} \u2014 ignored`);
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
const isSessionEnd = mapped === "session-end";
|
|
3925
|
+
const state = isSessionEnd ? "unknown" : mapped;
|
|
3926
|
+
const detail = extractDetail(sub, payload);
|
|
3927
|
+
const snap = {
|
|
3928
|
+
taskId,
|
|
3929
|
+
state,
|
|
3930
|
+
alive: !isSessionEnd,
|
|
3931
|
+
origin: "agent",
|
|
3932
|
+
at: Date.now(),
|
|
3933
|
+
...pid !== void 0 ? { pid } : {},
|
|
3934
|
+
...detail ? { detail } : {}
|
|
3935
|
+
};
|
|
3936
|
+
this.cache.set(taskId, snap);
|
|
3937
|
+
this.deps.report(snap);
|
|
3938
|
+
}
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
});
|
|
3942
|
+
|
|
3564
3943
|
// packages/workspaces/dist/crew-pane.js
|
|
3565
3944
|
import net from "net";
|
|
3566
3945
|
async function settleInputBox(runtime, pane) {
|
|
3567
3946
|
let prev = await runtime.readPaneScreen(pane) ?? "";
|
|
3947
|
+
let sawContent = parseDraftFromScreen(prev) !== "" && parseDraftFromScreen(prev) !== null;
|
|
3568
3948
|
for (let i = 0; i < SETTLE_MAX_POLLS; i++) {
|
|
3569
3949
|
await new Promise((r) => setTimeout(r, SETTLE_POLL_MS));
|
|
3570
3950
|
const cur = await runtime.readPaneScreen(pane) ?? "";
|
|
3951
|
+
const draft = parseDraftFromScreen(cur);
|
|
3952
|
+
if (draft !== "" && draft !== null)
|
|
3953
|
+
sawContent = true;
|
|
3571
3954
|
if (cur === prev)
|
|
3572
|
-
return;
|
|
3955
|
+
return sawContent;
|
|
3573
3956
|
prev = cur;
|
|
3574
3957
|
}
|
|
3958
|
+
return sawContent;
|
|
3575
3959
|
}
|
|
3576
3960
|
function getFreePort() {
|
|
3577
3961
|
return new Promise((resolve3, reject) => {
|
|
@@ -3609,17 +3993,26 @@ async function resolveCaptainWorkspace(project) {
|
|
|
3609
3993
|
async function confirmedSendToPane(runtime, pane, message) {
|
|
3610
3994
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3611
3995
|
await runtime.pasteToPane(pane, message);
|
|
3612
|
-
await settleInputBox(runtime, pane);
|
|
3996
|
+
let sawDraft = await settleInputBox(runtime, pane);
|
|
3613
3997
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
3998
|
+
let repasted = false;
|
|
3614
3999
|
for (let attempt = 0; attempt < SUBMIT_RETRY_LIMIT; attempt++) {
|
|
3615
4000
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3616
4001
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3617
4002
|
const draft = parseDraftFromScreen(afterScreen);
|
|
3618
|
-
if (draft
|
|
4003
|
+
if (draft !== "" && draft !== null)
|
|
4004
|
+
sawDraft = true;
|
|
4005
|
+
if (draft === "" && sawDraft)
|
|
3619
4006
|
return;
|
|
3620
4007
|
if (draft === null && afterScreen !== preSendScreen)
|
|
3621
4008
|
return;
|
|
3622
|
-
await settleInputBox(runtime, pane);
|
|
4009
|
+
const settled = await settleInputBox(runtime, pane);
|
|
4010
|
+
if (settled)
|
|
4011
|
+
sawDraft = true;
|
|
4012
|
+
if (!sawDraft && !repasted) {
|
|
4013
|
+
repasted = true;
|
|
4014
|
+
await runtime.pasteToPane(pane, message);
|
|
4015
|
+
}
|
|
3623
4016
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
3624
4017
|
}
|
|
3625
4018
|
}
|
|
@@ -3653,18 +4046,27 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
3653
4046
|
return;
|
|
3654
4047
|
}
|
|
3655
4048
|
await runtime.pasteToPane(pane, task);
|
|
3656
|
-
await settleInputBox(runtime, pane);
|
|
4049
|
+
let sawDraft = await settleInputBox(runtime, pane);
|
|
3657
4050
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
3658
4051
|
const retryLimit = acceptanceConfig?.retryLimit ?? SUBMIT_RETRY_LIMIT;
|
|
4052
|
+
let repasted = false;
|
|
3659
4053
|
for (let attempt = 0; attempt < retryLimit; attempt++) {
|
|
3660
4054
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3661
4055
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3662
4056
|
const draft = parseDraftFromScreen(afterScreen);
|
|
3663
|
-
if (draft
|
|
4057
|
+
if (draft !== "" && draft !== null)
|
|
4058
|
+
sawDraft = true;
|
|
4059
|
+
if (draft === "" && sawDraft)
|
|
3664
4060
|
return;
|
|
3665
4061
|
if (draft === null && afterScreen !== preSendScreen)
|
|
3666
4062
|
return;
|
|
3667
|
-
await settleInputBox(runtime, pane);
|
|
4063
|
+
const settled = await settleInputBox(runtime, pane);
|
|
4064
|
+
if (settled)
|
|
4065
|
+
sawDraft = true;
|
|
4066
|
+
if (!sawDraft && !repasted) {
|
|
4067
|
+
repasted = true;
|
|
4068
|
+
await runtime.pasteToPane(pane, task);
|
|
4069
|
+
}
|
|
3668
4070
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
3669
4071
|
}
|
|
3670
4072
|
}
|
|
@@ -3692,7 +4094,9 @@ var dist_exports = {};
|
|
|
3692
4094
|
__export(dist_exports, {
|
|
3693
4095
|
CMUX_TIMEOUT: () => CMUX_TIMEOUT,
|
|
3694
4096
|
CmuxEventsBridge: () => CmuxEventsBridge,
|
|
4097
|
+
CmuxStoreSource: () => CmuxStoreSource,
|
|
3695
4098
|
DaemonCmux: () => DaemonCmux,
|
|
4099
|
+
NativeHookSource: () => NativeHookSource,
|
|
3696
4100
|
NotifierRegistry: () => NotifierRegistry,
|
|
3697
4101
|
RuntimeRegistry: () => RuntimeRegistry,
|
|
3698
4102
|
WorkspaceRegistry: () => WorkspaceRegistry,
|
|
@@ -3705,8 +4109,10 @@ __export(dist_exports, {
|
|
|
3705
4109
|
deriveRunState: () => deriveRunState,
|
|
3706
4110
|
findCrew: () => findCrew,
|
|
3707
4111
|
getFreePort: () => getFreePort,
|
|
4112
|
+
installClaudeHooks: () => installClaudeHooks,
|
|
3708
4113
|
isInsideCmux: () => isInsideCmux,
|
|
3709
4114
|
listProjectCrews: () => listProjectCrews,
|
|
4115
|
+
mapSubToLifecycle: () => mapSubToLifecycle,
|
|
3710
4116
|
resolveCaptainWorkspace: () => resolveCaptainWorkspace,
|
|
3711
4117
|
sendFirstTurnWhenReady: () => sendFirstTurnWhenReady
|
|
3712
4118
|
});
|
|
@@ -3717,6 +4123,8 @@ var init_dist3 = __esm({
|
|
|
3717
4123
|
init_workspaces2();
|
|
3718
4124
|
init_events_bridge();
|
|
3719
4125
|
init_daemon_cmux();
|
|
4126
|
+
init_cmux_store_source();
|
|
4127
|
+
init_native_hook_source();
|
|
3720
4128
|
init_crew_pane();
|
|
3721
4129
|
}
|
|
3722
4130
|
});
|
|
@@ -4646,13 +5054,99 @@ var init_app_server_client = __esm({
|
|
|
4646
5054
|
}
|
|
4647
5055
|
});
|
|
4648
5056
|
|
|
5057
|
+
// packages/agents/dist/codex/codex-app-server-source.js
|
|
5058
|
+
function toSnapshot(ev) {
|
|
5059
|
+
const now = Date.now();
|
|
5060
|
+
switch (ev.type) {
|
|
5061
|
+
// ── running: a turn is live ──────────────────────────────────────────────
|
|
5062
|
+
case "task.started":
|
|
5063
|
+
case "task.reattached":
|
|
5064
|
+
case "task.turn.started":
|
|
5065
|
+
case "task.delta":
|
|
5066
|
+
case "task.progress":
|
|
5067
|
+
return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
|
|
5068
|
+
// ── idle: turn ended, crew alive, awaiting next input ────────────────────
|
|
5069
|
+
// task.failed: the turn ended with an error, but the crew process is alive.
|
|
5070
|
+
// task.session.ended: process is gone (alive:false) — signals liveness loss.
|
|
5071
|
+
case "task.turn.completed":
|
|
5072
|
+
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
5073
|
+
case "task.failed":
|
|
5074
|
+
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
5075
|
+
case "task.session.ended":
|
|
5076
|
+
return { taskId: ev.id, state: "idle", alive: false, origin: "agent", at: now };
|
|
5077
|
+
// ── needsInput: crew is blocked on a human ───────────────────────────────
|
|
5078
|
+
case "task.approval.requested":
|
|
5079
|
+
return {
|
|
5080
|
+
taskId: ev.id,
|
|
5081
|
+
state: "needsInput",
|
|
5082
|
+
alive: true,
|
|
5083
|
+
origin: "agent",
|
|
5084
|
+
at: now,
|
|
5085
|
+
detail: { note: ev.question, reason: ev.kind }
|
|
5086
|
+
};
|
|
5087
|
+
case "task.input.requested":
|
|
5088
|
+
return {
|
|
5089
|
+
taskId: ev.id,
|
|
5090
|
+
state: "needsInput",
|
|
5091
|
+
alive: true,
|
|
5092
|
+
origin: "agent",
|
|
5093
|
+
at: now,
|
|
5094
|
+
detail: { note: ev.question }
|
|
5095
|
+
};
|
|
5096
|
+
// ── terminal / notify-only — ignored ────────────────────────────────────
|
|
5097
|
+
// task.done, task.blocked, task.cancelled: terminal state from crew signal only.
|
|
5098
|
+
// task.session, task.stalled, task.quiet, task.idle, task.timeout, etc.: no-op.
|
|
5099
|
+
default:
|
|
5100
|
+
return null;
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
var CodexAppServerSource;
|
|
5104
|
+
var init_codex_app_server_source = __esm({
|
|
5105
|
+
"packages/agents/dist/codex/codex-app-server-source.js"() {
|
|
5106
|
+
CodexAppServerSource = class {
|
|
5107
|
+
name = "codex-appserver";
|
|
5108
|
+
deps;
|
|
5109
|
+
/** taskId → last reported snapshot (for snapshot() liveness floor). */
|
|
5110
|
+
cache = /* @__PURE__ */ new Map();
|
|
5111
|
+
start(deps) {
|
|
5112
|
+
this.deps = deps;
|
|
5113
|
+
}
|
|
5114
|
+
stop() {
|
|
5115
|
+
this.deps = void 0;
|
|
5116
|
+
this.cache.clear();
|
|
5117
|
+
}
|
|
5118
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor). */
|
|
5119
|
+
snapshot(taskId) {
|
|
5120
|
+
return this.cache.get(taskId);
|
|
5121
|
+
}
|
|
5122
|
+
/**
|
|
5123
|
+
* Feed a ControlEvent from CodexInteractiveDriver into this source.
|
|
5124
|
+
* The daemon wires: emit = (ev) => { source.observe(ev); handle(ev); }
|
|
5125
|
+
*
|
|
5126
|
+
* All events that carry lifecycle meaning for a codex crew are mapped to a
|
|
5127
|
+
* LifecycleSnapshot and reported. Events that are terminal signals (task.done,
|
|
5128
|
+
* task.cancelled, task.blocked) or notify-only (task.stalled, task.quiet, etc.)
|
|
5129
|
+
* are ignored — terminal state still comes exclusively from `squadrant crew signal`
|
|
5130
|
+
* (anti-#2576 invariant).
|
|
5131
|
+
*/
|
|
5132
|
+
observe(ev) {
|
|
5133
|
+
const snap = toSnapshot(ev);
|
|
5134
|
+
if (!snap || !this.deps)
|
|
5135
|
+
return;
|
|
5136
|
+
this.cache.set(snap.taskId, snap);
|
|
5137
|
+
this.deps.report(snap);
|
|
5138
|
+
}
|
|
5139
|
+
};
|
|
5140
|
+
}
|
|
5141
|
+
});
|
|
5142
|
+
|
|
4649
5143
|
// packages/agents/dist/codex/config.js
|
|
4650
5144
|
import { readFile as readFile5 } from "fs/promises";
|
|
4651
|
-
import { homedir as
|
|
4652
|
-
import { join as
|
|
5145
|
+
import { homedir as homedir11 } from "os";
|
|
5146
|
+
import { join as join16 } from "path";
|
|
4653
5147
|
async function resolveCodexModel() {
|
|
4654
|
-
const home = process.env["CODEX_HOME"] ??
|
|
4655
|
-
const configPath =
|
|
5148
|
+
const home = process.env["CODEX_HOME"] ?? join16(homedir11(), ".codex");
|
|
5149
|
+
const configPath = join16(home, "config.toml");
|
|
4656
5150
|
let text;
|
|
4657
5151
|
try {
|
|
4658
5152
|
text = await readFile5(configPath, "utf8");
|
|
@@ -5173,9 +5667,9 @@ var init_sse_bridge = __esm({
|
|
|
5173
5667
|
|
|
5174
5668
|
// packages/agents/dist/interactive/claude.js
|
|
5175
5669
|
import { execSync as execSync7 } from "child_process";
|
|
5176
|
-
import { readFileSync as
|
|
5177
|
-
import { homedir as
|
|
5178
|
-
import { join as
|
|
5670
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
5671
|
+
import { homedir as homedir12 } from "os";
|
|
5672
|
+
import { join as join17 } from "path";
|
|
5179
5673
|
function probeClaudeSettingsFlag() {
|
|
5180
5674
|
try {
|
|
5181
5675
|
const help = execSync7("claude --help", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -5226,11 +5720,11 @@ function deriveTranscriptPath(sessionId, cwd) {
|
|
|
5226
5720
|
if (!sessionId || !cwd)
|
|
5227
5721
|
return null;
|
|
5228
5722
|
const escaped = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5229
|
-
return
|
|
5723
|
+
return join17(homedir12(), ".claude", "projects", escaped, `${sessionId}.jsonl`);
|
|
5230
5724
|
}
|
|
5231
5725
|
function readLastAssistantText(transcriptPath) {
|
|
5232
5726
|
try {
|
|
5233
|
-
const raw =
|
|
5727
|
+
const raw = readFileSync10(transcriptPath, "utf-8");
|
|
5234
5728
|
const lines = raw.split(/\r?\n/);
|
|
5235
5729
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5236
5730
|
const line = lines[i].trim();
|
|
@@ -5347,6 +5841,18 @@ function classifyPaneTail(tail) {
|
|
|
5347
5841
|
}
|
|
5348
5842
|
return { kind: "approval", text: "Crew is awaiting permission approval." };
|
|
5349
5843
|
}
|
|
5844
|
+
const hasPickerFooter = cleaned.some((c) => c != null && PICKER_FOOTER_RE.test(c));
|
|
5845
|
+
if (options.length >= 2 && hasPickerFooter) {
|
|
5846
|
+
const firstOptCi = options[0].ci;
|
|
5847
|
+
for (let i = firstOptCi - 1; i >= 0; i--) {
|
|
5848
|
+
const c = cleaned[i];
|
|
5849
|
+
if (c == null)
|
|
5850
|
+
continue;
|
|
5851
|
+
if (c.endsWith("?"))
|
|
5852
|
+
return { kind: "question", text: c };
|
|
5853
|
+
}
|
|
5854
|
+
return { kind: "question", text: "Crew is awaiting a choice." };
|
|
5855
|
+
}
|
|
5350
5856
|
const region = cleaned.filter((c) => c != null).join("\n");
|
|
5351
5857
|
const q = detectTrailingQuestion(region);
|
|
5352
5858
|
if (q)
|
|
@@ -5356,8 +5862,13 @@ function classifyPaneTail(tail) {
|
|
|
5356
5862
|
if (c != null && ERROR_BANNER_RE.some((re) => re.test(c)))
|
|
5357
5863
|
errLine = c;
|
|
5358
5864
|
}
|
|
5359
|
-
if (errLine)
|
|
5865
|
+
if (errLine) {
|
|
5866
|
+
const isRetrying = cleaned.some((c) => c != null && RETRYING_RE.test(c));
|
|
5867
|
+
const isExhausted = cleaned.some((c) => c != null && EXHAUSTED_RE.test(c));
|
|
5868
|
+
if (isRetrying && !isExhausted)
|
|
5869
|
+
return null;
|
|
5360
5870
|
return { kind: "error", text: errLine.slice(0, 200) };
|
|
5871
|
+
}
|
|
5361
5872
|
return null;
|
|
5362
5873
|
}
|
|
5363
5874
|
function stripChrome(raw) {
|
|
@@ -5374,7 +5885,7 @@ function stripChrome(raw) {
|
|
|
5374
5885
|
return null;
|
|
5375
5886
|
return trimmed;
|
|
5376
5887
|
}
|
|
5377
|
-
var ERROR_BANNER_RE, OPTION_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5888
|
+
var ERROR_BANNER_RE, RETRYING_RE, EXHAUSTED_RE, OPTION_RE, PICKER_FOOTER_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5378
5889
|
var init_pane_classifier = __esm({
|
|
5379
5890
|
"packages/agents/dist/interactive/pane-classifier.js"() {
|
|
5380
5891
|
init_claude2();
|
|
@@ -5387,7 +5898,10 @@ var init_pane_classifier = __esm({
|
|
|
5387
5898
|
/\bretr(?:y|ies)\s+(?:exhausted|limit\s+(?:reached|exceeded))\b/i,
|
|
5388
5899
|
/\bmaximum\s+retries\b/i
|
|
5389
5900
|
];
|
|
5901
|
+
RETRYING_RE = /\bRetrying\b|\battempt\s+\d+\s*\/\s*\d+/i;
|
|
5902
|
+
EXHAUSTED_RE = /\bretr(?:y|ies)\s+(?:exhausted|limit\s+(?:reached|exceeded))\b|\bmaximum\s+retries\b/i;
|
|
5390
5903
|
OPTION_RE = /^[❯>›]?\s*(\d+)\.\s+(.*\S)\s*$/;
|
|
5904
|
+
PICKER_FOOTER_RE = /↑↓\s*select|enter\s+submit|esc\s+dismiss/i;
|
|
5391
5905
|
PURE_CHROME_RE = /^[\s─━│┃╭╮╰╯┌┐└┘├┤┬┴┼═║╔╗╚╝╠╣╦╩╬▁▂▃▄▅▆▇█▔▏▕]+$/;
|
|
5392
5906
|
STATUS_LINE_RE = /accept edits on|shift\+tab|⏵⏵|\? for shortcuts|esc to interrupt|tokens? (used|left)|context left/i;
|
|
5393
5907
|
}
|
|
@@ -5618,6 +6132,7 @@ var dist_exports2 = {};
|
|
|
5618
6132
|
__export(dist_exports2, {
|
|
5619
6133
|
AppServerClient: () => AppServerClient,
|
|
5620
6134
|
CapabilityRegistry: () => CapabilityRegistry,
|
|
6135
|
+
CodexAppServerSource: () => CodexAppServerSource,
|
|
5621
6136
|
CodexInteractiveDriver: () => CodexInteractiveDriver,
|
|
5622
6137
|
HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
|
|
5623
6138
|
MARKER_END: () => MARKER_END,
|
|
@@ -5656,6 +6171,7 @@ var init_dist4 = __esm({
|
|
|
5656
6171
|
init_drivers();
|
|
5657
6172
|
init_projection2();
|
|
5658
6173
|
init_app_server_client();
|
|
6174
|
+
init_codex_app_server_source();
|
|
5659
6175
|
init_driver();
|
|
5660
6176
|
init_normalize();
|
|
5661
6177
|
init_sse_bridge();
|
|
@@ -5672,11 +6188,11 @@ var init_dist4 = __esm({
|
|
|
5672
6188
|
// packages/cli/src/index.ts
|
|
5673
6189
|
init_dist();
|
|
5674
6190
|
init_dist2();
|
|
5675
|
-
import { Command as
|
|
5676
|
-
import { readFileSync as
|
|
6191
|
+
import { Command as Command29 } from "commander";
|
|
6192
|
+
import { readFileSync as readFileSync13, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
5677
6193
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
5678
|
-
import { dirname as dirname7, join as
|
|
5679
|
-
import { homedir as
|
|
6194
|
+
import { dirname as dirname7, join as join26 } from "path";
|
|
6195
|
+
import { homedir as homedir18 } from "os";
|
|
5680
6196
|
|
|
5681
6197
|
// packages/cli/src/commands/doctor.ts
|
|
5682
6198
|
init_dist();
|
|
@@ -5693,10 +6209,10 @@ import chalk3 from "chalk";
|
|
|
5693
6209
|
// packages/cli/src/commands/health-view.ts
|
|
5694
6210
|
init_dist2();
|
|
5695
6211
|
init_dist2();
|
|
5696
|
-
import { homedir as
|
|
5697
|
-
import { join as
|
|
6212
|
+
import { homedir as homedir10 } from "os";
|
|
6213
|
+
import { join as join15 } from "path";
|
|
5698
6214
|
import chalk2 from "chalk";
|
|
5699
|
-
var SOCK =
|
|
6215
|
+
var SOCK = join15(homedir10(), ".config", "squadrant", "squadrant.sock");
|
|
5700
6216
|
async function queryHealth(project) {
|
|
5701
6217
|
try {
|
|
5702
6218
|
const reply = await sendRequest(SOCK, { kind: "health", project });
|
|
@@ -6530,9 +7046,9 @@ init_dist4();
|
|
|
6530
7046
|
import { Command as Command8 } from "commander";
|
|
6531
7047
|
import { createConnection as createConnection3 } from "net";
|
|
6532
7048
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6533
|
-
import { homedir as
|
|
6534
|
-
import { join as
|
|
6535
|
-
import { mkdirSync as
|
|
7049
|
+
import { homedir as homedir14 } from "os";
|
|
7050
|
+
import { join as join19 } from "path";
|
|
7051
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
6536
7052
|
|
|
6537
7053
|
// packages/cli/src/commands/crew-output.ts
|
|
6538
7054
|
function tailLines(text, maxLines = 40, maxBytes = 4096) {
|
|
@@ -6590,11 +7106,11 @@ init_dist2();
|
|
|
6590
7106
|
import { Command as Command6 } from "commander";
|
|
6591
7107
|
import chalk8 from "chalk";
|
|
6592
7108
|
import { createConnection as createConnection2 } from "net";
|
|
6593
|
-
import { homedir as
|
|
6594
|
-
import { join as
|
|
7109
|
+
import { homedir as homedir13 } from "os";
|
|
7110
|
+
import { join as join18 } from "path";
|
|
6595
7111
|
import { createInterface } from "readline";
|
|
6596
7112
|
function socketPath() {
|
|
6597
|
-
return process.env.SQUADRANTD_SOCK ??
|
|
7113
|
+
return process.env.SQUADRANTD_SOCK ?? join18(homedir13(), ".config", "squadrant", "squadrant.sock");
|
|
6598
7114
|
}
|
|
6599
7115
|
function rule(width, ch = "\u2500") {
|
|
6600
7116
|
return ch.repeat(Math.max(0, width));
|
|
@@ -6830,7 +7346,7 @@ var crewChatCommand = new Command7("chat").description("[DEPRECATED] alias for `
|
|
|
6830
7346
|
});
|
|
6831
7347
|
|
|
6832
7348
|
// packages/cli/src/commands/crew-control.ts
|
|
6833
|
-
var SOCK2 =
|
|
7349
|
+
var SOCK2 = join19(homedir14(), ".config", "squadrant", "squadrant.sock");
|
|
6834
7350
|
var CODEX_FIRST_TURN_DELAY_MS = 1500;
|
|
6835
7351
|
async function sendCodexFirstTurn(taskId, text) {
|
|
6836
7352
|
await new Promise((r) => setTimeout(r, CODEX_FIRST_TURN_DELAY_MS));
|
|
@@ -6935,10 +7451,10 @@ function buildSignalRequest(signal, o) {
|
|
|
6935
7451
|
return { kind: "event", project, event };
|
|
6936
7452
|
}
|
|
6937
7453
|
function defaultWriteResult(id, payload) {
|
|
6938
|
-
const dir =
|
|
6939
|
-
|
|
6940
|
-
const file =
|
|
6941
|
-
|
|
7454
|
+
const dir = join19(homedir14(), ".config", "squadrant", "state", "_results");
|
|
7455
|
+
mkdirSync7(dir, { recursive: true });
|
|
7456
|
+
const file = join19(dir, `${id}.txt`);
|
|
7457
|
+
writeFileSync8(file, payload);
|
|
6942
7458
|
return file;
|
|
6943
7459
|
}
|
|
6944
7460
|
function addControlPlaneCrewCommands(crew) {
|
|
@@ -7037,8 +7553,8 @@ addControlPlaneCrewCommands(crewControlCommand);
|
|
|
7037
7553
|
|
|
7038
7554
|
// packages/cli/src/lib/per-crew-settings.ts
|
|
7039
7555
|
init_dist4();
|
|
7040
|
-
import { mkdirSync as
|
|
7041
|
-
import { join as
|
|
7556
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
7557
|
+
import { join as join20 } from "path";
|
|
7042
7558
|
var CREW_PERMISSION_ALLOWLIST = [
|
|
7043
7559
|
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
7044
7560
|
"Bash(git status:*)",
|
|
@@ -7129,24 +7645,24 @@ function mergeCrewPermissions(settings) {
|
|
|
7129
7645
|
return next;
|
|
7130
7646
|
}
|
|
7131
7647
|
function writePerCrewSettingsLocal(o) {
|
|
7132
|
-
const dir =
|
|
7133
|
-
|
|
7134
|
-
const file =
|
|
7648
|
+
const dir = join20(o.projectCwd, ".claude");
|
|
7649
|
+
mkdirSync8(dir, { recursive: true });
|
|
7650
|
+
const file = join20(dir, "settings.local.json");
|
|
7135
7651
|
let existing = {};
|
|
7136
7652
|
try {
|
|
7137
|
-
const raw = healStaleCockpitRefs(
|
|
7653
|
+
const raw = healStaleCockpitRefs(readFileSync11(file, "utf-8"));
|
|
7138
7654
|
existing = JSON.parse(raw);
|
|
7139
7655
|
} catch {
|
|
7140
7656
|
}
|
|
7141
7657
|
const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
|
|
7142
7658
|
const merged = mergeCrewPermissions(withHooks);
|
|
7143
|
-
|
|
7659
|
+
writeFileSync9(file, JSON.stringify(merged, null, 2));
|
|
7144
7660
|
return file;
|
|
7145
7661
|
}
|
|
7146
7662
|
function writePerCrewOpencodeConfig(o) {
|
|
7147
|
-
const dir =
|
|
7148
|
-
|
|
7149
|
-
const file =
|
|
7663
|
+
const dir = join20(o.stateRoot, o.project, o.taskId);
|
|
7664
|
+
mkdirSync8(dir, { recursive: true });
|
|
7665
|
+
const file = join20(dir, "opencode.json");
|
|
7150
7666
|
const config = {
|
|
7151
7667
|
permission: {
|
|
7152
7668
|
read: "allow",
|
|
@@ -7161,7 +7677,7 @@ function writePerCrewOpencodeConfig(o) {
|
|
|
7161
7677
|
external_directory: { "**": "allow" }
|
|
7162
7678
|
}
|
|
7163
7679
|
};
|
|
7164
|
-
|
|
7680
|
+
writeFileSync9(file, JSON.stringify(config, null, 2));
|
|
7165
7681
|
return file;
|
|
7166
7682
|
}
|
|
7167
7683
|
|
|
@@ -7454,8 +7970,8 @@ init_dist();
|
|
|
7454
7970
|
init_dist3();
|
|
7455
7971
|
import { Command as Command11 } from "commander";
|
|
7456
7972
|
import { execSync as execSync10 } from "child_process";
|
|
7457
|
-
import { homedir as
|
|
7458
|
-
import { join as
|
|
7973
|
+
import { homedir as homedir16 } from "os";
|
|
7974
|
+
import { join as join22 } from "path";
|
|
7459
7975
|
import chalk12 from "chalk";
|
|
7460
7976
|
|
|
7461
7977
|
// packages/web/dist/read-status.js
|
|
@@ -7651,9 +8167,9 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
7651
8167
|
// packages/web/dist/probes.js
|
|
7652
8168
|
init_dist();
|
|
7653
8169
|
init_dist();
|
|
7654
|
-
import { join as
|
|
7655
|
-
import { homedir as
|
|
7656
|
-
import { existsSync as
|
|
8170
|
+
import { join as join21 } from "path";
|
|
8171
|
+
import { homedir as homedir15 } from "os";
|
|
8172
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
7657
8173
|
import { execFile as execFile3 } from "child_process";
|
|
7658
8174
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
7659
8175
|
var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
|
|
@@ -7689,7 +8205,7 @@ function vaultProbe(run, dir) {
|
|
|
7689
8205
|
return { state: "unknown", detail: "no vault configured" };
|
|
7690
8206
|
if (!run.pathExists(dir))
|
|
7691
8207
|
return { state: "gone", detail: "vault directory missing" };
|
|
7692
|
-
if (!run.pathExists(
|
|
8208
|
+
if (!run.pathExists(join21(dir, ".obsidian")))
|
|
7693
8209
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
7694
8210
|
return { state: "alive" };
|
|
7695
8211
|
} catch {
|
|
@@ -7757,13 +8273,13 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
7757
8273
|
const sessions = probeSessions(run);
|
|
7758
8274
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
7759
8275
|
}
|
|
7760
|
-
var SESSIONS_PATH =
|
|
8276
|
+
var SESSIONS_PATH = join21(homedir15(), ".config", "squadrant", "sessions.json");
|
|
7761
8277
|
function onPath(cli) {
|
|
7762
8278
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
7763
|
-
return dirs.some((d) =>
|
|
8279
|
+
return dirs.some((d) => existsSync11(join21(d, cli)));
|
|
7764
8280
|
}
|
|
7765
8281
|
function readSessionsHashes() {
|
|
7766
|
-
const raw = JSON.parse(
|
|
8282
|
+
const raw = JSON.parse(readFileSync12(SESSIONS_PATH, "utf-8"));
|
|
7767
8283
|
const hashes = Object.values(raw.workspaces ?? {}).map((w) => w.templateHash).filter((h) => typeof h === "string" && h.length > 0);
|
|
7768
8284
|
return [...new Set(hashes)];
|
|
7769
8285
|
}
|
|
@@ -7777,7 +8293,7 @@ function defaultProbeRunners() {
|
|
|
7777
8293
|
}
|
|
7778
8294
|
}),
|
|
7779
8295
|
probeOnPath: async (cli) => onPath(cli),
|
|
7780
|
-
pathExists: (p) =>
|
|
8296
|
+
pathExists: (p) => existsSync11(p),
|
|
7781
8297
|
loadConfig: () => loadConfig(),
|
|
7782
8298
|
loadSessionsHashes: () => readSessionsHashes()
|
|
7783
8299
|
};
|
|
@@ -8430,7 +8946,7 @@ async function startWebServer(opts) {
|
|
|
8430
8946
|
|
|
8431
8947
|
// packages/cli/src/commands/dashboard.ts
|
|
8432
8948
|
init_dist();
|
|
8433
|
-
var SOCK3 =
|
|
8949
|
+
var SOCK3 = join22(homedir16(), ".config", "squadrant", "squadrant.sock");
|
|
8434
8950
|
function detectCurrentWorkspace2() {
|
|
8435
8951
|
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
8436
8952
|
const match = out.match(/workspace:\d+/);
|
|
@@ -8514,13 +9030,87 @@ init_dist();
|
|
|
8514
9030
|
init_dist4();
|
|
8515
9031
|
init_dist3();
|
|
8516
9032
|
init_dist2();
|
|
8517
|
-
init_dist2();
|
|
8518
9033
|
import { Command as Command12 } from "commander";
|
|
8519
9034
|
import { execSync as execSync11 } from "child_process";
|
|
8520
9035
|
import fs20 from "fs";
|
|
8521
9036
|
import path23 from "path";
|
|
8522
9037
|
import os12 from "os";
|
|
8523
9038
|
import chalk13 from "chalk";
|
|
9039
|
+
|
|
9040
|
+
// packages/cli/src/commands/launch-interactive.ts
|
|
9041
|
+
import checkbox, { Separator } from "@inquirer/checkbox";
|
|
9042
|
+
function getYesterday() {
|
|
9043
|
+
const d = /* @__PURE__ */ new Date();
|
|
9044
|
+
d.setDate(d.getDate() - 1);
|
|
9045
|
+
return d.toISOString().slice(0, 10);
|
|
9046
|
+
}
|
|
9047
|
+
function partitionByYesterday(entries, yesterday) {
|
|
9048
|
+
const y = [];
|
|
9049
|
+
const r = [];
|
|
9050
|
+
for (const e of entries) {
|
|
9051
|
+
if (e.lastLaunched === yesterday) {
|
|
9052
|
+
y.push(e);
|
|
9053
|
+
} else {
|
|
9054
|
+
r.push(e);
|
|
9055
|
+
}
|
|
9056
|
+
}
|
|
9057
|
+
return { yesterday: y, rest: r };
|
|
9058
|
+
}
|
|
9059
|
+
async function selectCaptainsInteractive(entries, yesterday = getYesterday()) {
|
|
9060
|
+
const { yesterday: yesterdayEntries, rest: restEntries } = partitionByYesterday(entries, yesterday);
|
|
9061
|
+
if (yesterdayEntries.length === 0) {
|
|
9062
|
+
const result2 = await checkbox({
|
|
9063
|
+
message: "Select captains to launch:",
|
|
9064
|
+
choices: entries.map((e) => ({
|
|
9065
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9066
|
+
value: e.projectName,
|
|
9067
|
+
checked: false
|
|
9068
|
+
})),
|
|
9069
|
+
pageSize: 20
|
|
9070
|
+
});
|
|
9071
|
+
return result2;
|
|
9072
|
+
}
|
|
9073
|
+
const initialChoices = [
|
|
9074
|
+
new Separator("\u2500\u2500 Opened yesterday \u2500\u2500"),
|
|
9075
|
+
...yesterdayEntries.map((e) => ({
|
|
9076
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9077
|
+
value: e.projectName,
|
|
9078
|
+
checked: true
|
|
9079
|
+
})),
|
|
9080
|
+
new Separator(),
|
|
9081
|
+
{ name: "Show all projects", value: "__show_all__", checked: false }
|
|
9082
|
+
];
|
|
9083
|
+
const result = await checkbox({
|
|
9084
|
+
message: "Select captains to launch:",
|
|
9085
|
+
choices: initialChoices,
|
|
9086
|
+
pageSize: 20
|
|
9087
|
+
});
|
|
9088
|
+
if (result.includes("__show_all__")) {
|
|
9089
|
+
const allChecked = yesterdayEntries.map((e) => e.projectName);
|
|
9090
|
+
const result2 = await checkbox({
|
|
9091
|
+
message: "Select captains to launch (all projects):",
|
|
9092
|
+
choices: [
|
|
9093
|
+
new Separator("\u2500\u2500 Opened yesterday \u2500\u2500"),
|
|
9094
|
+
...yesterdayEntries.map((e) => ({
|
|
9095
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9096
|
+
value: e.projectName,
|
|
9097
|
+
checked: allChecked.includes(e.projectName)
|
|
9098
|
+
})),
|
|
9099
|
+
...restEntries.map((e) => ({
|
|
9100
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9101
|
+
value: e.projectName,
|
|
9102
|
+
checked: false
|
|
9103
|
+
}))
|
|
9104
|
+
],
|
|
9105
|
+
pageSize: 30
|
|
9106
|
+
});
|
|
9107
|
+
return result2;
|
|
9108
|
+
}
|
|
9109
|
+
return result;
|
|
9110
|
+
}
|
|
9111
|
+
|
|
9112
|
+
// packages/cli/src/commands/launch.ts
|
|
9113
|
+
init_dist2();
|
|
8524
9114
|
var CMUX_APP = "/Applications/cmux.app";
|
|
8525
9115
|
var TEMPLATES_DIR4 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
|
|
8526
9116
|
var SESSIONS_PATH2 = path23.join(os12.homedir(), ".config", "squadrant", "sessions.json");
|
|
@@ -8604,12 +9194,43 @@ var launchCommand = new Command12("launch").description(
|
|
|
8604
9194
|
}
|
|
8605
9195
|
console.log("");
|
|
8606
9196
|
} else if (!project) {
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
9197
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9198
|
+
console.error(
|
|
9199
|
+
chalk13.red(
|
|
9200
|
+
"\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"
|
|
9201
|
+
)
|
|
9202
|
+
);
|
|
9203
|
+
process.exit(1);
|
|
9204
|
+
}
|
|
9205
|
+
ensureCmuxReady();
|
|
9206
|
+
const sessions = loadSessions(SESSIONS_PATH2);
|
|
9207
|
+
const entries = Object.entries(config.projects).map(([name, proj]) => ({
|
|
9208
|
+
projectName: name,
|
|
9209
|
+
captainName: proj.captainName,
|
|
9210
|
+
lastLaunched: sessions.workspaces[proj.captainName]?.lastLaunched ?? null
|
|
9211
|
+
}));
|
|
9212
|
+
const selected = await selectCaptainsInteractive(entries);
|
|
9213
|
+
if (selected.length === 0) {
|
|
9214
|
+
console.log(chalk13.yellow("\n No captains selected.\n"));
|
|
9215
|
+
return;
|
|
9216
|
+
}
|
|
9217
|
+
console.log(chalk13.bold(`
|
|
9218
|
+
Launching ${selected.length} captain workspace(s) in parallel
|
|
9219
|
+
`));
|
|
9220
|
+
await Promise.all(selected.map(async (name) => {
|
|
9221
|
+
const proj = config.projects[name];
|
|
9222
|
+
const projPath = resolveHome(proj.path);
|
|
9223
|
+
const spokePath = resolveHome(proj.spokeVault);
|
|
9224
|
+
if (!fs20.existsSync(spokePath)) {
|
|
9225
|
+
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
9226
|
+
await ensureSpokeLayout(spokeDriver);
|
|
9227
|
+
console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
9228
|
+
}
|
|
9229
|
+
console.log(chalk13.bold(`
|
|
9230
|
+
Captain: ${proj.captainName} (${name})`));
|
|
9231
|
+
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
9232
|
+
}));
|
|
9233
|
+
console.log("");
|
|
8613
9234
|
} else {
|
|
8614
9235
|
if (!config.projects[project]) {
|
|
8615
9236
|
console.error(
|
|
@@ -9606,7 +10227,7 @@ init_dist2();
|
|
|
9606
10227
|
import { Command as Command22 } from "commander";
|
|
9607
10228
|
import fs25 from "fs";
|
|
9608
10229
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
9609
|
-
import { dirname as dirname5, join as
|
|
10230
|
+
import { dirname as dirname5, join as join23 } from "path";
|
|
9610
10231
|
import chalk22 from "chalk";
|
|
9611
10232
|
function runConfigCheck(opts) {
|
|
9612
10233
|
const raw = JSON.parse(fs25.readFileSync(opts.configPath, "utf-8"));
|
|
@@ -9742,7 +10363,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
9742
10363
|
}
|
|
9743
10364
|
});
|
|
9744
10365
|
function readPkgVersion2() {
|
|
9745
|
-
const pkgPath =
|
|
10366
|
+
const pkgPath = join23(dirname5(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
9746
10367
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
9747
10368
|
}
|
|
9748
10369
|
|
|
@@ -10013,12 +10634,12 @@ var effortCommand = new Command26("effort").description("Get or set the global c
|
|
|
10013
10634
|
// packages/cli/src/commands/telegram.ts
|
|
10014
10635
|
init_dist();
|
|
10015
10636
|
init_dist2();
|
|
10016
|
-
import { join as
|
|
10637
|
+
import { join as join24, dirname as dirname6 } from "path";
|
|
10017
10638
|
import { emitKeypressEvents } from "readline";
|
|
10018
10639
|
import { Command as Command27 } from "commander";
|
|
10019
10640
|
import chalk27 from "chalk";
|
|
10020
10641
|
function defaultStateRoot() {
|
|
10021
|
-
return
|
|
10642
|
+
return join24(dirname6(DEFAULT_CONFIG_PATH), "state");
|
|
10022
10643
|
}
|
|
10023
10644
|
async function questionMasked() {
|
|
10024
10645
|
return new Promise((resolve3) => {
|
|
@@ -10339,25 +10960,88 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
10339
10960
|
}
|
|
10340
10961
|
});
|
|
10341
10962
|
|
|
10963
|
+
// packages/cli/src/commands/hooks.ts
|
|
10964
|
+
init_dist2();
|
|
10965
|
+
init_dist4();
|
|
10966
|
+
import { Command as Command28 } from "commander";
|
|
10967
|
+
import { join as join25 } from "path";
|
|
10968
|
+
import { homedir as homedir17 } from "os";
|
|
10969
|
+
var SOCK4 = join25(homedir17(), ".config", "squadrant", "squadrant.sock");
|
|
10970
|
+
async function sendToSock(req) {
|
|
10971
|
+
await sendRequest(SOCK4, req);
|
|
10972
|
+
}
|
|
10973
|
+
function mapHookSub(sub, payload, taskId) {
|
|
10974
|
+
switch (sub) {
|
|
10975
|
+
case "session-start":
|
|
10976
|
+
case "prompt-submit":
|
|
10977
|
+
case "pre-tool-use":
|
|
10978
|
+
return { type: "task.progress", id: taskId, note: sub };
|
|
10979
|
+
case "stop":
|
|
10980
|
+
return mapClaudeHookToEvent("Stop", payload, taskId);
|
|
10981
|
+
case "notification":
|
|
10982
|
+
return mapClaudeHookToEvent("Notification", payload, taskId);
|
|
10983
|
+
case "ask-question": {
|
|
10984
|
+
const q = typeof payload?.question === "string" ? payload.question : "awaiting input";
|
|
10985
|
+
return { type: "task.input.requested", id: taskId, requestId: 0, question: q };
|
|
10986
|
+
}
|
|
10987
|
+
case "session-end":
|
|
10988
|
+
return mapClaudeHookToEvent("SessionEnd", payload, taskId);
|
|
10989
|
+
default:
|
|
10990
|
+
return null;
|
|
10991
|
+
}
|
|
10992
|
+
}
|
|
10993
|
+
function hooksCommand() {
|
|
10994
|
+
const hooks = new Command28("hooks").description("(internal) receive lifecycle hook events from agent processes");
|
|
10995
|
+
hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
|
|
10996
|
+
const taskId = process.env.SQUADRANT_CREW_TASK_ID;
|
|
10997
|
+
const project = process.env.SQUADRANT_CREW_PROJECT;
|
|
10998
|
+
if (!taskId || !project) {
|
|
10999
|
+
process.exit(0);
|
|
11000
|
+
}
|
|
11001
|
+
let stdin = "";
|
|
11002
|
+
try {
|
|
11003
|
+
for await (const chunk of process.stdin) stdin += chunk;
|
|
11004
|
+
} catch {
|
|
11005
|
+
}
|
|
11006
|
+
let payload = void 0;
|
|
11007
|
+
if (stdin.trim()) {
|
|
11008
|
+
try {
|
|
11009
|
+
payload = JSON.parse(stdin);
|
|
11010
|
+
} catch {
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
11013
|
+
const ev = mapHookSub(sub, payload, taskId);
|
|
11014
|
+
if (!ev) {
|
|
11015
|
+
process.exit(0);
|
|
11016
|
+
}
|
|
11017
|
+
try {
|
|
11018
|
+
await sendToSock({ kind: "event", project, event: ev });
|
|
11019
|
+
} catch {
|
|
11020
|
+
}
|
|
11021
|
+
process.exit(0);
|
|
11022
|
+
});
|
|
11023
|
+
return hooks;
|
|
11024
|
+
}
|
|
11025
|
+
|
|
10342
11026
|
// packages/cli/src/index.ts
|
|
10343
11027
|
init_dist();
|
|
10344
11028
|
init_dist();
|
|
10345
11029
|
init_dist();
|
|
10346
11030
|
var __dirname = dirname7(fileURLToPath6(import.meta.url));
|
|
10347
|
-
var pkg = JSON.parse(
|
|
11031
|
+
var pkg = JSON.parse(readFileSync13(join26(__dirname, "..", "package.json"), "utf-8"));
|
|
10348
11032
|
ensureRuntimeSynced({
|
|
10349
|
-
sourceRoot:
|
|
10350
|
-
runtimeRoot:
|
|
11033
|
+
sourceRoot: join26(__dirname, ".."),
|
|
11034
|
+
runtimeRoot: join26(homedir18(), ".config", "squadrant")
|
|
10351
11035
|
});
|
|
10352
11036
|
if (process.argv[2] !== "config") {
|
|
10353
11037
|
try {
|
|
10354
|
-
const cfgPath =
|
|
10355
|
-
if (
|
|
10356
|
-
const cfg = JSON.parse(
|
|
11038
|
+
const cfgPath = join26(homedir18(), ".config", "squadrant", "config.json");
|
|
11039
|
+
if (existsSync12(cfgPath)) {
|
|
11040
|
+
const cfg = JSON.parse(readFileSync13(cfgPath, "utf-8"));
|
|
10357
11041
|
if (needsCheck(cfg, pkg.version)) {
|
|
10358
11042
|
const items = detectDrift(cfg, getDefaultConfig());
|
|
10359
11043
|
if (items.length === 0) {
|
|
10360
|
-
|
|
11044
|
+
writeFileSync10(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
|
|
10361
11045
|
} else {
|
|
10362
11046
|
const from = cfg._squadrantVersion ?? "an earlier version";
|
|
10363
11047
|
process.stderr.write(
|
|
@@ -10376,7 +11060,7 @@ if (process.argv[2] !== "config") {
|
|
|
10376
11060
|
if (!process.env.SQUADRANT_DAEMON_SKIP) {
|
|
10377
11061
|
ensureDaemon();
|
|
10378
11062
|
}
|
|
10379
|
-
var program = new
|
|
11063
|
+
var program = new Command29();
|
|
10380
11064
|
program.name("squadrant").description("Multi-project orchestration for your coding agents (Claude, Codex, opencode, Gemini)").version(pkg.version);
|
|
10381
11065
|
program.addCommand(doctorCommand);
|
|
10382
11066
|
program.addCommand(initCommand);
|
|
@@ -10403,6 +11087,7 @@ program.addCommand(groupCommand);
|
|
|
10403
11087
|
program.addCommand(cmuxCommand);
|
|
10404
11088
|
program.addCommand(effortCommand);
|
|
10405
11089
|
program.addCommand(telegramCommand);
|
|
11090
|
+
program.addCommand(hooksCommand());
|
|
10406
11091
|
program.parseAsync().catch((e) => {
|
|
10407
11092
|
process.stderr.write(`error: ${e instanceof Error ? e.message : String(e)}
|
|
10408
11093
|
`);
|