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 CHANGED
@@ -1950,7 +1950,7 @@ async function appendToMailbox(opts) {
1950
1950
  }));
1951
1951
  }
1952
1952
  async function appendCaptainMessage(opts) {
1953
- await appendEntry(opts.stateRoot, opts.project, (seq) => ({
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
- if (r.role !== "captain")
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 === "captain" && e.lastState === "start" && !seen.has(e.project)) {
3432
- deps.registry.markEnded(e.project, now);
3433
- logEntry(deps.log, e.project, deps.registry.get(e.project));
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 tasks = await deps.listTasks(project);
5869
- const task = tasks.find((t) => t.name === name);
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
- process.stderr.write(`\u26A0\uFE0F Message not delivered to crew '${name}' \u2014 use 'squadrant crew send ${project} ${name}' to re-send.
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.reduce((a, b) => (b.createdAt ?? 0) > (a.createdAt ?? 0) ? b : a);
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
- if (!Array.isArray(next.hooks[ev]))
9324
- next.hooks[ev] = [];
9325
- const already = next.hooks[ev].some((m) => Array.isArray(m?.hooks) && m.hooks.some((h) => typeof h.command === "string" && h.command.includes(hookCmd)));
9326
- if (!already) {
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
- const req = buildSignalRequest(state, {
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
- async function runRuntimeSend(arg1, arg2, opts) {
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
- const effort = resolveEffort(config, projectName);
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 global crew tokenomics dial (max | balance | low)").argument("[value]", "effort level to set: max | balance | low").option("--project <name>", "show resolved effort for a specific project").action(async (value, options) => {
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
- const { effort: effort2, description } = runEffortGet(void 0, options.project);
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(effort2));
14635
- console.log(chalk28.dim(EFFORT_MEANING[effort2]));
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
- const q = typeof payload?.question === "string" ? payload.question : "awaiting input";
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: