squadrant 0.12.0 → 0.13.0
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 +757 -90
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +592 -14
- package/dist/squadrantd.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -34,14 +34,14 @@ function getDefaultConfig() {
|
|
|
34
34
|
models: {
|
|
35
35
|
command: "opus",
|
|
36
36
|
captain: "opus",
|
|
37
|
-
crew: "
|
|
37
|
+
crew: "sonnet",
|
|
38
38
|
exploration: "haiku",
|
|
39
39
|
review: "opus"
|
|
40
40
|
},
|
|
41
41
|
roles: {
|
|
42
42
|
command: { agent: "claude", model: "opus" },
|
|
43
43
|
captain: { agent: "claude", model: "opus" },
|
|
44
|
-
crew: { agent: "claude", model: "
|
|
44
|
+
crew: { agent: "claude", model: "sonnet" },
|
|
45
45
|
exploration: { agent: "claude", model: "haiku" },
|
|
46
46
|
side: { agent: "claude", model: "opus" }
|
|
47
47
|
},
|
|
@@ -579,7 +579,7 @@ var init_config_drift = __esm({
|
|
|
579
579
|
}
|
|
580
580
|
];
|
|
581
581
|
KNOWN_DEFAULT_HISTORY = [
|
|
582
|
-
{ path: "defaults.roles.crew.model", oldDefaults: ["
|
|
582
|
+
{ path: "defaults.roles.crew.model", oldDefaults: ["opus"] },
|
|
583
583
|
{ path: "defaults.roles.captain.model", oldDefaults: ["sonnet"] }
|
|
584
584
|
];
|
|
585
585
|
KNOWN_DRIVERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencode"]);
|
|
@@ -2632,7 +2632,8 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
|
2632
2632
|
}
|
|
2633
2633
|
} catch {
|
|
2634
2634
|
}
|
|
2635
|
-
|
|
2635
|
+
const deliver = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg));
|
|
2636
|
+
await deliver(crew, message);
|
|
2636
2637
|
}
|
|
2637
2638
|
async function runCrewRead(project, name, runtime, workspaceId) {
|
|
2638
2639
|
const crew = await findCrewPane(runtime, workspaceId, project, name);
|
|
@@ -2699,6 +2700,12 @@ var init_crew_spawn = __esm({
|
|
|
2699
2700
|
}
|
|
2700
2701
|
});
|
|
2701
2702
|
|
|
2703
|
+
// packages/core/dist/lifecycle-source.js
|
|
2704
|
+
var init_lifecycle_source = __esm({
|
|
2705
|
+
"packages/core/dist/lifecycle-source.js"() {
|
|
2706
|
+
}
|
|
2707
|
+
});
|
|
2708
|
+
|
|
2702
2709
|
// packages/core/dist/index.js
|
|
2703
2710
|
var init_dist2 = __esm({
|
|
2704
2711
|
"packages/core/dist/index.js"() {
|
|
@@ -2731,6 +2738,7 @@ var init_dist2 = __esm({
|
|
|
2731
2738
|
init_launch_workspace();
|
|
2732
2739
|
init_side_session();
|
|
2733
2740
|
init_crew_spawn();
|
|
2741
|
+
init_lifecycle_source();
|
|
2734
2742
|
}
|
|
2735
2743
|
});
|
|
2736
2744
|
|
|
@@ -2999,8 +3007,14 @@ function createCmuxDriver() {
|
|
|
2999
3007
|
}
|
|
3000
3008
|
},
|
|
3001
3009
|
async sendToPane(pane, message) {
|
|
3002
|
-
await
|
|
3003
|
-
await
|
|
3010
|
+
await this.pasteToPane(pane, message);
|
|
3011
|
+
await this.sendKeyToPane(pane, "Enter");
|
|
3012
|
+
},
|
|
3013
|
+
async pasteToPane(pane, text) {
|
|
3014
|
+
await cmux(["send", "--workspace", pane.workspaceId, "--surface", pane.surfaceId, sanitizeForCmuxSend(text)]);
|
|
3015
|
+
},
|
|
3016
|
+
async sendKeyToPane(pane, key) {
|
|
3017
|
+
await cmux(["send-key", "--workspace", pane.workspaceId, "--surface", pane.surfaceId, key]);
|
|
3004
3018
|
},
|
|
3005
3019
|
async readPaneScreen(pane) {
|
|
3006
3020
|
try {
|
|
@@ -3554,8 +3568,357 @@ var init_daemon_cmux = __esm({
|
|
|
3554
3568
|
}
|
|
3555
3569
|
});
|
|
3556
3570
|
|
|
3571
|
+
// packages/workspaces/dist/cmux-daemon/cmux-store-source.js
|
|
3572
|
+
import { join as join13 } from "path";
|
|
3573
|
+
import { homedir as homedir8 } from "os";
|
|
3574
|
+
import { watch, readdirSync as readdirSync3, readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3575
|
+
function parseLifecycleState(s) {
|
|
3576
|
+
if (s === "running" || s === "idle" || s === "needsInput" || s === "unknown") {
|
|
3577
|
+
return s;
|
|
3578
|
+
}
|
|
3579
|
+
return "unknown";
|
|
3580
|
+
}
|
|
3581
|
+
function defaultIsPidAlive(pid) {
|
|
3582
|
+
try {
|
|
3583
|
+
process.kill(pid, 0);
|
|
3584
|
+
return true;
|
|
3585
|
+
} catch {
|
|
3586
|
+
return false;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
function defaultListFiles(dir) {
|
|
3590
|
+
try {
|
|
3591
|
+
return readdirSync3(dir).filter((f) => f.endsWith("-hook-sessions.json") && !f.endsWith(".lock"));
|
|
3592
|
+
} catch {
|
|
3593
|
+
return [];
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
function defaultReadFile(path29) {
|
|
3597
|
+
try {
|
|
3598
|
+
return readFileSync8(path29, "utf-8");
|
|
3599
|
+
} catch {
|
|
3600
|
+
return void 0;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function defaultWatchDir(dir, cb) {
|
|
3604
|
+
const w = watch(dir, (_event, filename) => {
|
|
3605
|
+
if (typeof filename === "string" && filename.endsWith("-hook-sessions.json")) {
|
|
3606
|
+
cb();
|
|
3607
|
+
}
|
|
3608
|
+
});
|
|
3609
|
+
return () => w.close();
|
|
3610
|
+
}
|
|
3611
|
+
var CmuxStoreSource;
|
|
3612
|
+
var init_cmux_store_source = __esm({
|
|
3613
|
+
"packages/workspaces/dist/cmux-daemon/cmux-store-source.js"() {
|
|
3614
|
+
CmuxStoreSource = class {
|
|
3615
|
+
name = "cmux-store";
|
|
3616
|
+
stateDir;
|
|
3617
|
+
debounceMs;
|
|
3618
|
+
isPidAlive;
|
|
3619
|
+
listFiles;
|
|
3620
|
+
readFile;
|
|
3621
|
+
fileExists;
|
|
3622
|
+
watchDir;
|
|
3623
|
+
scheduleTimer;
|
|
3624
|
+
cancelTimer;
|
|
3625
|
+
log;
|
|
3626
|
+
deps;
|
|
3627
|
+
stopWatcher;
|
|
3628
|
+
debounceTimer;
|
|
3629
|
+
/** taskId → last reported snapshot (for snapshot() liveness floor). */
|
|
3630
|
+
cache = /* @__PURE__ */ new Map();
|
|
3631
|
+
constructor(opts = {}) {
|
|
3632
|
+
this.stateDir = opts.stateDir ?? process.env.CMUX_AGENT_HOOK_STATE_DIR ?? join13(homedir8(), ".cmuxterm");
|
|
3633
|
+
this.debounceMs = opts.debounceMs ?? 50;
|
|
3634
|
+
this.isPidAlive = opts.isPidAlive ?? defaultIsPidAlive;
|
|
3635
|
+
this.listFiles = opts.listFiles ?? defaultListFiles;
|
|
3636
|
+
this.readFile = opts.readFile ?? defaultReadFile;
|
|
3637
|
+
this.fileExists = opts.fileExists ?? existsSync10;
|
|
3638
|
+
this.watchDir = opts.watchDir ?? defaultWatchDir;
|
|
3639
|
+
this.scheduleTimer = opts.scheduleTimer ?? setTimeout;
|
|
3640
|
+
this.cancelTimer = opts.cancelTimer ?? clearTimeout;
|
|
3641
|
+
this.log = opts.log ?? (() => {
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3644
|
+
start(deps) {
|
|
3645
|
+
this.deps = deps;
|
|
3646
|
+
this.scan();
|
|
3647
|
+
try {
|
|
3648
|
+
this.stopWatcher = this.watchDir(this.stateDir, () => this.scheduleDebounced());
|
|
3649
|
+
} catch (e) {
|
|
3650
|
+
this.log(`cmux-store: failed to watch ${this.stateDir}: ${e.message}`);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
stop() {
|
|
3654
|
+
if (this.debounceTimer !== void 0) {
|
|
3655
|
+
this.cancelTimer(this.debounceTimer);
|
|
3656
|
+
this.debounceTimer = void 0;
|
|
3657
|
+
}
|
|
3658
|
+
this.stopWatcher?.();
|
|
3659
|
+
this.stopWatcher = void 0;
|
|
3660
|
+
this.deps = void 0;
|
|
3661
|
+
this.cache.clear();
|
|
3662
|
+
}
|
|
3663
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor). */
|
|
3664
|
+
snapshot(taskId) {
|
|
3665
|
+
return this.cache.get(taskId);
|
|
3666
|
+
}
|
|
3667
|
+
// ── private ─────────────────────────────────────────────────────────────────
|
|
3668
|
+
scheduleDebounced() {
|
|
3669
|
+
if (this.debounceTimer !== void 0) {
|
|
3670
|
+
this.cancelTimer(this.debounceTimer);
|
|
3671
|
+
}
|
|
3672
|
+
this.debounceTimer = this.scheduleTimer(() => {
|
|
3673
|
+
this.debounceTimer = void 0;
|
|
3674
|
+
this.scan();
|
|
3675
|
+
}, this.debounceMs);
|
|
3676
|
+
}
|
|
3677
|
+
scan() {
|
|
3678
|
+
if (!this.deps)
|
|
3679
|
+
return;
|
|
3680
|
+
for (const filename of this.listFiles(this.stateDir)) {
|
|
3681
|
+
this.scanFile(filename);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
scanFile(filename) {
|
|
3685
|
+
const deps = this.deps;
|
|
3686
|
+
const filePath = join13(this.stateDir, filename);
|
|
3687
|
+
const lockPath = `${filePath}.lock`;
|
|
3688
|
+
if (this.fileExists(lockPath)) {
|
|
3689
|
+
this.log(`cmux-store: skipping ${filename} (locked)`);
|
|
3690
|
+
return;
|
|
3691
|
+
}
|
|
3692
|
+
const raw = this.readFile(filePath);
|
|
3693
|
+
if (!raw)
|
|
3694
|
+
return;
|
|
3695
|
+
let parsed;
|
|
3696
|
+
try {
|
|
3697
|
+
parsed = JSON.parse(raw);
|
|
3698
|
+
} catch {
|
|
3699
|
+
this.log(`cmux-store: failed to parse ${filename}`);
|
|
3700
|
+
return;
|
|
3701
|
+
}
|
|
3702
|
+
for (const session of Object.values(parsed.sessions ?? {})) {
|
|
3703
|
+
this.processSession(session, deps);
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
processSession(session, deps) {
|
|
3707
|
+
if (!session.sessionId || !session.cwd || typeof session.pid !== "number")
|
|
3708
|
+
return;
|
|
3709
|
+
const hint = {
|
|
3710
|
+
cwd: session.cwd,
|
|
3711
|
+
pid: session.pid,
|
|
3712
|
+
sessionId: session.sessionId
|
|
3713
|
+
};
|
|
3714
|
+
const resolved = deps.resolve(hint);
|
|
3715
|
+
if (!resolved)
|
|
3716
|
+
return;
|
|
3717
|
+
let alive = this.isPidAlive(session.pid);
|
|
3718
|
+
if (!alive && session.isRestorable === true && session.agentLifecycle === "idle") {
|
|
3719
|
+
alive = true;
|
|
3720
|
+
}
|
|
3721
|
+
const snap = {
|
|
3722
|
+
taskId: resolved.id,
|
|
3723
|
+
state: parseLifecycleState(session.agentLifecycle),
|
|
3724
|
+
alive,
|
|
3725
|
+
// "agent": the store carries the agent's own reported lifecycle state,
|
|
3726
|
+
// not a scan inference. needsInput from the store is authoritative.
|
|
3727
|
+
origin: "agent",
|
|
3728
|
+
at: Math.floor((session.updatedAt ?? 0) * 1e3),
|
|
3729
|
+
pid: session.pid,
|
|
3730
|
+
...session.lastBody ? { detail: { note: session.lastBody } } : {}
|
|
3731
|
+
};
|
|
3732
|
+
this.cache.set(resolved.id, snap);
|
|
3733
|
+
deps.report(snap);
|
|
3734
|
+
}
|
|
3735
|
+
};
|
|
3736
|
+
}
|
|
3737
|
+
});
|
|
3738
|
+
|
|
3739
|
+
// packages/workspaces/dist/native-hooks/native-hook-source.js
|
|
3740
|
+
import { join as join14 } from "path";
|
|
3741
|
+
import { homedir as homedir9 } from "os";
|
|
3742
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
3743
|
+
function installClaudeHooks(opts = {}) {
|
|
3744
|
+
const settingsPath = opts.settingsPath ?? join14(homedir9(), ".claude", "settings.json");
|
|
3745
|
+
const hookCmd = opts.hookCmd ?? DEFAULT_HOOK_CMD;
|
|
3746
|
+
const readFile6 = opts.readFile ?? defaultReadFile2;
|
|
3747
|
+
const writeFile5 = opts.writeFile ?? defaultWriteFile;
|
|
3748
|
+
const log = opts.log ?? (() => {
|
|
3749
|
+
});
|
|
3750
|
+
let settings = {};
|
|
3751
|
+
const raw = readFile6(settingsPath);
|
|
3752
|
+
if (raw) {
|
|
3753
|
+
try {
|
|
3754
|
+
settings = JSON.parse(raw);
|
|
3755
|
+
} catch {
|
|
3756
|
+
log(`native-hook: failed to parse ${settingsPath} \u2014 hooks section will be reset`);
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
if (typeof settings.hooks !== "object" || settings.hooks === null || Array.isArray(settings.hooks)) {
|
|
3760
|
+
settings.hooks = {};
|
|
3761
|
+
}
|
|
3762
|
+
const hooks = settings.hooks;
|
|
3763
|
+
let changed = false;
|
|
3764
|
+
for (const [eventName, sub, matcher] of CLAUDE_HOOK_EVENTS) {
|
|
3765
|
+
if (!Array.isArray(hooks[eventName])) {
|
|
3766
|
+
hooks[eventName] = [];
|
|
3767
|
+
}
|
|
3768
|
+
const entries = hooks[eventName];
|
|
3769
|
+
const command = `${hookCmd} claude ${sub}`;
|
|
3770
|
+
const hookMatcher = matcher ?? "";
|
|
3771
|
+
const alreadyPresent = entries.some((m) => Array.isArray(m.hooks) && m.hooks.some((h) => typeof h.command === "string" && h.command === command));
|
|
3772
|
+
if (!alreadyPresent) {
|
|
3773
|
+
entries.push({ matcher: hookMatcher, hooks: [{ type: "command", command, timeout: 10 }] });
|
|
3774
|
+
changed = true;
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
if (changed) {
|
|
3778
|
+
writeFile5(settingsPath, JSON.stringify(settings, null, 2));
|
|
3779
|
+
}
|
|
3780
|
+
return settingsPath;
|
|
3781
|
+
}
|
|
3782
|
+
function mapSubToLifecycle(sub) {
|
|
3783
|
+
switch (sub) {
|
|
3784
|
+
case "session-start":
|
|
3785
|
+
return "running";
|
|
3786
|
+
case "prompt-submit":
|
|
3787
|
+
return "running";
|
|
3788
|
+
case "pre-tool-use":
|
|
3789
|
+
return "running";
|
|
3790
|
+
case "stop":
|
|
3791
|
+
return "idle";
|
|
3792
|
+
case "notification":
|
|
3793
|
+
return "needsInput";
|
|
3794
|
+
case "ask-question":
|
|
3795
|
+
return "needsInput";
|
|
3796
|
+
case "session-end":
|
|
3797
|
+
return "session-end";
|
|
3798
|
+
default:
|
|
3799
|
+
return null;
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
function extractDetail(sub, payload) {
|
|
3803
|
+
if (!payload || typeof payload !== "object")
|
|
3804
|
+
return void 0;
|
|
3805
|
+
const p = payload;
|
|
3806
|
+
if (sub === "notification") {
|
|
3807
|
+
const note = typeof p.message === "string" ? p.message : void 0;
|
|
3808
|
+
return note ? { note } : void 0;
|
|
3809
|
+
}
|
|
3810
|
+
if (sub === "pre-tool-use") {
|
|
3811
|
+
const tool = typeof p.tool_name === "string" ? p.tool_name : void 0;
|
|
3812
|
+
return tool ? { tool } : void 0;
|
|
3813
|
+
}
|
|
3814
|
+
return void 0;
|
|
3815
|
+
}
|
|
3816
|
+
function defaultReadFile2(path29) {
|
|
3817
|
+
try {
|
|
3818
|
+
return readFileSync9(path29, "utf-8");
|
|
3819
|
+
} catch {
|
|
3820
|
+
return void 0;
|
|
3821
|
+
}
|
|
3822
|
+
}
|
|
3823
|
+
function defaultWriteFile(path29, content) {
|
|
3824
|
+
mkdirSync6(path29.replace(/\/[^/]+$/, ""), { recursive: true });
|
|
3825
|
+
writeFileSync7(path29, content, "utf-8");
|
|
3826
|
+
}
|
|
3827
|
+
var CLAUDE_HOOK_EVENTS, DEFAULT_HOOK_CMD, NativeHookSource;
|
|
3828
|
+
var init_native_hook_source = __esm({
|
|
3829
|
+
"packages/workspaces/dist/native-hooks/native-hook-source.js"() {
|
|
3830
|
+
CLAUDE_HOOK_EVENTS = [
|
|
3831
|
+
["SessionStart", "session-start"],
|
|
3832
|
+
["UserPromptSubmit", "prompt-submit"],
|
|
3833
|
+
["PreToolUse", "pre-tool-use"],
|
|
3834
|
+
["Stop", "stop"],
|
|
3835
|
+
["Notification", "notification"],
|
|
3836
|
+
["PreToolUse", "ask-question", "AskUserQuestion"],
|
|
3837
|
+
["SessionEnd", "session-end"]
|
|
3838
|
+
];
|
|
3839
|
+
DEFAULT_HOOK_CMD = "squadrant hooks";
|
|
3840
|
+
NativeHookSource = class {
|
|
3841
|
+
name = "native-hook";
|
|
3842
|
+
hookInstall;
|
|
3843
|
+
log;
|
|
3844
|
+
deps;
|
|
3845
|
+
/** taskId → last-reported snapshot, for snapshot() liveness floor. */
|
|
3846
|
+
cache = /* @__PURE__ */ new Map();
|
|
3847
|
+
constructor(opts = {}) {
|
|
3848
|
+
this.hookInstall = opts.hookInstall ?? {};
|
|
3849
|
+
this.log = opts.log ?? (() => {
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3852
|
+
start(deps) {
|
|
3853
|
+
this.deps = deps;
|
|
3854
|
+
}
|
|
3855
|
+
stop() {
|
|
3856
|
+
this.deps = void 0;
|
|
3857
|
+
this.cache.clear();
|
|
3858
|
+
}
|
|
3859
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor poll). */
|
|
3860
|
+
snapshot(taskId) {
|
|
3861
|
+
return this.cache.get(taskId);
|
|
3862
|
+
}
|
|
3863
|
+
/**
|
|
3864
|
+
* Install squadrant-owned hooks into ~/.claude/settings.json.
|
|
3865
|
+
* Idempotent — safe to call on every project init or crew spawn.
|
|
3866
|
+
* Returns the path to the settings file.
|
|
3867
|
+
*/
|
|
3868
|
+
install() {
|
|
3869
|
+
return installClaudeHooks(this.hookInstall);
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* Receive a lifecycle hook event from the daemon and report a LifecycleSnapshot.
|
|
3873
|
+
*
|
|
3874
|
+
* The daemon's 'squadrant hooks claude <sub>' CLI subcommand calls this after
|
|
3875
|
+
* reading SQUADRANT_CREW_TASK_ID from the hook's process environment — the only
|
|
3876
|
+
* collision-proof correlation key (blueprint §2.2 priority 1).
|
|
3877
|
+
*
|
|
3878
|
+
* @param sub Sub-event alias: "session-start" | "prompt-submit" | "stop" | …
|
|
3879
|
+
* @param taskId SQUADRANT_CREW_TASK_ID extracted from the hook process env.
|
|
3880
|
+
* @param pid Optional: OS pid from the hook's process env or argv.
|
|
3881
|
+
* @param payload Optional: parsed JSON payload from hook stdin (best-effort detail).
|
|
3882
|
+
*/
|
|
3883
|
+
handleHook(sub, taskId, pid, payload) {
|
|
3884
|
+
if (!this.deps)
|
|
3885
|
+
return;
|
|
3886
|
+
const mapped = mapSubToLifecycle(sub);
|
|
3887
|
+
if (mapped === null) {
|
|
3888
|
+
this.log(`native-hook: unknown sub '${sub}' for task ${taskId} \u2014 ignored`);
|
|
3889
|
+
return;
|
|
3890
|
+
}
|
|
3891
|
+
const isSessionEnd = mapped === "session-end";
|
|
3892
|
+
const state = isSessionEnd ? "unknown" : mapped;
|
|
3893
|
+
const detail = extractDetail(sub, payload);
|
|
3894
|
+
const snap = {
|
|
3895
|
+
taskId,
|
|
3896
|
+
state,
|
|
3897
|
+
alive: !isSessionEnd,
|
|
3898
|
+
origin: "agent",
|
|
3899
|
+
at: Date.now(),
|
|
3900
|
+
...pid !== void 0 ? { pid } : {},
|
|
3901
|
+
...detail ? { detail } : {}
|
|
3902
|
+
};
|
|
3903
|
+
this.cache.set(taskId, snap);
|
|
3904
|
+
this.deps.report(snap);
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3907
|
+
}
|
|
3908
|
+
});
|
|
3909
|
+
|
|
3557
3910
|
// packages/workspaces/dist/crew-pane.js
|
|
3558
3911
|
import net from "net";
|
|
3912
|
+
async function settleInputBox(runtime, pane) {
|
|
3913
|
+
let prev = await runtime.readPaneScreen(pane) ?? "";
|
|
3914
|
+
for (let i = 0; i < SETTLE_MAX_POLLS; i++) {
|
|
3915
|
+
await new Promise((r) => setTimeout(r, SETTLE_POLL_MS));
|
|
3916
|
+
const cur = await runtime.readPaneScreen(pane) ?? "";
|
|
3917
|
+
if (cur === prev)
|
|
3918
|
+
return;
|
|
3919
|
+
prev = cur;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3559
3922
|
function getFreePort() {
|
|
3560
3923
|
return new Promise((resolve3, reject) => {
|
|
3561
3924
|
const srv = net.createServer();
|
|
@@ -3589,6 +3952,23 @@ async function resolveCaptainWorkspace(project) {
|
|
|
3589
3952
|
}
|
|
3590
3953
|
return { runtime, workspaceId: captain.id };
|
|
3591
3954
|
}
|
|
3955
|
+
async function confirmedSendToPane(runtime, pane, message) {
|
|
3956
|
+
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3957
|
+
await runtime.pasteToPane(pane, message);
|
|
3958
|
+
await settleInputBox(runtime, pane);
|
|
3959
|
+
await runtime.sendKeyToPane(pane, "Enter");
|
|
3960
|
+
for (let attempt = 0; attempt < SUBMIT_RETRY_LIMIT; attempt++) {
|
|
3961
|
+
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3962
|
+
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3963
|
+
const draft = parseDraftFromScreen(afterScreen);
|
|
3964
|
+
if (draft === "")
|
|
3965
|
+
return;
|
|
3966
|
+
if (draft === null && afterScreen !== preSendScreen)
|
|
3967
|
+
return;
|
|
3968
|
+
await settleInputBox(runtime, pane);
|
|
3969
|
+
await runtime.sendKeyToPane(pane, "Enter");
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3592
3972
|
async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acceptanceConfig) {
|
|
3593
3973
|
await new Promise((r) => setTimeout(r, SEND_FIRST_TURN_FLOOR_MS));
|
|
3594
3974
|
const maxPolls = Math.floor((SEND_FIRST_TURN_TIMEOUT_MS - SEND_FIRST_TURN_FLOOR_MS) / POLL_INTERVAL_MS);
|
|
@@ -3604,8 +3984,8 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
3604
3984
|
}
|
|
3605
3985
|
}
|
|
3606
3986
|
const preSendScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
3607
|
-
await runtime.sendToPane(pane, task);
|
|
3608
3987
|
if (acceptanceConfig?.splashMarker) {
|
|
3988
|
+
await runtime.sendToPane(pane, task);
|
|
3609
3989
|
for (let check2 = 0; check2 < SPLASH_MAX_CHECKS; check2++) {
|
|
3610
3990
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
3611
3991
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
@@ -3616,21 +3996,25 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
3616
3996
|
await runtime.sendToPane(pane, task);
|
|
3617
3997
|
}
|
|
3618
3998
|
}
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3999
|
+
return;
|
|
4000
|
+
}
|
|
4001
|
+
await runtime.pasteToPane(pane, task);
|
|
4002
|
+
await settleInputBox(runtime, pane);
|
|
4003
|
+
await runtime.sendKeyToPane(pane, "Enter");
|
|
4004
|
+
const retryLimit = acceptanceConfig?.retryLimit ?? SUBMIT_RETRY_LIMIT;
|
|
4005
|
+
for (let attempt = 0; attempt < retryLimit; attempt++) {
|
|
4006
|
+
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
4007
|
+
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
4008
|
+
const draft = parseDraftFromScreen(afterScreen);
|
|
4009
|
+
if (draft === "")
|
|
4010
|
+
return;
|
|
4011
|
+
if (draft === null && afterScreen !== preSendScreen)
|
|
4012
|
+
return;
|
|
4013
|
+
await settleInputBox(runtime, pane);
|
|
4014
|
+
await runtime.sendKeyToPane(pane, "Enter");
|
|
3631
4015
|
}
|
|
3632
4016
|
}
|
|
3633
|
-
var SEND_FIRST_TURN_FLOOR_MS, POLL_INTERVAL_MS, SEND_FIRST_TURN_TIMEOUT_MS, POST_SEND_CHECK_MS, SPLASH_MAX_CHECKS, SPLASH_RESEND_EVERY_N;
|
|
4017
|
+
var SEND_FIRST_TURN_FLOOR_MS, POLL_INTERVAL_MS, SEND_FIRST_TURN_TIMEOUT_MS, POST_SEND_CHECK_MS, SPLASH_MAX_CHECKS, SPLASH_RESEND_EVERY_N, SETTLE_POLL_MS, SETTLE_MAX_POLLS, SUBMIT_RETRY_LIMIT;
|
|
3634
4018
|
var init_crew_pane = __esm({
|
|
3635
4019
|
"packages/workspaces/dist/crew-pane.js"() {
|
|
3636
4020
|
init_dist();
|
|
@@ -3643,6 +4027,9 @@ var init_crew_pane = __esm({
|
|
|
3643
4027
|
POST_SEND_CHECK_MS = 750;
|
|
3644
4028
|
SPLASH_MAX_CHECKS = 20;
|
|
3645
4029
|
SPLASH_RESEND_EVERY_N = 4;
|
|
4030
|
+
SETTLE_POLL_MS = 400;
|
|
4031
|
+
SETTLE_MAX_POLLS = 8;
|
|
4032
|
+
SUBMIT_RETRY_LIMIT = 4;
|
|
3646
4033
|
}
|
|
3647
4034
|
});
|
|
3648
4035
|
|
|
@@ -3651,20 +4038,25 @@ var dist_exports = {};
|
|
|
3651
4038
|
__export(dist_exports, {
|
|
3652
4039
|
CMUX_TIMEOUT: () => CMUX_TIMEOUT,
|
|
3653
4040
|
CmuxEventsBridge: () => CmuxEventsBridge,
|
|
4041
|
+
CmuxStoreSource: () => CmuxStoreSource,
|
|
3654
4042
|
DaemonCmux: () => DaemonCmux,
|
|
4043
|
+
NativeHookSource: () => NativeHookSource,
|
|
3655
4044
|
NotifierRegistry: () => NotifierRegistry,
|
|
3656
4045
|
RuntimeRegistry: () => RuntimeRegistry,
|
|
3657
4046
|
WorkspaceRegistry: () => WorkspaceRegistry,
|
|
3658
4047
|
classifyStartupSurface: () => classifyStartupSurface,
|
|
3659
4048
|
cmuxLocal: () => cmuxLocal,
|
|
4049
|
+
confirmedSendToPane: () => confirmedSendToPane,
|
|
3660
4050
|
createCmuxDriver: () => createCmuxDriver,
|
|
3661
4051
|
createCmuxNotifier: () => createCmuxNotifier,
|
|
3662
4052
|
createObsidianDriver: () => createObsidianDriver,
|
|
3663
4053
|
deriveRunState: () => deriveRunState,
|
|
3664
4054
|
findCrew: () => findCrew,
|
|
3665
4055
|
getFreePort: () => getFreePort,
|
|
4056
|
+
installClaudeHooks: () => installClaudeHooks,
|
|
3666
4057
|
isInsideCmux: () => isInsideCmux,
|
|
3667
4058
|
listProjectCrews: () => listProjectCrews,
|
|
4059
|
+
mapSubToLifecycle: () => mapSubToLifecycle,
|
|
3668
4060
|
resolveCaptainWorkspace: () => resolveCaptainWorkspace,
|
|
3669
4061
|
sendFirstTurnWhenReady: () => sendFirstTurnWhenReady
|
|
3670
4062
|
});
|
|
@@ -3675,6 +4067,8 @@ var init_dist3 = __esm({
|
|
|
3675
4067
|
init_workspaces2();
|
|
3676
4068
|
init_events_bridge();
|
|
3677
4069
|
init_daemon_cmux();
|
|
4070
|
+
init_cmux_store_source();
|
|
4071
|
+
init_native_hook_source();
|
|
3678
4072
|
init_crew_pane();
|
|
3679
4073
|
}
|
|
3680
4074
|
});
|
|
@@ -4604,13 +4998,99 @@ var init_app_server_client = __esm({
|
|
|
4604
4998
|
}
|
|
4605
4999
|
});
|
|
4606
5000
|
|
|
5001
|
+
// packages/agents/dist/codex/codex-app-server-source.js
|
|
5002
|
+
function toSnapshot(ev) {
|
|
5003
|
+
const now = Date.now();
|
|
5004
|
+
switch (ev.type) {
|
|
5005
|
+
// ── running: a turn is live ──────────────────────────────────────────────
|
|
5006
|
+
case "task.started":
|
|
5007
|
+
case "task.reattached":
|
|
5008
|
+
case "task.turn.started":
|
|
5009
|
+
case "task.delta":
|
|
5010
|
+
case "task.progress":
|
|
5011
|
+
return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
|
|
5012
|
+
// ── idle: turn ended, crew alive, awaiting next input ────────────────────
|
|
5013
|
+
// task.failed: the turn ended with an error, but the crew process is alive.
|
|
5014
|
+
// task.session.ended: process is gone (alive:false) — signals liveness loss.
|
|
5015
|
+
case "task.turn.completed":
|
|
5016
|
+
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
5017
|
+
case "task.failed":
|
|
5018
|
+
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
5019
|
+
case "task.session.ended":
|
|
5020
|
+
return { taskId: ev.id, state: "idle", alive: false, origin: "agent", at: now };
|
|
5021
|
+
// ── needsInput: crew is blocked on a human ───────────────────────────────
|
|
5022
|
+
case "task.approval.requested":
|
|
5023
|
+
return {
|
|
5024
|
+
taskId: ev.id,
|
|
5025
|
+
state: "needsInput",
|
|
5026
|
+
alive: true,
|
|
5027
|
+
origin: "agent",
|
|
5028
|
+
at: now,
|
|
5029
|
+
detail: { note: ev.question, reason: ev.kind }
|
|
5030
|
+
};
|
|
5031
|
+
case "task.input.requested":
|
|
5032
|
+
return {
|
|
5033
|
+
taskId: ev.id,
|
|
5034
|
+
state: "needsInput",
|
|
5035
|
+
alive: true,
|
|
5036
|
+
origin: "agent",
|
|
5037
|
+
at: now,
|
|
5038
|
+
detail: { note: ev.question }
|
|
5039
|
+
};
|
|
5040
|
+
// ── terminal / notify-only — ignored ────────────────────────────────────
|
|
5041
|
+
// task.done, task.blocked, task.cancelled: terminal state from crew signal only.
|
|
5042
|
+
// task.session, task.stalled, task.quiet, task.idle, task.timeout, etc.: no-op.
|
|
5043
|
+
default:
|
|
5044
|
+
return null;
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
var CodexAppServerSource;
|
|
5048
|
+
var init_codex_app_server_source = __esm({
|
|
5049
|
+
"packages/agents/dist/codex/codex-app-server-source.js"() {
|
|
5050
|
+
CodexAppServerSource = class {
|
|
5051
|
+
name = "codex-appserver";
|
|
5052
|
+
deps;
|
|
5053
|
+
/** taskId → last reported snapshot (for snapshot() liveness floor). */
|
|
5054
|
+
cache = /* @__PURE__ */ new Map();
|
|
5055
|
+
start(deps) {
|
|
5056
|
+
this.deps = deps;
|
|
5057
|
+
}
|
|
5058
|
+
stop() {
|
|
5059
|
+
this.deps = void 0;
|
|
5060
|
+
this.cache.clear();
|
|
5061
|
+
}
|
|
5062
|
+
/** Returns the last-reported snapshot for a known crew (liveness floor). */
|
|
5063
|
+
snapshot(taskId) {
|
|
5064
|
+
return this.cache.get(taskId);
|
|
5065
|
+
}
|
|
5066
|
+
/**
|
|
5067
|
+
* Feed a ControlEvent from CodexInteractiveDriver into this source.
|
|
5068
|
+
* The daemon wires: emit = (ev) => { source.observe(ev); handle(ev); }
|
|
5069
|
+
*
|
|
5070
|
+
* All events that carry lifecycle meaning for a codex crew are mapped to a
|
|
5071
|
+
* LifecycleSnapshot and reported. Events that are terminal signals (task.done,
|
|
5072
|
+
* task.cancelled, task.blocked) or notify-only (task.stalled, task.quiet, etc.)
|
|
5073
|
+
* are ignored — terminal state still comes exclusively from `squadrant crew signal`
|
|
5074
|
+
* (anti-#2576 invariant).
|
|
5075
|
+
*/
|
|
5076
|
+
observe(ev) {
|
|
5077
|
+
const snap = toSnapshot(ev);
|
|
5078
|
+
if (!snap || !this.deps)
|
|
5079
|
+
return;
|
|
5080
|
+
this.cache.set(snap.taskId, snap);
|
|
5081
|
+
this.deps.report(snap);
|
|
5082
|
+
}
|
|
5083
|
+
};
|
|
5084
|
+
}
|
|
5085
|
+
});
|
|
5086
|
+
|
|
4607
5087
|
// packages/agents/dist/codex/config.js
|
|
4608
5088
|
import { readFile as readFile5 } from "fs/promises";
|
|
4609
|
-
import { homedir as
|
|
4610
|
-
import { join as
|
|
5089
|
+
import { homedir as homedir11 } from "os";
|
|
5090
|
+
import { join as join16 } from "path";
|
|
4611
5091
|
async function resolveCodexModel() {
|
|
4612
|
-
const home = process.env["CODEX_HOME"] ??
|
|
4613
|
-
const configPath =
|
|
5092
|
+
const home = process.env["CODEX_HOME"] ?? join16(homedir11(), ".codex");
|
|
5093
|
+
const configPath = join16(home, "config.toml");
|
|
4614
5094
|
let text;
|
|
4615
5095
|
try {
|
|
4616
5096
|
text = await readFile5(configPath, "utf8");
|
|
@@ -5131,9 +5611,9 @@ var init_sse_bridge = __esm({
|
|
|
5131
5611
|
|
|
5132
5612
|
// packages/agents/dist/interactive/claude.js
|
|
5133
5613
|
import { execSync as execSync7 } from "child_process";
|
|
5134
|
-
import { readFileSync as
|
|
5135
|
-
import { homedir as
|
|
5136
|
-
import { join as
|
|
5614
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
5615
|
+
import { homedir as homedir12 } from "os";
|
|
5616
|
+
import { join as join17 } from "path";
|
|
5137
5617
|
function probeClaudeSettingsFlag() {
|
|
5138
5618
|
try {
|
|
5139
5619
|
const help = execSync7("claude --help", { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
@@ -5184,11 +5664,11 @@ function deriveTranscriptPath(sessionId, cwd) {
|
|
|
5184
5664
|
if (!sessionId || !cwd)
|
|
5185
5665
|
return null;
|
|
5186
5666
|
const escaped = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5187
|
-
return
|
|
5667
|
+
return join17(homedir12(), ".claude", "projects", escaped, `${sessionId}.jsonl`);
|
|
5188
5668
|
}
|
|
5189
5669
|
function readLastAssistantText(transcriptPath) {
|
|
5190
5670
|
try {
|
|
5191
|
-
const raw =
|
|
5671
|
+
const raw = readFileSync10(transcriptPath, "utf-8");
|
|
5192
5672
|
const lines = raw.split(/\r?\n/);
|
|
5193
5673
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
5194
5674
|
const line = lines[i].trim();
|
|
@@ -5305,6 +5785,18 @@ function classifyPaneTail(tail) {
|
|
|
5305
5785
|
}
|
|
5306
5786
|
return { kind: "approval", text: "Crew is awaiting permission approval." };
|
|
5307
5787
|
}
|
|
5788
|
+
const hasPickerFooter = cleaned.some((c) => c != null && PICKER_FOOTER_RE.test(c));
|
|
5789
|
+
if (options.length >= 2 && hasPickerFooter) {
|
|
5790
|
+
const firstOptCi = options[0].ci;
|
|
5791
|
+
for (let i = firstOptCi - 1; i >= 0; i--) {
|
|
5792
|
+
const c = cleaned[i];
|
|
5793
|
+
if (c == null)
|
|
5794
|
+
continue;
|
|
5795
|
+
if (c.endsWith("?"))
|
|
5796
|
+
return { kind: "question", text: c };
|
|
5797
|
+
}
|
|
5798
|
+
return { kind: "question", text: "Crew is awaiting a choice." };
|
|
5799
|
+
}
|
|
5308
5800
|
const region = cleaned.filter((c) => c != null).join("\n");
|
|
5309
5801
|
const q = detectTrailingQuestion(region);
|
|
5310
5802
|
if (q)
|
|
@@ -5332,7 +5824,7 @@ function stripChrome(raw) {
|
|
|
5332
5824
|
return null;
|
|
5333
5825
|
return trimmed;
|
|
5334
5826
|
}
|
|
5335
|
-
var ERROR_BANNER_RE, OPTION_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5827
|
+
var ERROR_BANNER_RE, OPTION_RE, PICKER_FOOTER_RE, PURE_CHROME_RE, STATUS_LINE_RE;
|
|
5336
5828
|
var init_pane_classifier = __esm({
|
|
5337
5829
|
"packages/agents/dist/interactive/pane-classifier.js"() {
|
|
5338
5830
|
init_claude2();
|
|
@@ -5346,6 +5838,7 @@ var init_pane_classifier = __esm({
|
|
|
5346
5838
|
/\bmaximum\s+retries\b/i
|
|
5347
5839
|
];
|
|
5348
5840
|
OPTION_RE = /^[❯>›]?\s*(\d+)\.\s+(.*\S)\s*$/;
|
|
5841
|
+
PICKER_FOOTER_RE = /↑↓\s*select|enter\s+submit|esc\s+dismiss/i;
|
|
5349
5842
|
PURE_CHROME_RE = /^[\s─━│┃╭╮╰╯┌┐└┘├┤┬┴┼═║╔╗╚╝╠╣╦╩╬▁▂▃▄▅▆▇█▔▏▕]+$/;
|
|
5350
5843
|
STATUS_LINE_RE = /accept edits on|shift\+tab|⏵⏵|\? for shortcuts|esc to interrupt|tokens? (used|left)|context left/i;
|
|
5351
5844
|
}
|
|
@@ -5576,6 +6069,7 @@ var dist_exports2 = {};
|
|
|
5576
6069
|
__export(dist_exports2, {
|
|
5577
6070
|
AppServerClient: () => AppServerClient,
|
|
5578
6071
|
CapabilityRegistry: () => CapabilityRegistry,
|
|
6072
|
+
CodexAppServerSource: () => CodexAppServerSource,
|
|
5579
6073
|
CodexInteractiveDriver: () => CodexInteractiveDriver,
|
|
5580
6074
|
HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
|
|
5581
6075
|
MARKER_END: () => MARKER_END,
|
|
@@ -5614,6 +6108,7 @@ var init_dist4 = __esm({
|
|
|
5614
6108
|
init_drivers();
|
|
5615
6109
|
init_projection2();
|
|
5616
6110
|
init_app_server_client();
|
|
6111
|
+
init_codex_app_server_source();
|
|
5617
6112
|
init_driver();
|
|
5618
6113
|
init_normalize();
|
|
5619
6114
|
init_sse_bridge();
|
|
@@ -5630,11 +6125,11 @@ var init_dist4 = __esm({
|
|
|
5630
6125
|
// packages/cli/src/index.ts
|
|
5631
6126
|
init_dist();
|
|
5632
6127
|
init_dist2();
|
|
5633
|
-
import { Command as
|
|
5634
|
-
import { readFileSync as
|
|
6128
|
+
import { Command as Command29 } from "commander";
|
|
6129
|
+
import { readFileSync as readFileSync13, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
5635
6130
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
5636
|
-
import { dirname as dirname7, join as
|
|
5637
|
-
import { homedir as
|
|
6131
|
+
import { dirname as dirname7, join as join26 } from "path";
|
|
6132
|
+
import { homedir as homedir18 } from "os";
|
|
5638
6133
|
|
|
5639
6134
|
// packages/cli/src/commands/doctor.ts
|
|
5640
6135
|
init_dist();
|
|
@@ -5651,10 +6146,10 @@ import chalk3 from "chalk";
|
|
|
5651
6146
|
// packages/cli/src/commands/health-view.ts
|
|
5652
6147
|
init_dist2();
|
|
5653
6148
|
init_dist2();
|
|
5654
|
-
import { homedir as
|
|
5655
|
-
import { join as
|
|
6149
|
+
import { homedir as homedir10 } from "os";
|
|
6150
|
+
import { join as join15 } from "path";
|
|
5656
6151
|
import chalk2 from "chalk";
|
|
5657
|
-
var SOCK =
|
|
6152
|
+
var SOCK = join15(homedir10(), ".config", "squadrant", "squadrant.sock");
|
|
5658
6153
|
async function queryHealth(project) {
|
|
5659
6154
|
try {
|
|
5660
6155
|
const reply = await sendRequest(SOCK, { kind: "health", project });
|
|
@@ -6488,9 +6983,9 @@ init_dist4();
|
|
|
6488
6983
|
import { Command as Command8 } from "commander";
|
|
6489
6984
|
import { createConnection as createConnection3 } from "net";
|
|
6490
6985
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6491
|
-
import { homedir as
|
|
6492
|
-
import { join as
|
|
6493
|
-
import { mkdirSync as
|
|
6986
|
+
import { homedir as homedir14 } from "os";
|
|
6987
|
+
import { join as join19 } from "path";
|
|
6988
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
6494
6989
|
|
|
6495
6990
|
// packages/cli/src/commands/crew-output.ts
|
|
6496
6991
|
function tailLines(text, maxLines = 40, maxBytes = 4096) {
|
|
@@ -6548,11 +7043,11 @@ init_dist2();
|
|
|
6548
7043
|
import { Command as Command6 } from "commander";
|
|
6549
7044
|
import chalk8 from "chalk";
|
|
6550
7045
|
import { createConnection as createConnection2 } from "net";
|
|
6551
|
-
import { homedir as
|
|
6552
|
-
import { join as
|
|
7046
|
+
import { homedir as homedir13 } from "os";
|
|
7047
|
+
import { join as join18 } from "path";
|
|
6553
7048
|
import { createInterface } from "readline";
|
|
6554
7049
|
function socketPath() {
|
|
6555
|
-
return process.env.SQUADRANTD_SOCK ??
|
|
7050
|
+
return process.env.SQUADRANTD_SOCK ?? join18(homedir13(), ".config", "squadrant", "squadrant.sock");
|
|
6556
7051
|
}
|
|
6557
7052
|
function rule(width, ch = "\u2500") {
|
|
6558
7053
|
return ch.repeat(Math.max(0, width));
|
|
@@ -6788,7 +7283,7 @@ var crewChatCommand = new Command7("chat").description("[DEPRECATED] alias for `
|
|
|
6788
7283
|
});
|
|
6789
7284
|
|
|
6790
7285
|
// packages/cli/src/commands/crew-control.ts
|
|
6791
|
-
var SOCK2 =
|
|
7286
|
+
var SOCK2 = join19(homedir14(), ".config", "squadrant", "squadrant.sock");
|
|
6792
7287
|
var CODEX_FIRST_TURN_DELAY_MS = 1500;
|
|
6793
7288
|
async function sendCodexFirstTurn(taskId, text) {
|
|
6794
7289
|
await new Promise((r) => setTimeout(r, CODEX_FIRST_TURN_DELAY_MS));
|
|
@@ -6893,10 +7388,10 @@ function buildSignalRequest(signal, o) {
|
|
|
6893
7388
|
return { kind: "event", project, event };
|
|
6894
7389
|
}
|
|
6895
7390
|
function defaultWriteResult(id, payload) {
|
|
6896
|
-
const dir =
|
|
6897
|
-
|
|
6898
|
-
const file =
|
|
6899
|
-
|
|
7391
|
+
const dir = join19(homedir14(), ".config", "squadrant", "state", "_results");
|
|
7392
|
+
mkdirSync7(dir, { recursive: true });
|
|
7393
|
+
const file = join19(dir, `${id}.txt`);
|
|
7394
|
+
writeFileSync8(file, payload);
|
|
6900
7395
|
return file;
|
|
6901
7396
|
}
|
|
6902
7397
|
function addControlPlaneCrewCommands(crew) {
|
|
@@ -6995,8 +7490,8 @@ addControlPlaneCrewCommands(crewControlCommand);
|
|
|
6995
7490
|
|
|
6996
7491
|
// packages/cli/src/lib/per-crew-settings.ts
|
|
6997
7492
|
init_dist4();
|
|
6998
|
-
import { mkdirSync as
|
|
6999
|
-
import { join as
|
|
7493
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
7494
|
+
import { join as join20 } from "path";
|
|
7000
7495
|
var CREW_PERMISSION_ALLOWLIST = [
|
|
7001
7496
|
// git — read + safe mutations (reset/clean/config intentionally excluded)
|
|
7002
7497
|
"Bash(git status:*)",
|
|
@@ -7087,24 +7582,24 @@ function mergeCrewPermissions(settings) {
|
|
|
7087
7582
|
return next;
|
|
7088
7583
|
}
|
|
7089
7584
|
function writePerCrewSettingsLocal(o) {
|
|
7090
|
-
const dir =
|
|
7091
|
-
|
|
7092
|
-
const file =
|
|
7585
|
+
const dir = join20(o.projectCwd, ".claude");
|
|
7586
|
+
mkdirSync8(dir, { recursive: true });
|
|
7587
|
+
const file = join20(dir, "settings.local.json");
|
|
7093
7588
|
let existing = {};
|
|
7094
7589
|
try {
|
|
7095
|
-
const raw = healStaleCockpitRefs(
|
|
7590
|
+
const raw = healStaleCockpitRefs(readFileSync11(file, "utf-8"));
|
|
7096
7591
|
existing = JSON.parse(raw);
|
|
7097
7592
|
} catch {
|
|
7098
7593
|
}
|
|
7099
7594
|
const withHooks = mergeClaudeHooks(existing, o.hookCmd ?? "squadrant crew _hook");
|
|
7100
7595
|
const merged = mergeCrewPermissions(withHooks);
|
|
7101
|
-
|
|
7596
|
+
writeFileSync9(file, JSON.stringify(merged, null, 2));
|
|
7102
7597
|
return file;
|
|
7103
7598
|
}
|
|
7104
7599
|
function writePerCrewOpencodeConfig(o) {
|
|
7105
|
-
const dir =
|
|
7106
|
-
|
|
7107
|
-
const file =
|
|
7600
|
+
const dir = join20(o.stateRoot, o.project, o.taskId);
|
|
7601
|
+
mkdirSync8(dir, { recursive: true });
|
|
7602
|
+
const file = join20(dir, "opencode.json");
|
|
7108
7603
|
const config = {
|
|
7109
7604
|
permission: {
|
|
7110
7605
|
read: "allow",
|
|
@@ -7119,7 +7614,7 @@ function writePerCrewOpencodeConfig(o) {
|
|
|
7119
7614
|
external_directory: { "**": "allow" }
|
|
7120
7615
|
}
|
|
7121
7616
|
};
|
|
7122
|
-
|
|
7617
|
+
writeFileSync9(file, JSON.stringify(config, null, 2));
|
|
7123
7618
|
return file;
|
|
7124
7619
|
}
|
|
7125
7620
|
|
|
@@ -7160,7 +7655,10 @@ async function runCrewSend2(project, name, message) {
|
|
|
7160
7655
|
listTasks: async (p) => await squadrantdCall({ kind: "list", project: p }),
|
|
7161
7656
|
emitEvent: async (p, event) => {
|
|
7162
7657
|
await squadrantdCall({ kind: "event", project: p, event });
|
|
7163
|
-
}
|
|
7658
|
+
},
|
|
7659
|
+
// #448: use paste-settle-Enter confirmation for follow-up sends (same guard
|
|
7660
|
+
// as first-turn #447) so large messages don't strand in paste mode.
|
|
7661
|
+
sendToPane: (pane, msg) => confirmedSendToPane(runtime, pane, msg)
|
|
7164
7662
|
});
|
|
7165
7663
|
}
|
|
7166
7664
|
async function runCrewRead2(project, name) {
|
|
@@ -7409,8 +7907,8 @@ init_dist();
|
|
|
7409
7907
|
init_dist3();
|
|
7410
7908
|
import { Command as Command11 } from "commander";
|
|
7411
7909
|
import { execSync as execSync10 } from "child_process";
|
|
7412
|
-
import { homedir as
|
|
7413
|
-
import { join as
|
|
7910
|
+
import { homedir as homedir16 } from "os";
|
|
7911
|
+
import { join as join22 } from "path";
|
|
7414
7912
|
import chalk12 from "chalk";
|
|
7415
7913
|
|
|
7416
7914
|
// packages/web/dist/read-status.js
|
|
@@ -7606,9 +8104,9 @@ function mergeSnapshot(daemon, external, now) {
|
|
|
7606
8104
|
// packages/web/dist/probes.js
|
|
7607
8105
|
init_dist();
|
|
7608
8106
|
init_dist();
|
|
7609
|
-
import { join as
|
|
7610
|
-
import { homedir as
|
|
7611
|
-
import { existsSync as
|
|
8107
|
+
import { join as join21 } from "path";
|
|
8108
|
+
import { homedir as homedir15 } from "os";
|
|
8109
|
+
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
7612
8110
|
import { execFile as execFile3 } from "child_process";
|
|
7613
8111
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
7614
8112
|
var AGENT_CLIS = ["claude", "codex", "gemini", "opencode"];
|
|
@@ -7644,7 +8142,7 @@ function vaultProbe(run, dir) {
|
|
|
7644
8142
|
return { state: "unknown", detail: "no vault configured" };
|
|
7645
8143
|
if (!run.pathExists(dir))
|
|
7646
8144
|
return { state: "gone", detail: "vault directory missing" };
|
|
7647
|
-
if (!run.pathExists(
|
|
8145
|
+
if (!run.pathExists(join21(dir, ".obsidian")))
|
|
7648
8146
|
return { state: "gone", detail: "no .obsidian/ (not a vault)" };
|
|
7649
8147
|
return { state: "alive" };
|
|
7650
8148
|
} catch {
|
|
@@ -7712,13 +8210,13 @@ async function runExternalProbes(run, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
7712
8210
|
const sessions = probeSessions(run);
|
|
7713
8211
|
return { cmux: cmux2, agentClis, vaults, config: { parseable, projectPaths, sessions } };
|
|
7714
8212
|
}
|
|
7715
|
-
var SESSIONS_PATH =
|
|
8213
|
+
var SESSIONS_PATH = join21(homedir15(), ".config", "squadrant", "sessions.json");
|
|
7716
8214
|
function onPath(cli) {
|
|
7717
8215
|
const dirs = (process.env.PATH ?? "").split(":").filter(Boolean);
|
|
7718
|
-
return dirs.some((d) =>
|
|
8216
|
+
return dirs.some((d) => existsSync11(join21(d, cli)));
|
|
7719
8217
|
}
|
|
7720
8218
|
function readSessionsHashes() {
|
|
7721
|
-
const raw = JSON.parse(
|
|
8219
|
+
const raw = JSON.parse(readFileSync12(SESSIONS_PATH, "utf-8"));
|
|
7722
8220
|
const hashes = Object.values(raw.workspaces ?? {}).map((w) => w.templateHash).filter((h) => typeof h === "string" && h.length > 0);
|
|
7723
8221
|
return [...new Set(hashes)];
|
|
7724
8222
|
}
|
|
@@ -7732,7 +8230,7 @@ function defaultProbeRunners() {
|
|
|
7732
8230
|
}
|
|
7733
8231
|
}),
|
|
7734
8232
|
probeOnPath: async (cli) => onPath(cli),
|
|
7735
|
-
pathExists: (p) =>
|
|
8233
|
+
pathExists: (p) => existsSync11(p),
|
|
7736
8234
|
loadConfig: () => loadConfig(),
|
|
7737
8235
|
loadSessionsHashes: () => readSessionsHashes()
|
|
7738
8236
|
};
|
|
@@ -8385,7 +8883,7 @@ async function startWebServer(opts) {
|
|
|
8385
8883
|
|
|
8386
8884
|
// packages/cli/src/commands/dashboard.ts
|
|
8387
8885
|
init_dist();
|
|
8388
|
-
var SOCK3 =
|
|
8886
|
+
var SOCK3 = join22(homedir16(), ".config", "squadrant", "squadrant.sock");
|
|
8389
8887
|
function detectCurrentWorkspace2() {
|
|
8390
8888
|
const out = execSync10(`"${resolveCmuxBin()}" current-workspace`, { encoding: "utf-8" }).trim();
|
|
8391
8889
|
const match = out.match(/workspace:\d+/);
|
|
@@ -8469,13 +8967,87 @@ init_dist();
|
|
|
8469
8967
|
init_dist4();
|
|
8470
8968
|
init_dist3();
|
|
8471
8969
|
init_dist2();
|
|
8472
|
-
init_dist2();
|
|
8473
8970
|
import { Command as Command12 } from "commander";
|
|
8474
8971
|
import { execSync as execSync11 } from "child_process";
|
|
8475
8972
|
import fs20 from "fs";
|
|
8476
8973
|
import path23 from "path";
|
|
8477
8974
|
import os12 from "os";
|
|
8478
8975
|
import chalk13 from "chalk";
|
|
8976
|
+
|
|
8977
|
+
// packages/cli/src/commands/launch-interactive.ts
|
|
8978
|
+
import checkbox, { Separator } from "@inquirer/checkbox";
|
|
8979
|
+
function getYesterday() {
|
|
8980
|
+
const d = /* @__PURE__ */ new Date();
|
|
8981
|
+
d.setDate(d.getDate() - 1);
|
|
8982
|
+
return d.toISOString().slice(0, 10);
|
|
8983
|
+
}
|
|
8984
|
+
function partitionByYesterday(entries, yesterday) {
|
|
8985
|
+
const y = [];
|
|
8986
|
+
const r = [];
|
|
8987
|
+
for (const e of entries) {
|
|
8988
|
+
if (e.lastLaunched === yesterday) {
|
|
8989
|
+
y.push(e);
|
|
8990
|
+
} else {
|
|
8991
|
+
r.push(e);
|
|
8992
|
+
}
|
|
8993
|
+
}
|
|
8994
|
+
return { yesterday: y, rest: r };
|
|
8995
|
+
}
|
|
8996
|
+
async function selectCaptainsInteractive(entries, yesterday = getYesterday()) {
|
|
8997
|
+
const { yesterday: yesterdayEntries, rest: restEntries } = partitionByYesterday(entries, yesterday);
|
|
8998
|
+
if (yesterdayEntries.length === 0) {
|
|
8999
|
+
const result2 = await checkbox({
|
|
9000
|
+
message: "Select captains to launch:",
|
|
9001
|
+
choices: entries.map((e) => ({
|
|
9002
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9003
|
+
value: e.projectName,
|
|
9004
|
+
checked: false
|
|
9005
|
+
})),
|
|
9006
|
+
pageSize: 20
|
|
9007
|
+
});
|
|
9008
|
+
return result2;
|
|
9009
|
+
}
|
|
9010
|
+
const initialChoices = [
|
|
9011
|
+
new Separator("\u2500\u2500 Opened yesterday \u2500\u2500"),
|
|
9012
|
+
...yesterdayEntries.map((e) => ({
|
|
9013
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9014
|
+
value: e.projectName,
|
|
9015
|
+
checked: true
|
|
9016
|
+
})),
|
|
9017
|
+
new Separator(),
|
|
9018
|
+
{ name: "Show all projects", value: "__show_all__", checked: false }
|
|
9019
|
+
];
|
|
9020
|
+
const result = await checkbox({
|
|
9021
|
+
message: "Select captains to launch:",
|
|
9022
|
+
choices: initialChoices,
|
|
9023
|
+
pageSize: 20
|
|
9024
|
+
});
|
|
9025
|
+
if (result.includes("__show_all__")) {
|
|
9026
|
+
const allChecked = yesterdayEntries.map((e) => e.projectName);
|
|
9027
|
+
const result2 = await checkbox({
|
|
9028
|
+
message: "Select captains to launch (all projects):",
|
|
9029
|
+
choices: [
|
|
9030
|
+
new Separator("\u2500\u2500 Opened yesterday \u2500\u2500"),
|
|
9031
|
+
...yesterdayEntries.map((e) => ({
|
|
9032
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9033
|
+
value: e.projectName,
|
|
9034
|
+
checked: allChecked.includes(e.projectName)
|
|
9035
|
+
})),
|
|
9036
|
+
...restEntries.map((e) => ({
|
|
9037
|
+
name: `${e.captainName} (${e.projectName})`,
|
|
9038
|
+
value: e.projectName,
|
|
9039
|
+
checked: false
|
|
9040
|
+
}))
|
|
9041
|
+
],
|
|
9042
|
+
pageSize: 30
|
|
9043
|
+
});
|
|
9044
|
+
return result2;
|
|
9045
|
+
}
|
|
9046
|
+
return result;
|
|
9047
|
+
}
|
|
9048
|
+
|
|
9049
|
+
// packages/cli/src/commands/launch.ts
|
|
9050
|
+
init_dist2();
|
|
8479
9051
|
var CMUX_APP = "/Applications/cmux.app";
|
|
8480
9052
|
var TEMPLATES_DIR4 = path23.join(os12.homedir(), ".config", "squadrant", "templates");
|
|
8481
9053
|
var SESSIONS_PATH2 = path23.join(os12.homedir(), ".config", "squadrant", "sessions.json");
|
|
@@ -8559,12 +9131,43 @@ var launchCommand = new Command12("launch").description(
|
|
|
8559
9131
|
}
|
|
8560
9132
|
console.log("");
|
|
8561
9133
|
} else if (!project) {
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
9134
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
9135
|
+
console.error(
|
|
9136
|
+
chalk13.red(
|
|
9137
|
+
"\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"
|
|
9138
|
+
)
|
|
9139
|
+
);
|
|
9140
|
+
process.exit(1);
|
|
9141
|
+
}
|
|
9142
|
+
ensureCmuxReady();
|
|
9143
|
+
const sessions = loadSessions(SESSIONS_PATH2);
|
|
9144
|
+
const entries = Object.entries(config.projects).map(([name, proj]) => ({
|
|
9145
|
+
projectName: name,
|
|
9146
|
+
captainName: proj.captainName,
|
|
9147
|
+
lastLaunched: sessions.workspaces[proj.captainName]?.lastLaunched ?? null
|
|
9148
|
+
}));
|
|
9149
|
+
const selected = await selectCaptainsInteractive(entries);
|
|
9150
|
+
if (selected.length === 0) {
|
|
9151
|
+
console.log(chalk13.yellow("\n No captains selected.\n"));
|
|
9152
|
+
return;
|
|
9153
|
+
}
|
|
9154
|
+
console.log(chalk13.bold(`
|
|
9155
|
+
Launching ${selected.length} captain workspace(s) in parallel
|
|
9156
|
+
`));
|
|
9157
|
+
await Promise.all(selected.map(async (name) => {
|
|
9158
|
+
const proj = config.projects[name];
|
|
9159
|
+
const projPath = resolveHome(proj.path);
|
|
9160
|
+
const spokePath = resolveHome(proj.spokeVault);
|
|
9161
|
+
if (!fs20.existsSync(spokePath)) {
|
|
9162
|
+
const spokeDriver = new WorkspaceRegistry({ obsidian: createObsidianDriver }).forProject(name, config);
|
|
9163
|
+
await ensureSpokeLayout(spokeDriver);
|
|
9164
|
+
console.log(chalk13.cyan(` \u2714 Created spoke vault at ${spokePath}`));
|
|
9165
|
+
}
|
|
9166
|
+
console.log(chalk13.bold(`
|
|
9167
|
+
Captain: ${proj.captainName} (${name})`));
|
|
9168
|
+
await launchOne(proj.captainName, "captain", projPath, config.defaults.permissions?.captain || "auto", false, true, name);
|
|
9169
|
+
}));
|
|
9170
|
+
console.log("");
|
|
8568
9171
|
} else {
|
|
8569
9172
|
if (!config.projects[project]) {
|
|
8570
9173
|
console.error(
|
|
@@ -9561,7 +10164,7 @@ init_dist2();
|
|
|
9561
10164
|
import { Command as Command22 } from "commander";
|
|
9562
10165
|
import fs25 from "fs";
|
|
9563
10166
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
9564
|
-
import { dirname as dirname5, join as
|
|
10167
|
+
import { dirname as dirname5, join as join23 } from "path";
|
|
9565
10168
|
import chalk22 from "chalk";
|
|
9566
10169
|
function runConfigCheck(opts) {
|
|
9567
10170
|
const raw = JSON.parse(fs25.readFileSync(opts.configPath, "utf-8"));
|
|
@@ -9697,7 +10300,7 @@ configCommand.command("set").description("Write a config value by dotted key (e.
|
|
|
9697
10300
|
}
|
|
9698
10301
|
});
|
|
9699
10302
|
function readPkgVersion2() {
|
|
9700
|
-
const pkgPath =
|
|
10303
|
+
const pkgPath = join23(dirname5(fileURLToPath5(import.meta.url)), "..", "package.json");
|
|
9701
10304
|
return JSON.parse(fs25.readFileSync(pkgPath, "utf-8")).version;
|
|
9702
10305
|
}
|
|
9703
10306
|
|
|
@@ -9968,12 +10571,12 @@ var effortCommand = new Command26("effort").description("Get or set the global c
|
|
|
9968
10571
|
// packages/cli/src/commands/telegram.ts
|
|
9969
10572
|
init_dist();
|
|
9970
10573
|
init_dist2();
|
|
9971
|
-
import { join as
|
|
10574
|
+
import { join as join24, dirname as dirname6 } from "path";
|
|
9972
10575
|
import { emitKeypressEvents } from "readline";
|
|
9973
10576
|
import { Command as Command27 } from "commander";
|
|
9974
10577
|
import chalk27 from "chalk";
|
|
9975
10578
|
function defaultStateRoot() {
|
|
9976
|
-
return
|
|
10579
|
+
return join24(dirname6(DEFAULT_CONFIG_PATH), "state");
|
|
9977
10580
|
}
|
|
9978
10581
|
async function questionMasked() {
|
|
9979
10582
|
return new Promise((resolve3) => {
|
|
@@ -10294,25 +10897,88 @@ telegramCommand.command("send").argument("<project>", "project whose topic recei
|
|
|
10294
10897
|
}
|
|
10295
10898
|
});
|
|
10296
10899
|
|
|
10900
|
+
// packages/cli/src/commands/hooks.ts
|
|
10901
|
+
init_dist2();
|
|
10902
|
+
init_dist4();
|
|
10903
|
+
import { Command as Command28 } from "commander";
|
|
10904
|
+
import { join as join25 } from "path";
|
|
10905
|
+
import { homedir as homedir17 } from "os";
|
|
10906
|
+
var SOCK4 = join25(homedir17(), ".config", "squadrant", "squadrant.sock");
|
|
10907
|
+
async function sendToSock(req) {
|
|
10908
|
+
await sendRequest(SOCK4, req);
|
|
10909
|
+
}
|
|
10910
|
+
function mapHookSub(sub, payload, taskId) {
|
|
10911
|
+
switch (sub) {
|
|
10912
|
+
case "session-start":
|
|
10913
|
+
case "prompt-submit":
|
|
10914
|
+
case "pre-tool-use":
|
|
10915
|
+
return { type: "task.progress", id: taskId, note: sub };
|
|
10916
|
+
case "stop":
|
|
10917
|
+
return mapClaudeHookToEvent("Stop", payload, taskId);
|
|
10918
|
+
case "notification":
|
|
10919
|
+
return mapClaudeHookToEvent("Notification", payload, taskId);
|
|
10920
|
+
case "ask-question": {
|
|
10921
|
+
const q = typeof payload?.question === "string" ? payload.question : "awaiting input";
|
|
10922
|
+
return { type: "task.input.requested", id: taskId, requestId: 0, question: q };
|
|
10923
|
+
}
|
|
10924
|
+
case "session-end":
|
|
10925
|
+
return mapClaudeHookToEvent("SessionEnd", payload, taskId);
|
|
10926
|
+
default:
|
|
10927
|
+
return null;
|
|
10928
|
+
}
|
|
10929
|
+
}
|
|
10930
|
+
function hooksCommand() {
|
|
10931
|
+
const hooks = new Command28("hooks").description("(internal) receive lifecycle hook events from agent processes");
|
|
10932
|
+
hooks.command("claude <sub>", { hidden: true }).description("internal: bridge a NativeHookSource claude hook to squadrantd").action(async (sub) => {
|
|
10933
|
+
const taskId = process.env.SQUADRANT_CREW_TASK_ID;
|
|
10934
|
+
const project = process.env.SQUADRANT_CREW_PROJECT;
|
|
10935
|
+
if (!taskId || !project) {
|
|
10936
|
+
process.exit(0);
|
|
10937
|
+
}
|
|
10938
|
+
let stdin = "";
|
|
10939
|
+
try {
|
|
10940
|
+
for await (const chunk of process.stdin) stdin += chunk;
|
|
10941
|
+
} catch {
|
|
10942
|
+
}
|
|
10943
|
+
let payload = void 0;
|
|
10944
|
+
if (stdin.trim()) {
|
|
10945
|
+
try {
|
|
10946
|
+
payload = JSON.parse(stdin);
|
|
10947
|
+
} catch {
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10950
|
+
const ev = mapHookSub(sub, payload, taskId);
|
|
10951
|
+
if (!ev) {
|
|
10952
|
+
process.exit(0);
|
|
10953
|
+
}
|
|
10954
|
+
try {
|
|
10955
|
+
await sendToSock({ kind: "event", project, event: ev });
|
|
10956
|
+
} catch {
|
|
10957
|
+
}
|
|
10958
|
+
process.exit(0);
|
|
10959
|
+
});
|
|
10960
|
+
return hooks;
|
|
10961
|
+
}
|
|
10962
|
+
|
|
10297
10963
|
// packages/cli/src/index.ts
|
|
10298
10964
|
init_dist();
|
|
10299
10965
|
init_dist();
|
|
10300
10966
|
init_dist();
|
|
10301
10967
|
var __dirname = dirname7(fileURLToPath6(import.meta.url));
|
|
10302
|
-
var pkg = JSON.parse(
|
|
10968
|
+
var pkg = JSON.parse(readFileSync13(join26(__dirname, "..", "package.json"), "utf-8"));
|
|
10303
10969
|
ensureRuntimeSynced({
|
|
10304
|
-
sourceRoot:
|
|
10305
|
-
runtimeRoot:
|
|
10970
|
+
sourceRoot: join26(__dirname, ".."),
|
|
10971
|
+
runtimeRoot: join26(homedir18(), ".config", "squadrant")
|
|
10306
10972
|
});
|
|
10307
10973
|
if (process.argv[2] !== "config") {
|
|
10308
10974
|
try {
|
|
10309
|
-
const cfgPath =
|
|
10310
|
-
if (
|
|
10311
|
-
const cfg = JSON.parse(
|
|
10975
|
+
const cfgPath = join26(homedir18(), ".config", "squadrant", "config.json");
|
|
10976
|
+
if (existsSync12(cfgPath)) {
|
|
10977
|
+
const cfg = JSON.parse(readFileSync13(cfgPath, "utf-8"));
|
|
10312
10978
|
if (needsCheck(cfg, pkg.version)) {
|
|
10313
10979
|
const items = detectDrift(cfg, getDefaultConfig());
|
|
10314
10980
|
if (items.length === 0) {
|
|
10315
|
-
|
|
10981
|
+
writeFileSync10(cfgPath, JSON.stringify(withStamp(cfg, pkg.version), null, 2) + "\n");
|
|
10316
10982
|
} else {
|
|
10317
10983
|
const from = cfg._squadrantVersion ?? "an earlier version";
|
|
10318
10984
|
process.stderr.write(
|
|
@@ -10331,7 +10997,7 @@ if (process.argv[2] !== "config") {
|
|
|
10331
10997
|
if (!process.env.SQUADRANT_DAEMON_SKIP) {
|
|
10332
10998
|
ensureDaemon();
|
|
10333
10999
|
}
|
|
10334
|
-
var program = new
|
|
11000
|
+
var program = new Command29();
|
|
10335
11001
|
program.name("squadrant").description("Multi-project orchestration for your coding agents (Claude, Codex, opencode, Gemini)").version(pkg.version);
|
|
10336
11002
|
program.addCommand(doctorCommand);
|
|
10337
11003
|
program.addCommand(initCommand);
|
|
@@ -10358,6 +11024,7 @@ program.addCommand(groupCommand);
|
|
|
10358
11024
|
program.addCommand(cmuxCommand);
|
|
10359
11025
|
program.addCommand(effortCommand);
|
|
10360
11026
|
program.addCommand(telegramCommand);
|
|
11027
|
+
program.addCommand(hooksCommand());
|
|
10361
11028
|
program.parseAsync().catch((e) => {
|
|
10362
11029
|
process.stderr.write(`error: ${e instanceof Error ? e.message : String(e)}
|
|
10363
11030
|
`);
|