squadrant 0.16.1 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +158 -38
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +19 -5
- package/dist/squadrantd.js.map +1 -1
- package/package.json +3 -2
- package/plugin/skills/captain-ops/SKILL.md +1 -1
- package/scripts/heavy-lock.mjs +129 -0
package/dist/index.js
CHANGED
|
@@ -1950,7 +1950,7 @@ async function appendToMailbox(opts) {
|
|
|
1950
1950
|
}));
|
|
1951
1951
|
}
|
|
1952
1952
|
async function appendCaptainMessage(opts) {
|
|
1953
|
-
|
|
1953
|
+
return appendEntry(opts.stateRoot, opts.project, (seq) => ({
|
|
1954
1954
|
seq,
|
|
1955
1955
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1956
1956
|
kind: "captain.message",
|
|
@@ -1978,6 +1978,18 @@ async function readCursor(opts) {
|
|
|
1978
1978
|
return null;
|
|
1979
1979
|
}
|
|
1980
1980
|
}
|
|
1981
|
+
async function waitForCaptainDelivery(opts) {
|
|
1982
|
+
const subscriber = opts.subscriber ?? "captain";
|
|
1983
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
1984
|
+
for (; ; ) {
|
|
1985
|
+
const cursor = await readCursor({ stateRoot: opts.stateRoot, project: opts.project, subscriber });
|
|
1986
|
+
if (cursor && cursor.lastAckedSeq >= opts.seq)
|
|
1987
|
+
return true;
|
|
1988
|
+
if (Date.now() >= deadline)
|
|
1989
|
+
return false;
|
|
1990
|
+
await new Promise((r) => setTimeout(r, opts.pollMs));
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1981
1993
|
async function writeCursor(opts) {
|
|
1982
1994
|
await fs9.mkdir(inboxDir(opts.stateRoot), { recursive: true });
|
|
1983
1995
|
const dest = cursorPath(opts.stateRoot, opts.project, opts.subscriber);
|
|
@@ -2389,6 +2401,9 @@ function reconcileLiveness(prev, next) {
|
|
|
2389
2401
|
}
|
|
2390
2402
|
if (next.startedAt >= prev.startedAt || next.lastState === "end")
|
|
2391
2403
|
return next;
|
|
2404
|
+
const prevAlive = prev.lastState === "start" && prev.pidAlive;
|
|
2405
|
+
if (!prevAlive && next.lastState === "start" && next.pidAlive)
|
|
2406
|
+
return next;
|
|
2392
2407
|
return prev;
|
|
2393
2408
|
}
|
|
2394
2409
|
var CREW_STALE_MS, CREW_GONE_MS, TERMINAL;
|
|
@@ -3394,9 +3409,15 @@ async function runLivenessTick(deps) {
|
|
|
3394
3409
|
return;
|
|
3395
3410
|
}
|
|
3396
3411
|
const seen = /* @__PURE__ */ new Set();
|
|
3412
|
+
const knownCaptainSessions = /* @__PURE__ */ new Map();
|
|
3413
|
+
for (const e of deps.registry.all()) {
|
|
3414
|
+
if (e.role === "captain")
|
|
3415
|
+
knownCaptainSessions.set(e.sessionId, e.project);
|
|
3416
|
+
}
|
|
3397
3417
|
const byProject = /* @__PURE__ */ new Map();
|
|
3398
3418
|
for (const r of records) {
|
|
3399
|
-
|
|
3419
|
+
const role = r.role === "captain" || knownCaptainSessions.get(r.sessionId) === r.project ? "captain" : r.role;
|
|
3420
|
+
if (role !== "captain")
|
|
3400
3421
|
continue;
|
|
3401
3422
|
let arr = byProject.get(r.project);
|
|
3402
3423
|
if (!arr) {
|
|
@@ -3428,10 +3449,14 @@ async function runLivenessTick(deps) {
|
|
|
3428
3449
|
logEntry(deps.log, project, deps.registry.get(project));
|
|
3429
3450
|
}
|
|
3430
3451
|
for (const e of deps.registry.all()) {
|
|
3431
|
-
if (e.role
|
|
3432
|
-
|
|
3433
|
-
|
|
3452
|
+
if (e.role !== "captain" || e.lastState !== "start" || seen.has(e.project))
|
|
3453
|
+
continue;
|
|
3454
|
+
if (e.pid == null || deps.isPidAlive(e.pid)) {
|
|
3455
|
+
deps.log?.(`[${e.role}/runtime] ${e.project} pid=${e.pid} missing from snapshot but not confirmed dead \u2014 leaving alive`);
|
|
3456
|
+
continue;
|
|
3434
3457
|
}
|
|
3458
|
+
deps.registry.markEnded(e.project, now);
|
|
3459
|
+
logEntry(deps.log, e.project, deps.registry.get(e.project));
|
|
3435
3460
|
}
|
|
3436
3461
|
if (deps.reap) {
|
|
3437
3462
|
for (const e of deps.registry.all()) {
|
|
@@ -5855,6 +5880,9 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
|
5855
5880
|
}
|
|
5856
5881
|
return { ...pane, title };
|
|
5857
5882
|
}
|
|
5883
|
+
function pickMostRecentTask(tasks) {
|
|
5884
|
+
return tasks.reduce((a, b) => (b.createdAt ?? 0) > (a.createdAt ?? 0) ? b : a);
|
|
5885
|
+
}
|
|
5858
5886
|
async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
5859
5887
|
const crew = await findCrewPane(runtime, workspaceId, project, name);
|
|
5860
5888
|
if (!crew) {
|
|
@@ -5865,8 +5893,8 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
|
5865
5893
|
throw new Error(blockedByModalMessage());
|
|
5866
5894
|
}
|
|
5867
5895
|
try {
|
|
5868
|
-
const
|
|
5869
|
-
const task =
|
|
5896
|
+
const matches = (await deps.listTasks(project)).filter((t) => t.name === name);
|
|
5897
|
+
const task = matches.length > 0 ? pickMostRecentTask(matches) : void 0;
|
|
5870
5898
|
if (task) {
|
|
5871
5899
|
if (TERMINAL_STATES.has(task.state)) {
|
|
5872
5900
|
await deps.emitEvent(project, { type: "task.reopened", id: task.id });
|
|
@@ -5882,8 +5910,7 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
|
5882
5910
|
throw new Error(blockedByModalMessage());
|
|
5883
5911
|
}
|
|
5884
5912
|
if (!delivered) {
|
|
5885
|
-
|
|
5886
|
-
`);
|
|
5913
|
+
throw new Error(`Message not delivered to crew '${name}' \u2014 the paste/submit could not be confirmed. Re-send with 'squadrant crew send ${project} ${name}'.`);
|
|
5887
5914
|
}
|
|
5888
5915
|
}
|
|
5889
5916
|
async function runCrewRead(project, name, runtime, workspaceId) {
|
|
@@ -5905,7 +5932,7 @@ async function runCrewClose(project, name, runtime, workspaceId, deps) {
|
|
|
5905
5932
|
matches = (await deps.listTasks(project)).filter((t) => t.name === name);
|
|
5906
5933
|
}
|
|
5907
5934
|
if (matches.length > 0) {
|
|
5908
|
-
const primary = matches
|
|
5935
|
+
const primary = pickMostRecentTask(matches);
|
|
5909
5936
|
taskId = primary.id;
|
|
5910
5937
|
if (primary.cwd && projRoot && primary.cwd !== projRoot) {
|
|
5911
5938
|
worktreeCwd = primary.cwd;
|
|
@@ -6144,6 +6171,7 @@ __export(dist_exports2, {
|
|
|
6144
6171
|
topicKey: () => topicKey,
|
|
6145
6172
|
topicName: () => topicName,
|
|
6146
6173
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
6174
|
+
waitForCaptainDelivery: () => waitForCaptainDelivery,
|
|
6147
6175
|
waitForWarmup: () => waitForWarmup,
|
|
6148
6176
|
writeCursor: () => writeCursor,
|
|
6149
6177
|
writeTelegramConfig: () => writeTelegramConfig
|
|
@@ -9316,16 +9344,23 @@ function isPermissionNotification(message) {
|
|
|
9316
9344
|
const lower = message.toLowerCase();
|
|
9317
9345
|
return lower.includes("permission") || lower.includes("approve");
|
|
9318
9346
|
}
|
|
9347
|
+
function installHookEntry(hooks, event, matcher, command) {
|
|
9348
|
+
if (!Array.isArray(hooks[event]))
|
|
9349
|
+
hooks[event] = [];
|
|
9350
|
+
const entries = hooks[event];
|
|
9351
|
+
const already = entries.some((m) => m?.matcher === matcher && Array.isArray(m?.hooks) && m.hooks.some((h) => typeof h?.command === "string" && h.command.includes(command)));
|
|
9352
|
+
if (!already) {
|
|
9353
|
+
entries.push({ matcher, hooks: [{ type: "command", command, timeout: 10 }] });
|
|
9354
|
+
}
|
|
9355
|
+
}
|
|
9319
9356
|
function mergeClaudeHooks(settings, hookCmd) {
|
|
9320
9357
|
const next = structuredClone(settings ?? {});
|
|
9321
9358
|
next.hooks ??= {};
|
|
9322
9359
|
for (const ev of EVENTS) {
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
next.hooks[ev].push({ matcher: "", hooks: [{ type: "command", command: `${hookCmd} ${ev}`, timeout: 10 }] });
|
|
9328
|
-
}
|
|
9360
|
+
installHookEntry(next.hooks, ev, "", `${hookCmd} ${ev}`);
|
|
9361
|
+
}
|
|
9362
|
+
for (const [ev, matcher] of MATCHED_EVENTS) {
|
|
9363
|
+
installHookEntry(next.hooks, ev, matcher, `${hookCmd} ${ev}`);
|
|
9329
9364
|
}
|
|
9330
9365
|
return next;
|
|
9331
9366
|
}
|
|
@@ -9405,8 +9440,32 @@ function resolveLastAssistantText(payload) {
|
|
|
9405
9440
|
}
|
|
9406
9441
|
return null;
|
|
9407
9442
|
}
|
|
9443
|
+
function formatAskUserQuestionPrompt(toolInput) {
|
|
9444
|
+
const questions = toolInput?.questions;
|
|
9445
|
+
if (!Array.isArray(questions) || questions.length === 0)
|
|
9446
|
+
return null;
|
|
9447
|
+
const parts = [];
|
|
9448
|
+
for (const q of questions) {
|
|
9449
|
+
if (!q || typeof q !== "object")
|
|
9450
|
+
continue;
|
|
9451
|
+
const text = q.question;
|
|
9452
|
+
if (typeof text !== "string" || !text.trim())
|
|
9453
|
+
continue;
|
|
9454
|
+
const options = Array.isArray(q.options) ? q.options : [];
|
|
9455
|
+
const labels = options.map((o) => o && typeof o.label === "string" ? o.label.trim() : null).filter((l) => !!l);
|
|
9456
|
+
parts.push(labels.length > 0 ? `${text.trim()} (options: ${labels.join(", ")})` : text.trim());
|
|
9457
|
+
}
|
|
9458
|
+
return parts.length > 0 ? parts.join(" | ") : null;
|
|
9459
|
+
}
|
|
9408
9460
|
function mapClaudeHookToEvent(event, payload, taskId) {
|
|
9409
9461
|
switch (event) {
|
|
9462
|
+
case "PreToolUse": {
|
|
9463
|
+
const toolName = payload?.tool_name;
|
|
9464
|
+
if (toolName !== "AskUserQuestion")
|
|
9465
|
+
return null;
|
|
9466
|
+
const question = formatAskUserQuestionPrompt(payload?.tool_input) ?? "crew opened an AskUserQuestion prompt (options unavailable)";
|
|
9467
|
+
return { type: "task.input.requested", id: taskId, requestId: nextAskUserQuestionRequestId++, question };
|
|
9468
|
+
}
|
|
9410
9469
|
case "Stop": {
|
|
9411
9470
|
const text = resolveLastAssistantText(payload);
|
|
9412
9471
|
const question = text ? detectTrailingQuestion2(text) : null;
|
|
@@ -9433,10 +9492,14 @@ function mapClaudeHookToEvent(event, payload, taskId) {
|
|
|
9433
9492
|
return null;
|
|
9434
9493
|
}
|
|
9435
9494
|
}
|
|
9436
|
-
var EVENTS, claudeInteractive;
|
|
9495
|
+
var EVENTS, MATCHED_EVENTS, nextAskUserQuestionRequestId, claudeInteractive;
|
|
9437
9496
|
var init_claude2 = __esm({
|
|
9438
9497
|
"packages/agents/dist/interactive/claude.js"() {
|
|
9439
9498
|
EVENTS = ["Stop", "SubagentStop", "SessionEnd", "PostToolUse", "Notification", "UserPromptSubmit"];
|
|
9499
|
+
MATCHED_EVENTS = [
|
|
9500
|
+
["PreToolUse", "AskUserQuestion"]
|
|
9501
|
+
];
|
|
9502
|
+
nextAskUserQuestionRequestId = Date.now();
|
|
9440
9503
|
claudeInteractive = {
|
|
9441
9504
|
provider: "claude",
|
|
9442
9505
|
tier: "strong",
|
|
@@ -9789,6 +9852,7 @@ __export(dist_exports4, {
|
|
|
9789
9852
|
createOpencodeEmitter: () => createOpencodeEmitter,
|
|
9790
9853
|
deriveTranscriptPath: () => deriveTranscriptPath,
|
|
9791
9854
|
detectTrailingQuestion: () => detectTrailingQuestion2,
|
|
9855
|
+
formatAskUserQuestionPrompt: () => formatAskUserQuestionPrompt,
|
|
9792
9856
|
getHeadlessAdapter: () => getHeadlessAdapter,
|
|
9793
9857
|
getInteractiveAdapter: () => getInteractiveAdapter,
|
|
9794
9858
|
isPermissionNotification: () => isPermissionNotification,
|
|
@@ -11285,6 +11349,22 @@ function defaultWriteResult(id, payload) {
|
|
|
11285
11349
|
writeFileSync10(file, payload);
|
|
11286
11350
|
return file;
|
|
11287
11351
|
}
|
|
11352
|
+
async function runCrewSignal(signal, o, deps) {
|
|
11353
|
+
const taskId = o.taskId ?? process.env.SQUADRANT_CREW_TASK_ID;
|
|
11354
|
+
const project = o.project ?? process.env.SQUADRANT_CREW_PROJECT;
|
|
11355
|
+
if (!taskId)
|
|
11356
|
+
throw new Error("not running under a crew (SQUADRANT_CREW_TASK_ID unset)");
|
|
11357
|
+
if (!project)
|
|
11358
|
+
throw new Error("not running under a crew (SQUADRANT_CREW_PROJECT unset)");
|
|
11359
|
+
const current = await deps.call(buildStatusRequest(project, taskId));
|
|
11360
|
+
if (current && TERMINAL_STATES.has(current.state)) {
|
|
11361
|
+
throw new Error(
|
|
11362
|
+
`Task ${taskId} is already terminal (state=${current.state}) \u2014 signal '${signal}' would be silently ignored by the daemon. Stop here: your task record was never reopened for this turn. Ask the captain to run 'squadrant crew send' to reopen it before signaling again.`
|
|
11363
|
+
);
|
|
11364
|
+
}
|
|
11365
|
+
const req = buildSignalRequest(signal, { ...o, writeResult: o.writeResult ?? defaultWriteResult });
|
|
11366
|
+
await deps.call(req);
|
|
11367
|
+
}
|
|
11288
11368
|
function addControlPlaneCrewCommands(crew) {
|
|
11289
11369
|
crew.command("dispatch <project> <task>").description("Dispatch a crew task via the control-plane daemon").requiredOption("--provider <p>", "claude|opencode|codex (gemini: experimental, headless not supported)").option("--mode <m>", "headless|interactive", "interactive").option("--cwd <dir>", "working dir for the crew (project/worktree); required for codex to edit code").action(async (project, task, opts) => {
|
|
11290
11370
|
const req = buildDispatchRequest({ project, task, provider: opts.provider, mode: opts.mode, cwd: opts.cwd });
|
|
@@ -11366,15 +11446,14 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
11366
11446
|
process.exit(2);
|
|
11367
11447
|
}
|
|
11368
11448
|
try {
|
|
11369
|
-
|
|
11449
|
+
await runCrewSignal(state, {
|
|
11370
11450
|
...opts.message !== void 0 ? { message: opts.message } : {},
|
|
11371
11451
|
...opts.question !== void 0 ? { question: opts.question } : {},
|
|
11372
11452
|
...opts.error !== void 0 ? { error: opts.error } : {},
|
|
11373
11453
|
...opts.taskId !== void 0 ? { taskId: opts.taskId } : {},
|
|
11374
11454
|
...opts.project !== void 0 ? { project: opts.project } : {},
|
|
11375
11455
|
writeResult: defaultWriteResult
|
|
11376
|
-
});
|
|
11377
|
-
await squadrantdCall(req);
|
|
11456
|
+
}, { call: squadrantdCall });
|
|
11378
11457
|
process.exit(0);
|
|
11379
11458
|
} catch (e) {
|
|
11380
11459
|
process.stderr.write(`${e.message}
|
|
@@ -13767,7 +13846,9 @@ runtimeCommand.command("status").description("Print 'running' or 'stopped' for a
|
|
|
13767
13846
|
process.exit(2);
|
|
13768
13847
|
}
|
|
13769
13848
|
});
|
|
13770
|
-
|
|
13849
|
+
var SEND_CONFIRM_TIMEOUT_MS = 15e3;
|
|
13850
|
+
var SEND_CONFIRM_POLL_MS = 500;
|
|
13851
|
+
async function runRuntimeSend(arg1, arg2, opts, confirmOpts) {
|
|
13771
13852
|
const config = loadConfig();
|
|
13772
13853
|
const registry = buildRegistry();
|
|
13773
13854
|
if (opts.command && arg2 !== void 0) {
|
|
@@ -13777,7 +13858,7 @@ async function runRuntimeSend(arg1, arg2, opts) {
|
|
|
13777
13858
|
const message = opts.command ? arg1 : arg2;
|
|
13778
13859
|
if (!message) throw new Error("Message is required");
|
|
13779
13860
|
const { requireDaemon: requireDaemon2 } = await Promise.resolve().then(() => (init_require_daemon(), require_daemon_exports));
|
|
13780
|
-
const { appendCaptainMessage: appendCaptainMessage2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
13861
|
+
const { appendCaptainMessage: appendCaptainMessage2, waitForCaptainDelivery: waitForCaptainDelivery2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports2));
|
|
13781
13862
|
await requireDaemon2();
|
|
13782
13863
|
const resolved = resolveTarget(registry, config, target, !!opts.command);
|
|
13783
13864
|
await needRef(resolved);
|
|
@@ -13785,16 +13866,30 @@ async function runRuntimeSend(arg1, arg2, opts) {
|
|
|
13785
13866
|
const { join: join30, dirname: dirname10 } = await import("path");
|
|
13786
13867
|
const { DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
13787
13868
|
const stateRoot = join30(dirname10(DEFAULT_CONFIG_PATH2), "state");
|
|
13788
|
-
await appendCaptainMessage2({
|
|
13869
|
+
const seq = await appendCaptainMessage2({
|
|
13789
13870
|
stateRoot,
|
|
13790
13871
|
project: finalProject,
|
|
13791
13872
|
text: message,
|
|
13792
13873
|
source: "cli"
|
|
13793
13874
|
});
|
|
13875
|
+
const timeoutMs = confirmOpts?.timeoutMs ?? SEND_CONFIRM_TIMEOUT_MS;
|
|
13876
|
+
const delivered = await waitForCaptainDelivery2({
|
|
13877
|
+
stateRoot,
|
|
13878
|
+
project: finalProject,
|
|
13879
|
+
seq,
|
|
13880
|
+
timeoutMs,
|
|
13881
|
+
pollMs: confirmOpts?.pollMs ?? SEND_CONFIRM_POLL_MS
|
|
13882
|
+
});
|
|
13883
|
+
if (!delivered) {
|
|
13884
|
+
throw new Error(
|
|
13885
|
+
`Message queued for '${finalProject}' (seq=${seq}) but delivery was not confirmed within ${Math.round(timeoutMs / 1e3)}s. It may still be pending \u2014 check with 'squadrant runtime read-screen ${finalProject}${opts.command ? " --command" : ""}'.`
|
|
13886
|
+
);
|
|
13887
|
+
}
|
|
13794
13888
|
}
|
|
13795
13889
|
runtimeCommand.command("send").description("Send a message to a target workspace AND commit with Enter. With --command, the first positional is the message.").argument("<arg1>", "Project name, or the message when --command is used").argument("[arg2]", "Message (when target is a project). Omit when using --command.").option("--command", "Target the command workspace").action(async (arg1, arg2, opts) => {
|
|
13796
13890
|
try {
|
|
13797
13891
|
await runRuntimeSend(arg1, arg2, opts);
|
|
13892
|
+
console.log(chalk18.green("\u2714 Delivered (confirmed)"));
|
|
13798
13893
|
} catch (err) {
|
|
13799
13894
|
console.error(chalk18.red(err.message));
|
|
13800
13895
|
process.exit(1);
|
|
@@ -14590,22 +14685,38 @@ var EFFORT_MEANING = {
|
|
|
14590
14685
|
balance: "normal routing \u2014 use default crew routing rules unchanged",
|
|
14591
14686
|
low: "conserve tokens \u2014 bias crew spawns toward opencode/sonnet; reserve opus for work that genuinely needs it"
|
|
14592
14687
|
};
|
|
14593
|
-
function runEffortGet(configPath = DEFAULT_CONFIG_PATH, projectName) {
|
|
14688
|
+
function runEffortGet(configPath = DEFAULT_CONFIG_PATH, projectName, projectConfigRoot) {
|
|
14594
14689
|
const config = loadConfig(configPath);
|
|
14595
|
-
|
|
14690
|
+
if (projectName && !(projectName in config.projects)) {
|
|
14691
|
+
const known = Object.keys(config.projects).sort().join(", ") || "(no projects registered)";
|
|
14692
|
+
throw new Error(`Unknown project '${projectName}'. Known projects: ${known}`);
|
|
14693
|
+
}
|
|
14694
|
+
const effort = resolveEffort(config, projectName, projectConfigRoot);
|
|
14596
14695
|
const description = `${effort}: ${EFFORT_MEANING[effort]}`;
|
|
14597
14696
|
return { effort, description };
|
|
14598
14697
|
}
|
|
14599
|
-
function runEffortSet(value, configPath = DEFAULT_CONFIG_PATH) {
|
|
14698
|
+
function runEffortSet(value, configPath = DEFAULT_CONFIG_PATH, projectName, projectConfigRoot) {
|
|
14600
14699
|
if (!VALID_EFFORTS.includes(value)) {
|
|
14601
14700
|
throw new Error(
|
|
14602
14701
|
`Invalid effort '${value}'. Valid values: ${VALID_EFFORTS.join(" | ")}`
|
|
14603
14702
|
);
|
|
14604
14703
|
}
|
|
14704
|
+
if (projectName) {
|
|
14705
|
+
const config2 = loadConfig(configPath);
|
|
14706
|
+
if (!(projectName in config2.projects)) {
|
|
14707
|
+
const known = Object.keys(config2.projects).sort().join(", ") || "(no projects registered)";
|
|
14708
|
+
throw new Error(`Unknown project '${projectName}'. Known projects: ${known}`);
|
|
14709
|
+
}
|
|
14710
|
+
saveProjectOverride(projectName, { effort: value }, projectConfigRoot);
|
|
14711
|
+
return;
|
|
14712
|
+
}
|
|
14605
14713
|
const config = loadConfig(configPath);
|
|
14606
14714
|
config.defaults.effort = value;
|
|
14607
14715
|
saveConfig(config, configPath);
|
|
14608
14716
|
}
|
|
14717
|
+
function effortScopeLabel(projectName) {
|
|
14718
|
+
return projectName ? `project: ${projectName}` : "global";
|
|
14719
|
+
}
|
|
14609
14720
|
function canonical(p) {
|
|
14610
14721
|
try {
|
|
14611
14722
|
return fs28.realpathSync(p);
|
|
@@ -14613,11 +14724,16 @@ function canonical(p) {
|
|
|
14613
14724
|
return path29.resolve(p);
|
|
14614
14725
|
}
|
|
14615
14726
|
}
|
|
14616
|
-
async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(), append) {
|
|
14727
|
+
async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(), append, scopeProject, projectConfigRoot) {
|
|
14617
14728
|
const here = canonical(cwd);
|
|
14618
14729
|
const notice = `\u{1F39A}\uFE0F effort \u2192 ${effort}: ${EFFORT_MEANING[effort]}`;
|
|
14619
14730
|
for (const [projName, proj] of Object.entries(config.projects)) {
|
|
14620
14731
|
if (canonical(proj.path) === here) continue;
|
|
14732
|
+
if (scopeProject) {
|
|
14733
|
+
if (projName !== scopeProject) continue;
|
|
14734
|
+
} else if (loadProjectOverride(projName, projectConfigRoot).effort !== void 0) {
|
|
14735
|
+
continue;
|
|
14736
|
+
}
|
|
14621
14737
|
try {
|
|
14622
14738
|
const ref = await driver.status(proj.captainName);
|
|
14623
14739
|
if (ref) {
|
|
@@ -14627,22 +14743,28 @@ async function notifyCaptainsOfEffort(effort, config, driver, cwd = process.cwd(
|
|
|
14627
14743
|
}
|
|
14628
14744
|
}
|
|
14629
14745
|
}
|
|
14630
|
-
var effortCommand = new Command28("effort").description("Get or set the
|
|
14746
|
+
var effortCommand = new Command28("effort").description("Get or set the crew tokenomics dial (max | balance | low) \u2014 global by default, or per-project with --project").argument("[value]", "effort level to set: max | balance | low").option("--project <name>", "target a specific project (get: show its resolved effort; set: write a per-project override)").action(async (value, options) => {
|
|
14631
14747
|
if (value === void 0) {
|
|
14632
|
-
|
|
14748
|
+
let result;
|
|
14749
|
+
try {
|
|
14750
|
+
result = runEffortGet(void 0, options.project);
|
|
14751
|
+
} catch (err) {
|
|
14752
|
+
console.error(chalk28.red(err.message));
|
|
14753
|
+
process.exit(1);
|
|
14754
|
+
}
|
|
14633
14755
|
const label = options.project ? `${options.project} project` : "global";
|
|
14634
|
-
console.log(chalk28.bold(`Current effort (${label}):`), chalk28.cyan(
|
|
14635
|
-
console.log(chalk28.dim(EFFORT_MEANING[
|
|
14756
|
+
console.log(chalk28.bold(`Current effort (${label}):`), chalk28.cyan(result.effort));
|
|
14757
|
+
console.log(chalk28.dim(EFFORT_MEANING[result.effort]));
|
|
14636
14758
|
return;
|
|
14637
14759
|
}
|
|
14638
14760
|
try {
|
|
14639
|
-
runEffortSet(value);
|
|
14761
|
+
runEffortSet(value, void 0, options.project);
|
|
14640
14762
|
} catch (err) {
|
|
14641
14763
|
console.error(chalk28.red(err.message));
|
|
14642
14764
|
process.exit(1);
|
|
14643
14765
|
}
|
|
14644
14766
|
const effort = value;
|
|
14645
|
-
console.log(chalk28.green(`\u2714 effort \u2192 ${effort}`));
|
|
14767
|
+
console.log(chalk28.green(`\u2714 effort \u2192 ${effort} (${effortScopeLabel(options.project)})`));
|
|
14646
14768
|
console.log(chalk28.dim(EFFORT_MEANING[effort]));
|
|
14647
14769
|
try {
|
|
14648
14770
|
const { createCmuxDriver: createCmuxDriver2, RuntimeRegistry: RuntimeRegistry2 } = await Promise.resolve().then(() => (init_dist3(), dist_exports3));
|
|
@@ -14651,7 +14773,7 @@ var effortCommand = new Command28("effort").description("Get or set the global c
|
|
|
14651
14773
|
const driver = registry.global(config);
|
|
14652
14774
|
const stateRoot = path29.join(path29.dirname(DEFAULT_CONFIG_PATH), "state");
|
|
14653
14775
|
const append = (project, text) => appendCaptainMessage({ stateRoot, project, text, source: "daemon" });
|
|
14654
|
-
await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append);
|
|
14776
|
+
await notifyCaptainsOfEffort(effort, config, driver, process.cwd(), append, options.project);
|
|
14655
14777
|
} catch {
|
|
14656
14778
|
console.log(chalk28.dim("(no running captain detected \u2014 change applies on next launch)"));
|
|
14657
14779
|
}
|
|
@@ -15007,10 +15129,8 @@ function mapHookSub(sub, payload, taskId) {
|
|
|
15007
15129
|
return mapClaudeHookToEvent("Stop", payload, taskId);
|
|
15008
15130
|
case "notification":
|
|
15009
15131
|
return mapClaudeHookToEvent("Notification", payload, taskId);
|
|
15010
|
-
case "ask-question":
|
|
15011
|
-
|
|
15012
|
-
return { type: "task.input.requested", id: taskId, requestId: 0, question: q };
|
|
15013
|
-
}
|
|
15132
|
+
case "ask-question":
|
|
15133
|
+
return mapClaudeHookToEvent("PreToolUse", payload, taskId);
|
|
15014
15134
|
case "session-end":
|
|
15015
15135
|
return mapClaudeHookToEvent("SessionEnd", payload, taskId);
|
|
15016
15136
|
default:
|