nexrall-code 0.5.176 → 0.5.178

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.
Files changed (2) hide show
  1. package/dist/index.js +1083 -84
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14343,6 +14343,7 @@ var require_sharedTasks = __commonJS({
14343
14343
  exports2.createSharedTask = createSharedTask;
14344
14344
  exports2.claimTask = claimTask;
14345
14345
  exports2.updateSharedTaskStatus = updateSharedTaskStatus;
14346
+ exports2.deleteSharedTask = deleteSharedTask;
14346
14347
  var fs32 = __importStar(__require("fs"));
14347
14348
  var os20 = __importStar(__require("os"));
14348
14349
  var path32 = __importStar(__require("path"));
@@ -14476,6 +14477,14 @@ var require_sharedTasks = __commonJS({
14476
14477
  }, workDir);
14477
14478
  }
14478
14479
  exports2._sharedTasksInternal = { tasksRoot, scopeDir, taskPath, isClaimAbandoned, isUnblocked, CLAIM_STALE_MS, MIN_CLAIM_AGE_FOR_DEATH_CHECK_MS };
14480
+ function deleteSharedTask(workDir, taskId) {
14481
+ try {
14482
+ fs32.unlinkSync(taskPath(workDir, taskId));
14483
+ return true;
14484
+ } catch {
14485
+ return false;
14486
+ }
14487
+ }
14479
14488
  }
14480
14489
  });
14481
14490
 
@@ -106697,6 +106706,14 @@ var require_hooks = __commonJS({
106697
106706
  Object.defineProperty(exports2, "__esModule", { value: true });
106698
106707
  exports2.withAgentHooks = withAgentHooks;
106699
106708
  exports2.loadHooks = loadHooks2;
106709
+ exports2.setHookWakeListener = setHookWakeListener2;
106710
+ exports2.setHookStatusListener = setHookStatusListener2;
106711
+ exports2.drainHookDeliveries = drainHookDeliveries;
106712
+ exports2.takeHookWakes = takeHookWakes2;
106713
+ exports2.hasPendingHookWake = hasPendingHookWake2;
106714
+ exports2.formatHookDeliveries = formatHookDeliveries2;
106715
+ exports2.resetHookOnceState = resetHookOnceState2;
106716
+ exports2.setHookMcpCaller = setHookMcpCaller2;
106700
106717
  exports2.parsePromptHookReply = parsePromptHookReply;
106701
106718
  exports2.hookRunOptsFor = hookRunOptsFor;
106702
106719
  exports2.classifyStopFailure = classifyStopFailure;
@@ -106786,7 +106803,10 @@ var require_hooks = __commonJS({
106786
106803
  "ConfigChange",
106787
106804
  "WorktreeCreate",
106788
106805
  "WorktreeRemove",
106789
- "Setup"
106806
+ "Setup",
106807
+ "PostToolBatch",
106808
+ "TaskCreated",
106809
+ "TaskCompleted"
106790
106810
  ];
106791
106811
  function toHookEntries(list) {
106792
106812
  return list.map((it) => {
@@ -106795,11 +106815,117 @@ var require_hooks = __commonJS({
106795
106815
  return { matcher: e2.matcher, hooks: e2.hooks };
106796
106816
  const { matcher, ...rest } = e2;
106797
106817
  const hook = { ...rest };
106798
- if (!isKnownHandlerType(hook.type))
106799
- hook.type = "command";
106818
+ if (!isKnownHandlerType(hook.type)) {
106819
+ hook.type = !hook.command && !hook.url && !hook.prompt && hook.server && hook.tool ? "mcp_tool" : "command";
106820
+ }
106800
106821
  return { matcher, hooks: [hook] };
106801
106822
  });
106802
106823
  }
106824
+ var _hookDeliveries = [];
106825
+ var _hookWakeListener = null;
106826
+ var _hookStatusListener = null;
106827
+ function setHookWakeListener2(fn) {
106828
+ _hookWakeListener = fn;
106829
+ }
106830
+ function setHookStatusListener2(fn) {
106831
+ _hookStatusListener = fn;
106832
+ }
106833
+ function drainHookDeliveries() {
106834
+ return _hookDeliveries.splice(0);
106835
+ }
106836
+ function takeHookWakes2() {
106837
+ const out = _hookDeliveries.filter((d) => d.kind === "wake");
106838
+ for (let i2 = _hookDeliveries.length - 1; i2 >= 0; i2--)
106839
+ if (_hookDeliveries[i2].kind === "wake")
106840
+ _hookDeliveries.splice(i2, 1);
106841
+ return out;
106842
+ }
106843
+ function hasPendingHookWake2() {
106844
+ return _hookDeliveries.some((d) => d.kind === "wake");
106845
+ }
106846
+ function formatHookDeliveries2(list) {
106847
+ return list.map((d) => `<hook-context source="${d.source}">
106848
+ ${d.text}
106849
+ </hook-context>`).join("\n\n");
106850
+ }
106851
+ var _hookOnceDone = /* @__PURE__ */ new Set();
106852
+ function hookIdentity(event, hook, matcher, dir) {
106853
+ const body = hook.command ?? hook.url ?? hook.prompt ?? `${hook.server ?? ""}__${hook.tool ?? ""}`;
106854
+ return `${dir ?? ""}|${event}|${matcher ?? ""}|${body}`;
106855
+ }
106856
+ function resetHookOnceState2() {
106857
+ _hookOnceDone.clear();
106858
+ }
106859
+ var _activeStatuses = /* @__PURE__ */ new Map();
106860
+ var _statusSeq = 0;
106861
+ function hookStatusBegin(message) {
106862
+ const id = ++_statusSeq;
106863
+ _activeStatuses.set(id, message);
106864
+ _hookStatusListener?.(message);
106865
+ return id;
106866
+ }
106867
+ function hookStatusEnd(id) {
106868
+ if (!_activeStatuses.delete(id))
106869
+ return;
106870
+ if (!_activeStatuses.size) {
106871
+ _hookStatusListener?.(null);
106872
+ return;
106873
+ }
106874
+ _hookStatusListener?.([..._activeStatuses.values()].pop() ?? null);
106875
+ }
106876
+ var _hookMcpCaller = null;
106877
+ function setHookMcpCaller2(fn) {
106878
+ _hookMcpCaller = fn;
106879
+ }
106880
+ function isBackgroundHook(hook) {
106881
+ return hook.type === "command" && (hook.async === true || hook.asyncRewake === true);
106882
+ }
106883
+ function hookGate(hook, event, matcher, dir) {
106884
+ const key = hook.once ? hookIdentity(event, hook, matcher, dir) : void 0;
106885
+ if (key && _hookOnceDone.has(key))
106886
+ return { skip: true };
106887
+ if (isBackgroundHook(hook))
106888
+ return { background: true, key };
106889
+ return { key, statusId: hook.statusMessage ? hookStatusBegin(hook.statusMessage) : void 0 };
106890
+ }
106891
+ function startBackgroundHook(hook, payload, opts) {
106892
+ const statusId = hook.statusMessage ? hookStatusBegin(hook.statusMessage) : void 0;
106893
+ const done = (r2) => {
106894
+ if (statusId !== void 0)
106895
+ hookStatusEnd(statusId);
106896
+ if (r2.status === 0 && opts.key)
106897
+ _hookOnceDone.add(opts.key);
106898
+ const out = (r2.stdout ?? "").toString().trim();
106899
+ if (hook.asyncRewake && r2.status === 2) {
106900
+ const text = (r2.stderr ?? "").toString().trim() || out;
106901
+ _hookDeliveries.push({ kind: "wake", text: text || "Hook asked the session to wake (exit 2).", source: opts.eventLabel });
106902
+ try {
106903
+ _hookWakeListener?.();
106904
+ } catch {
106905
+ }
106906
+ return;
106907
+ }
106908
+ const context = directiveContext(out);
106909
+ if (context)
106910
+ _hookDeliveries.push({ kind: "context", text: context, source: opts.eventLabel });
106911
+ };
106912
+ void spawnHook(String(hook.command ?? ""), { cwd: opts.cwd, timeout: opts.timeoutMs, input: payload, env: opts.env }).then(done, () => done({ status: null, stdout: "", stderr: "" }));
106913
+ }
106914
+ function directiveContext(out) {
106915
+ if (!out.startsWith("{"))
106916
+ return "";
106917
+ try {
106918
+ const j = JSON.parse(out);
106919
+ const parts = [];
106920
+ if (typeof j.additionalContext === "string" && j.additionalContext)
106921
+ parts.push(j.additionalContext);
106922
+ if (typeof j.systemMessage === "string" && j.systemMessage)
106923
+ parts.push(j.systemMessage);
106924
+ return parts.join("\n");
106925
+ } catch {
106926
+ return "";
106927
+ }
106928
+ }
106803
106929
  function spawnHook(command, opts) {
106804
106930
  return new Promise((resolve12) => {
106805
106931
  let stdout = "";
@@ -106809,7 +106935,8 @@ var require_hooks = __commonJS({
106809
106935
  if (settled)
106810
106936
  return;
106811
106937
  settled = true;
106812
- clearTimeout(timer);
106938
+ if (timer)
106939
+ clearTimeout(timer);
106813
106940
  resolve12({ status, stdout, stderr });
106814
106941
  };
106815
106942
  let child;
@@ -106819,7 +106946,7 @@ var require_hooks = __commonJS({
106819
106946
  resolve12({ status: null, stdout: "", stderr: "" });
106820
106947
  return;
106821
106948
  }
106822
- const timer = setTimeout(() => {
106949
+ const timer = opts.timeout === null ? null : setTimeout(() => {
106823
106950
  try {
106824
106951
  child.kill("SIGTERM");
106825
106952
  } catch {
@@ -106866,9 +106993,85 @@ var require_hooks = __commonJS({
106866
106993
  return { status: 1, stdout: "", stderr: `hook request failed: ${err.message}` };
106867
106994
  }
106868
106995
  }
106996
+ async function mcpToolHook(hook, payload) {
106997
+ const caller = _hookMcpCaller;
106998
+ const server = String(hook.server ?? "");
106999
+ const tool = String(hook.tool ?? "");
107000
+ if (!caller) {
107001
+ return { status: 1, stdout: "", stderr: `mcp_tool hook "${server}:${tool}" needs a connected MCP client` };
107002
+ }
107003
+ try {
107004
+ const res = await caller(server, tool, mcpHookInput(hook.input, payload));
107005
+ if ("error" in res)
107006
+ return { status: 1, stdout: "", stderr: res.error };
107007
+ const text = (res.text ?? "").trim();
107008
+ if (!text)
107009
+ return { status: 0, stdout: "", stderr: "" };
107010
+ return { status: 0, stdout: text.startsWith("{") ? text : JSON.stringify({ additionalContext: text }), stderr: "" };
107011
+ } catch (err) {
107012
+ return { status: 1, stdout: "", stderr: `mcp_tool hook ${server}:${tool} failed: ${err.message}` };
107013
+ }
107014
+ }
107015
+ function mcpHookInput(input, payload) {
107016
+ if (!input)
107017
+ return {};
107018
+ let root = {};
107019
+ try {
107020
+ const j = JSON.parse(payload);
107021
+ if (j && typeof j === "object" && !Array.isArray(j))
107022
+ root = j;
107023
+ } catch {
107024
+ }
107025
+ const nav = (parts) => {
107026
+ let cur = root;
107027
+ for (const p of parts) {
107028
+ if (cur === null || typeof cur !== "object")
107029
+ return void 0;
107030
+ cur = cur[p];
107031
+ }
107032
+ return cur;
107033
+ };
107034
+ const lookup = (path33) => {
107035
+ const parts = path33.split(".").map((s2) => s2.trim()).filter(Boolean);
107036
+ if (!parts.length)
107037
+ return void 0;
107038
+ const candidates = [parts];
107039
+ if (parts[0] === "tool_input" || parts[0] === "input") {
107040
+ const rest = parts.slice(1);
107041
+ candidates.unshift(rest, ["input", ...rest], ["tool_input", ...rest]);
107042
+ }
107043
+ for (const c of candidates) {
107044
+ const v = nav(c);
107045
+ if (v !== void 0)
107046
+ return v;
107047
+ }
107048
+ return void 0;
107049
+ };
107050
+ const subst = (v) => {
107051
+ if (typeof v !== "string")
107052
+ return v;
107053
+ const whole = v.match(/^\$\{([^}]+)\}$/);
107054
+ if (whole) {
107055
+ const hit = lookup(whole[1]);
107056
+ return hit === void 0 ? "" : hit;
107057
+ }
107058
+ return v.replace(/\$\{([^}]+)\}/g, (_m, p) => {
107059
+ const hit = lookup(p);
107060
+ if (hit === void 0)
107061
+ return "";
107062
+ return typeof hit === "string" ? hit : JSON.stringify(hit);
107063
+ });
107064
+ };
107065
+ const out = {};
107066
+ for (const [k, val] of Object.entries(input))
107067
+ out[k] = subst(val);
107068
+ return out;
107069
+ }
106869
107070
  async function runHookHandler(hook, payload, opts) {
106870
107071
  if (hook.type === "http")
106871
107072
  return httpHook(hook, payload, opts.timeoutMs);
107073
+ if (hook.type === "mcp_tool")
107074
+ return mcpToolHook(hook, payload);
106872
107075
  if (hook.type === "prompt" || hook.type === "agent") {
106873
107076
  return promptHook(hook, payload, { ...opts, envCtx: opts.hookEnv });
106874
107077
  }
@@ -107094,22 +107297,36 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107094
107297
  input,
107095
107298
  ...result ? { result: { output: result.output, error: result.error } } : {}
107096
107299
  });
107300
+ const eventLabel = `${phase} (${toolName})`;
107301
+ const hookEnv = {
107302
+ ...process.env,
107303
+ NEXRALL_TOOL_NAME: toolName,
107304
+ NEXRALL_TOOL_INPUT: JSON.stringify(input),
107305
+ NEXRALL_HOOK_PHASE: phase
107306
+ };
107097
107307
  for (const entry of entries) {
107098
107308
  if (!hookMatches(entry.matcher, toolName))
107099
107309
  continue;
107100
107310
  for (const hook of entry.hooks ?? []) {
107101
107311
  if (!isRunnableHook(hook))
107102
107312
  continue;
107103
- const hookTimeout = hookTimeoutMs(hook, 6e4);
107313
+ const gate = hookGate(hook, phase, entry.matcher, workDir);
107314
+ if (gate.skip)
107315
+ continue;
107316
+ if (gate.background) {
107317
+ startBackgroundHook(hook, payload, {
107318
+ cwd: workDir,
107319
+ env: hookEnv,
107320
+ eventLabel,
107321
+ timeoutMs: hook.asyncRewake ? hookTimeoutMs(hook, 6e4) : null,
107322
+ key: gate.key
107323
+ });
107324
+ continue;
107325
+ }
107104
107326
  const r2 = await runHookHandler(hook, payload, {
107105
107327
  cwd: workDir,
107106
- timeoutMs: hookTimeout,
107107
- env: {
107108
- ...process.env,
107109
- NEXRALL_TOOL_NAME: toolName,
107110
- NEXRALL_TOOL_INPUT: JSON.stringify(input),
107111
- NEXRALL_HOOK_PHASE: phase
107112
- },
107328
+ timeoutMs: hookTimeoutMs(hook, 6e4),
107329
+ env: hookEnv,
107113
107330
  ...hookOpts
107114
107331
  });
107115
107332
  const out = (r2.stdout ?? "").toString().trim();
@@ -107131,17 +107348,33 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107131
107348
  const err = (r2.stderr ?? "").toString().trim();
107132
107349
  outcome.reason = err || outcome.reason || `Blocked by ${phase} hook`;
107133
107350
  }
107351
+ if (gate.statusId !== void 0)
107352
+ hookStatusEnd(gate.statusId);
107353
+ if (r2.status === 0 && gate.key)
107354
+ _hookOnceDone.add(gate.key);
107134
107355
  }
107135
107356
  }
107136
107357
  return outcome;
107137
107358
  }
107138
- async function runSimpleHooks(defs, workDir, extraEnv, hookOpts) {
107359
+ async function runSimpleHooks(defs, workDir, extraEnv, hookOpts, event = "hook") {
107139
107360
  if (!defs?.length)
107140
107361
  return;
107362
+ const env4 = extraEnv ? { ...process.env, ...extraEnv } : void 0;
107141
107363
  for (const hook of defs) {
107142
107364
  if (!isRunnableHook(hook))
107143
107365
  continue;
107144
- await runHookHandler(hook, "", { cwd: workDir, timeoutMs: hookTimeoutMs(hook, 1e4), env: extraEnv ? { ...process.env, ...extraEnv } : void 0, ...hookOpts });
107366
+ const gate = hookGate(hook, event, void 0, workDir);
107367
+ if (gate.skip)
107368
+ continue;
107369
+ if (gate.background) {
107370
+ startBackgroundHook(hook, "", { cwd: workDir, env: env4, eventLabel: event, timeoutMs: hook.asyncRewake ? hookTimeoutMs(hook, 1e4) : null, key: gate.key });
107371
+ continue;
107372
+ }
107373
+ const r2 = await runHookHandler(hook, "", { cwd: workDir, timeoutMs: hookTimeoutMs(hook, 1e4), env: env4, ...hookOpts });
107374
+ if (gate.statusId !== void 0)
107375
+ hookStatusEnd(gate.statusId);
107376
+ if (r2.status === 0 && gate.key)
107377
+ _hookOnceDone.add(gate.key);
107145
107378
  }
107146
107379
  }
107147
107380
  function flattenHookDefs(items, matchValue) {
@@ -107158,7 +107391,7 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107158
107391
  return out;
107159
107392
  }
107160
107393
  function isKnownHandlerType(t2) {
107161
- return t2 === "command" || t2 === "http" || t2 === "prompt" || t2 === "agent";
107394
+ return t2 === "command" || t2 === "http" || t2 === "prompt" || t2 === "agent" || t2 === "mcp_tool";
107162
107395
  }
107163
107396
  function isRunnableHook(hook) {
107164
107397
  if (!isKnownHandlerType(hook.type))
@@ -107167,6 +107400,8 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107167
107400
  return !!hook.command;
107168
107401
  if (hook.type === "http")
107169
107402
  return !!hook.url;
107403
+ if (hook.type === "mcp_tool")
107404
+ return !!hook.server && !!hook.tool;
107170
107405
  return !!hook.prompt;
107171
107406
  }
107172
107407
  async function runLifecycleHooks(items, event, workDir, payload, matchValue, hookOpts) {
@@ -107178,6 +107413,19 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107178
107413
  for (const hook of defs) {
107179
107414
  if (!isRunnableHook(hook))
107180
107415
  continue;
107416
+ const gate = hookGate(hook, event, matchValue, workDir);
107417
+ if (gate.skip)
107418
+ continue;
107419
+ if (gate.background) {
107420
+ startBackgroundHook(hook, input, {
107421
+ cwd: workDir,
107422
+ env: { ...process.env, NEXRALL_HOOK_PHASE: event },
107423
+ eventLabel: event,
107424
+ timeoutMs: hook.asyncRewake ? hookTimeoutMs(hook, 6e4) : null,
107425
+ key: gate.key
107426
+ });
107427
+ continue;
107428
+ }
107181
107429
  const timeout = hookTimeoutMs(hook, hook.type === "agent" ? 6e4 : hook.type === "prompt" ? 3e4 : 3e4);
107182
107430
  const r2 = await runHookHandler(hook, input, { cwd: workDir, timeoutMs: timeout, env: { ...process.env, NEXRALL_HOOK_PHASE: event }, ...hookOpts });
107183
107431
  const out = (r2.stdout ?? "").toString().trim();
@@ -107212,6 +107460,10 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107212
107460
  outcome.block = true;
107213
107461
  outcome.reason = (r2.stderr ?? "").toString().trim() || outcome.reason || `Blocked by ${event} hook`;
107214
107462
  }
107463
+ if (gate.statusId !== void 0)
107464
+ hookStatusEnd(gate.statusId);
107465
+ if (r2.status === 0 && gate.key)
107466
+ _hookOnceDone.add(gate.key);
107215
107467
  }
107216
107468
  return outcome;
107217
107469
  }
@@ -107923,6 +108175,11 @@ var require_iterationPolicy = __commonJS({
107923
108175
  \u{1F6D1} Stopped: the same tool error repeated ${exports2.REPEAT_STALL_LIMIT} rounds in a row, so the agent was looping without making progress.` + (ctx.repeatError ? ` The recurring error was:
107924
108176
  ${ctx.repeatError}
107925
108177
  ` : "\n") + `Fix that underlying cause (or grant the needed permission) and send "continue".
108178
+ `;
108179
+ case "hook-blocked":
108180
+ return `
108181
+ \u26D4 A PostToolBatch hook stopped the turn before the next model call` + (ctx.hookReason ? `: ${ctx.hookReason}` : ".") + `
108182
+ The reason is in the conversation, so send "continue" once the cause is handled.
107926
108183
  `;
107927
108184
  case "budget":
107928
108185
  return `
@@ -108523,7 +108780,10 @@ ${body}`);
108523
108780
  "no-balance",
108524
108781
  "no-team-budget",
108525
108782
  "team-unavailable",
108526
- "reported-elsewhere"
108783
+ "reported-elsewhere",
108784
+ // A hook stopping the batch means the sub-agent stopped short of its report, so the
108785
+ // parent must not read the result as a finished one.
108786
+ "hook-blocked"
108527
108787
  ]);
108528
108788
  exports2.SUBAGENT_PREAMBLE = [
108529
108789
  "# You are a sub-agent",
@@ -109232,8 +109492,8 @@ var require_loop = __commonJS({
109232
109492
  "../core/dist/agent/loop.js"(exports2) {
109233
109493
  "use strict";
109234
109494
  Object.defineProperty(exports2, "__esModule", { value: true });
109235
- exports2.resolveSubtaskTimeoutMs = exports2.canSpawnSubAgents = exports2.intersectAllowlists = exports2.noSpawnReason = exports2.resolveMaxSubagentDepth = exports2._sessionSubAgentCount = exports2.resetSessionSubAgentBudget = exports2._resetSubTaskLimiter = exports2.createLimiter = exports2.resolveMaxSubagentsPerSession = exports2.resolveMaxConcurrentSubtasks = exports2.lockPathsFor = exports2.resolveMaxIterations = exports2.stopReasonNotice = exports2.executeAgentMemoryWrite = exports2.AGENT_MEMORY_TOOL_SCHEMA = exports2.AGENT_MEMORY_TOOL = exports2._stallLimits = exports2.errorRoundSignature = exports2._emptyTurnRetry = exports2.shouldRetryEmptyTurn = exports2.emptyTurnBackoffMs = exports2.compactMessagesForResume = exports2.estimateTokensRough = exports2.makeCachedSummarizer = exports2.pruneOldToolResults = exports2.ledgerSummary = exports2.ledgerRecord = exports2.createLedger = exports2.transcriptOf = exports2.persistRuntimeContext = exports2.findSafeCutIndex = exports2.VERIFY_CMD_RE = exports2.allowsTestOnlyWrite = exports2.WRITE_TOOL_NAMES = exports2.estimateBodyBytes = exports2.pruneReclaimFloor = exports2.compactionThresholds = exports2.compactionLimits = exports2.contextWindowFor = exports2.fireObserverHook = exports2.fireManualPreCompactHook = exports2.fireManualPostCompactHook = exports2.fireNotificationHooks = exports2.runPermissionRequestHooks = exports2.runLifecycleHooks = exports2.classifyStopFailure = exports2.parsePromptHookReply = exports2.loadHooks = exports2.bashNeedsRepoLock = void 0;
109236
- exports2.lastToolResults = exports2.summariseSubTaskProgress = exports2.capSubTaskText = exports2.extractSubTaskText = exports2.ToolNotAllowedError = void 0;
109495
+ exports2.createLimiter = exports2.resolveMaxSubagentsPerSession = exports2.resolveMaxConcurrentSubtasks = exports2.lockPathsFor = exports2.resolveMaxIterations = exports2.stopReasonNotice = exports2.executeAgentMemoryWrite = exports2.AGENT_MEMORY_TOOL_SCHEMA = exports2.AGENT_MEMORY_TOOL = exports2._stallLimits = exports2.errorRoundSignature = exports2._emptyTurnRetry = exports2.shouldRetryEmptyTurn = exports2.emptyTurnBackoffMs = exports2.compactMessagesForResume = exports2.estimateTokensRough = exports2.makeCachedSummarizer = exports2.pruneOldToolResults = exports2.ledgerSummary = exports2.ledgerRecord = exports2.createLedger = exports2.transcriptOf = exports2.persistRuntimeContext = exports2.findSafeCutIndex = exports2.VERIFY_CMD_RE = exports2.allowsTestOnlyWrite = exports2.WRITE_TOOL_NAMES = exports2.estimateBodyBytes = exports2.pruneReclaimFloor = exports2.compactionThresholds = exports2.compactionLimits = exports2.contextWindowFor = exports2.resetHookOnceState = exports2.setHookMcpCaller = exports2.setHookStatusListener = exports2.setHookWakeListener = exports2.formatHookDeliveries = exports2.hasPendingHookWake = exports2.takeHookWakes = exports2.drainHookDeliveries = exports2.fireObserverHook = exports2.fireManualPreCompactHook = exports2.fireManualPostCompactHook = exports2.fireNotificationHooks = exports2.runPermissionRequestHooks = exports2.runLifecycleHooks = exports2.classifyStopFailure = exports2.parsePromptHookReply = exports2.loadHooks = exports2.bashNeedsRepoLock = void 0;
109496
+ exports2.lastToolResults = exports2.summariseSubTaskProgress = exports2.capSubTaskText = exports2.extractSubTaskText = exports2.ToolNotAllowedError = exports2.resolveSubtaskTimeoutMs = exports2.canSpawnSubAgents = exports2.intersectAllowlists = exports2.noSpawnReason = exports2.resolveMaxSubagentDepth = exports2._sessionSubAgentCount = exports2.resetSessionSubAgentBudget = exports2._resetSubTaskLimiter = void 0;
109237
109497
  exports2.dispatchSubAgent = dispatchSubAgent;
109238
109498
  exports2.dispatchBackgroundSubAgent = dispatchBackgroundSubAgent;
109239
109499
  exports2.runAgentLoop = runAgentLoop2;
@@ -109244,6 +109504,7 @@ var require_loop = __commonJS({
109244
109504
  var types_1 = require_types();
109245
109505
  var client_1 = require_client();
109246
109506
  var executor_1 = require_executor();
109507
+ var sharedTasks_1 = require_sharedTasks();
109247
109508
  var agentTypes_1 = require_agentTypes();
109248
109509
  var skills_1 = require_skills();
109249
109510
  var rules_1 = require_rules();
@@ -109291,6 +109552,30 @@ var require_loop = __commonJS({
109291
109552
  Object.defineProperty(exports2, "fireObserverHook", { enumerable: true, get: function() {
109292
109553
  return hooks_2.fireObserverHook;
109293
109554
  } });
109555
+ Object.defineProperty(exports2, "drainHookDeliveries", { enumerable: true, get: function() {
109556
+ return hooks_2.drainHookDeliveries;
109557
+ } });
109558
+ Object.defineProperty(exports2, "takeHookWakes", { enumerable: true, get: function() {
109559
+ return hooks_2.takeHookWakes;
109560
+ } });
109561
+ Object.defineProperty(exports2, "hasPendingHookWake", { enumerable: true, get: function() {
109562
+ return hooks_2.hasPendingHookWake;
109563
+ } });
109564
+ Object.defineProperty(exports2, "formatHookDeliveries", { enumerable: true, get: function() {
109565
+ return hooks_2.formatHookDeliveries;
109566
+ } });
109567
+ Object.defineProperty(exports2, "setHookWakeListener", { enumerable: true, get: function() {
109568
+ return hooks_2.setHookWakeListener;
109569
+ } });
109570
+ Object.defineProperty(exports2, "setHookStatusListener", { enumerable: true, get: function() {
109571
+ return hooks_2.setHookStatusListener;
109572
+ } });
109573
+ Object.defineProperty(exports2, "setHookMcpCaller", { enumerable: true, get: function() {
109574
+ return hooks_2.setHookMcpCaller;
109575
+ } });
109576
+ Object.defineProperty(exports2, "resetHookOnceState", { enumerable: true, get: function() {
109577
+ return hooks_2.resetHookOnceState;
109578
+ } });
109294
109579
  var compaction_1 = require_compaction();
109295
109580
  var compaction_2 = require_compaction();
109296
109581
  Object.defineProperty(exports2, "contextWindowFor", { enumerable: true, get: function() {
@@ -109465,6 +109750,27 @@ var require_loop = __commonJS({
109465
109750
  subAgentBudget_1._inFlightByDepth.delete(childDepth);
109466
109751
  }
109467
109752
  }
109753
+ async function taskHookVeto(entries, event, input, options, extra = {}) {
109754
+ if (!entries?.length)
109755
+ return null;
109756
+ const taskId = typeof input.task_id === "string" ? input.task_id : "";
109757
+ const task = extra.task_subject ? null : (0, sharedTasks_1.listSharedTasks)(options.workDir).find((t2) => t2.id === taskId);
109758
+ const payload = {
109759
+ session_id: options.sessionId ?? "",
109760
+ ...taskId ? { task_id: taskId } : {},
109761
+ ...task ? { task_subject: task.content, task_description: task.content } : {},
109762
+ ...extra,
109763
+ tool_input: input
109764
+ };
109765
+ try {
109766
+ const outcome = await (0, hooks_1.runLifecycleHooks)(entries, event, options.workDir, payload, void 0, (0, hooks_1.hookRunOptsFor)(options));
109767
+ if (!outcome.block)
109768
+ return null;
109769
+ return `Refused by ${event} hook${outcome.reason ? `: ${outcome.reason}` : ""}`;
109770
+ } catch {
109771
+ return null;
109772
+ }
109773
+ }
109468
109774
  function dispatchBackgroundSubAgent(input, options, agentTypes, hub) {
109469
109775
  const overBudget = (0, subAgentBudget_1.claimSessionSubAgentSlot)(options.workDir, options.onNotice ?? options.onText, options.sessionId);
109470
109776
  if (overBudget)
@@ -109668,6 +109974,7 @@ ${ctx}
109668
109974
  let repeatedErrorRounds = 0;
109669
109975
  let lastErrorSignature = "";
109670
109976
  let stalledRepeatError = null;
109977
+ let hookBlockedReason = null;
109671
109978
  let budget = maxIterations;
109672
109979
  let iteration = 0;
109673
109980
  let emptyTurnRetries = 0;
@@ -109851,7 +110158,8 @@ ${ctx}
109851
110158
  const queued = options.takePendingInput?.() ?? [];
109852
110159
  const peerMsgs = options.drainPeerMessages?.() ?? [];
109853
110160
  const bgDone = drainBackgroundNotifications();
109854
- if (queued.length || peerMsgs.length || bgDone.length) {
110161
+ const hookMsgs = (0, hooks_1.drainHookDeliveries)();
110162
+ if (queued.length || peerMsgs.length || bgDone.length || hookMsgs.length) {
109855
110163
  const parts = [];
109856
110164
  if (queued.length) {
109857
110165
  parts.push(queued.join("\n\n"));
@@ -109863,6 +110171,8 @@ ${ctx}
109863
110171
  }
109864
110172
  if (bgDone.length)
109865
110173
  parts.push((0, backgroundAgents_1.formatBackgroundNotifications)(bgDone));
110174
+ if (hookMsgs.length)
110175
+ parts.push((0, hooks_1.formatHookDeliveries)(hookMsgs));
109866
110176
  messages.push({ role: "user", content: [{ type: "text", text: parts.join("\n\n") }] });
109867
110177
  continue;
109868
110178
  }
@@ -109895,7 +110205,8 @@ ${ctx}
109895
110205
  const queued = options.takePendingInput?.() ?? [];
109896
110206
  const peerMsgs = options.drainPeerMessages?.() ?? [];
109897
110207
  const bgDone = drainBackgroundNotifications();
109898
- if (queued.length || peerMsgs.length || bgDone.length) {
110208
+ const hookMsgs = (0, hooks_1.drainHookDeliveries)();
110209
+ if (queued.length || peerMsgs.length || bgDone.length || hookMsgs.length) {
109899
110210
  const parts = [];
109900
110211
  if (queued.length) {
109901
110212
  parts.push(queued.join("\n\n"));
@@ -109907,6 +110218,8 @@ ${ctx}
109907
110218
  }
109908
110219
  if (bgDone.length)
109909
110220
  parts.push((0, backgroundAgents_1.formatBackgroundNotifications)(bgDone));
110221
+ if (hookMsgs.length)
110222
+ parts.push((0, hooks_1.formatHookDeliveries)(hookMsgs));
109910
110223
  messages.push({ role: "user", content: [{ type: "text", text: parts.join("\n\n") }] });
109911
110224
  continue;
109912
110225
  }
@@ -110065,7 +110378,16 @@ ${ctx}
110065
110378
  if (pre.block) {
110066
110379
  result = { error: `Blocked by PreToolUse hook: ${pre.reason}` };
110067
110380
  } else {
110381
+ const taskIdsBefore = name === "create_shared_task" ? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t2) => t2.id)) : null;
110068
110382
  try {
110383
+ if (name === "update_shared_task_status" && String(input.status ?? "") === "completed") {
110384
+ const veto = await taskHookVeto(hooks.TaskCompleted, "TaskCompleted", input, options);
110385
+ if (veto) {
110386
+ result = { error: veto };
110387
+ options.onToolResult(name, result, void 0, toolMeta);
110388
+ return { block: { ...block, id }, result };
110389
+ }
110390
+ }
110069
110391
  if (name === iterationPolicy_1.AGENT_MEMORY_TOOL) {
110070
110392
  result = await (0, iterationPolicy_1.executeAgentMemoryWrite)(input, options._agentMemory, options.workDir);
110071
110393
  options.onToolResult(name, result, void 0, toolMeta);
@@ -110103,6 +110425,21 @@ ${ctx}
110103
110425
  } catch (err) {
110104
110426
  result = { error: `Tool execution failed: ${err.message}` };
110105
110427
  }
110428
+ if (name === "create_shared_task" && result.error === void 0 && taskIdsBefore) {
110429
+ const created = (0, sharedTasks_1.listSharedTasks)(options.workDir).filter((t2) => !taskIdsBefore.has(t2.id) && typeof input.content === "string" && t2.content === input.content);
110430
+ const task = created[created.length - 1];
110431
+ if (task) {
110432
+ const veto = await taskHookVeto(hooks.TaskCreated, "TaskCreated", input, options, {
110433
+ task_id: task.id,
110434
+ task_subject: task.content,
110435
+ task_description: task.content
110436
+ });
110437
+ if (veto) {
110438
+ (0, sharedTasks_1.deleteSharedTask)(options.workDir, task.id);
110439
+ result = { error: veto };
110440
+ }
110441
+ }
110442
+ }
110106
110443
  if (name === "memory_write" && result.error === void 0) {
110107
110444
  const scope = input.scope === "global" ? "global" : "project";
110108
110445
  void (0, compaction_1.maybeCompactMemory)(scope, options);
@@ -110194,6 +110531,32 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
110194
110531
  if (bgFinished.length) {
110195
110532
  toolResultContent.push({ type: "text", text: (0, backgroundAgents_1.formatBackgroundNotifications)(bgFinished) });
110196
110533
  }
110534
+ const hookDeliveries = (0, hooks_1.drainHookDeliveries)();
110535
+ if (hookDeliveries.length) {
110536
+ toolResultContent.push({ type: "text", text: (0, hooks_1.formatHookDeliveries)(hookDeliveries) });
110537
+ }
110538
+ let postBatchStop = null;
110539
+ if (hooks.PostToolBatch?.length) {
110540
+ const batch = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolBatch, "PostToolBatch", options.workDir, {
110541
+ tool_calls: toolResults.map(({ block: b, result: r2 }) => ({
110542
+ tool_name: b.name,
110543
+ tool_input: b.input,
110544
+ tool_use_id: b.id,
110545
+ tool_response: r2.error !== void 0 ? r2.output ? `Error: ${r2.error}
110546
+
110547
+ ${r2.output}` : `Error: ${r2.error}` : r2.output ?? ""
110548
+ }))
110549
+ }, void 0, (0, hooks_1.hookRunOptsFor)(options));
110550
+ if (batch.context)
110551
+ toolResultContent.push({ type: "text", text: batch.context });
110552
+ if (batch.block) {
110553
+ postBatchStop = batch.reason?.trim() || "a PostToolBatch hook stopped the turn";
110554
+ toolResultContent.push({
110555
+ type: "text",
110556
+ text: `A PostToolBatch hook stopped the turn before the next model call: ${postBatchStop}`
110557
+ });
110558
+ }
110559
+ }
110197
110560
  const toolResultMessage = {
110198
110561
  role: "user",
110199
110562
  content: toolResultContent
@@ -110201,6 +110564,11 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
110201
110564
  messages.push(toolResultMessage);
110202
110565
  completedRounds++;
110203
110566
  options.onProgress?.(messages);
110567
+ if (postBatchStop) {
110568
+ stopReason = "hook-blocked";
110569
+ hookBlockedReason = postBatchStop;
110570
+ break;
110571
+ }
110204
110572
  const errored = toolResults.filter(({ result }) => result.error !== void 0);
110205
110573
  const allErrored = toolResults.length > 0 && errored.length === toolResults.length;
110206
110574
  consecutiveErrorRounds = allErrored ? consecutiveErrorRounds + 1 : 0;
@@ -110234,7 +110602,7 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
110234
110602
  const apiFailure = (0, hooks_1.apiFailureForStopReason)(stopReason);
110235
110603
  if (apiFailure)
110236
110604
  await fireStopFailure(apiFailure);
110237
- const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, { budget, repeatError: stalledRepeatError });
110605
+ const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, { budget, repeatError: stalledRepeatError, hookReason: hookBlockedReason });
110238
110606
  if (options._onStopReason)
110239
110607
  options._onStopReason(stopReason, notice || null);
110240
110608
  else if (notice)
@@ -110527,6 +110895,7 @@ var require_client2 = __commonJS({
110527
110895
  Object.defineProperty(exports2, "__esModule", { value: true });
110528
110896
  exports2.McpClient = void 0;
110529
110897
  exports2.flattenResourceContents = flattenResourceContents;
110898
+ exports2.flattenPromptMessages = flattenPromptMessages;
110530
110899
  var child_process_1 = __require("child_process");
110531
110900
  function flattenResourceContents(result, maxChars = 1e5) {
110532
110901
  const contents = result?.contents ?? [];
@@ -110539,6 +110908,31 @@ var require_client2 = __commonJS({
110539
110908
  }).filter(Boolean);
110540
110909
  const text = parts.join("\n");
110541
110910
  return text.length > maxChars ? text.slice(0, maxChars) + `
110911
+ \u2026 [truncated, ${text.length - maxChars} more characters]` : text;
110912
+ }
110913
+ function flattenPromptMessages(result, maxChars = 1e5) {
110914
+ const messages = result?.messages ?? [];
110915
+ const parts = messages.map((m2) => {
110916
+ const content = m2?.content;
110917
+ const items = Array.isArray(content) ? content : content ? [content] : [];
110918
+ return items.map((c) => {
110919
+ const item = c;
110920
+ if (item?.type === "text" && typeof item.text === "string")
110921
+ return item.text;
110922
+ if (item?.type === "image")
110923
+ return `[image prompt content (${item.mimeType ?? "unknown type"}) not shown]`;
110924
+ if (item?.type === "audio")
110925
+ return `[audio prompt content (${item.mimeType ?? "unknown type"}) not shown]`;
110926
+ if (item?.type === "resource") {
110927
+ if (typeof item.resource?.text === "string")
110928
+ return item.resource.text;
110929
+ return `[resource prompt content (${item.resource?.uri ?? "unknown uri"}) not shown]`;
110930
+ }
110931
+ return "";
110932
+ }).filter(Boolean).join("\n");
110933
+ }).filter(Boolean);
110934
+ const text = parts.join("\n\n");
110935
+ return text.length > maxChars ? text.slice(0, maxChars) + `
110542
110936
  \u2026 [truncated, ${text.length - maxChars} more characters]` : text;
110543
110937
  }
110544
110938
  var McpClient = class {
@@ -110599,6 +110993,20 @@ var require_client2 = __commonJS({
110599
110993
  async readResource(uri) {
110600
110994
  return flattenResourceContents(await this._request("resources/read", { uri }));
110601
110995
  }
110996
+ async listPrompts() {
110997
+ try {
110998
+ const res = await this._request("prompts/list", {});
110999
+ return Array.isArray(res?.prompts) ? res.prompts : [];
111000
+ } catch {
111001
+ return [];
111002
+ }
111003
+ }
111004
+ async getPrompt(name, args) {
111005
+ const params = { name };
111006
+ if (Object.keys(args).length)
111007
+ params.arguments = args;
111008
+ return flattenPromptMessages(await this._request("prompts/get", params));
111009
+ }
110602
111010
  async callTool(name, args) {
110603
111011
  const result = await this._request("tools/call", { name, arguments: args });
110604
111012
  if (!result)
@@ -110726,6 +111134,20 @@ var require_httpClient = __commonJS({
110726
111134
  async readResource(uri) {
110727
111135
  return (0, client_1.flattenResourceContents)(await this._rpc("resources/read", { uri }));
110728
111136
  }
111137
+ async listPrompts() {
111138
+ try {
111139
+ const res = await this._rpc("prompts/list", {});
111140
+ return Array.isArray(res?.prompts) ? res.prompts : [];
111141
+ } catch {
111142
+ return [];
111143
+ }
111144
+ }
111145
+ async getPrompt(name, args) {
111146
+ const params = { name };
111147
+ if (Object.keys(args).length)
111148
+ params.arguments = args;
111149
+ return (0, client_1.flattenPromptMessages)(await this._rpc("prompts/get", params));
111150
+ }
110729
111151
  async callTool(name, args) {
110730
111152
  const result = await this._rpc("tools/call", { name, arguments: args });
110731
111153
  if (!result)
@@ -110872,6 +111294,20 @@ var require_sseClient = __commonJS({
110872
111294
  async readResource(uri) {
110873
111295
  return (0, client_1.flattenResourceContents)(await this._rpc("resources/read", { uri }));
110874
111296
  }
111297
+ async listPrompts() {
111298
+ try {
111299
+ const res = await this._rpc("prompts/list", {});
111300
+ return Array.isArray(res?.prompts) ? res.prompts : [];
111301
+ } catch {
111302
+ return [];
111303
+ }
111304
+ }
111305
+ async getPrompt(name, args) {
111306
+ const params = { name };
111307
+ if (Object.keys(args).length)
111308
+ params.arguments = args;
111309
+ return (0, client_1.flattenPromptMessages)(await this._rpc("prompts/get", params));
111310
+ }
110875
111311
  async callTool(name, args) {
110876
111312
  const result = await this._rpc("tools/call", { name, arguments: args });
110877
111313
  if (!result)
@@ -111216,6 +111652,7 @@ var require_manager = __commonJS({
111216
111652
  constructor() {
111217
111653
  this._clients = /* @__PURE__ */ new Map();
111218
111654
  this._toolMap = /* @__PURE__ */ new Map();
111655
+ this._promptCache = /* @__PURE__ */ new Map();
111219
111656
  this._configs = /* @__PURE__ */ new Map();
111220
111657
  this._errors = /* @__PURE__ */ new Map();
111221
111658
  this._needsAuth = /* @__PURE__ */ new Map();
@@ -111383,6 +111820,75 @@ var require_manager = __commonJS({
111383
111820
  clearTimeout(timer);
111384
111821
  }
111385
111822
  }
111823
+ /**
111824
+ * Prompt metadata for one connected server. Fetched once per connection and
111825
+ * cached — including an empty list (server has no prompts capability), so the
111826
+ * `/` menu does not re-ask a server on every keystroke; reconnecting clears
111827
+ * the entry. Returns [] for a server that is not connected.
111828
+ */
111829
+ async promptsFor(server) {
111830
+ const cached = this._promptCache.get(server);
111831
+ if (cached)
111832
+ return cached;
111833
+ const client = this._clients.get(server);
111834
+ if (!client)
111835
+ return [];
111836
+ const list = await client.listPrompts();
111837
+ this._promptCache.set(server, list);
111838
+ return list;
111839
+ }
111840
+ /** Prompts across connected, enabled servers, tagged with the server they came from. */
111841
+ async listPrompts(server) {
111842
+ const out = [];
111843
+ for (const name of this._clients.keys()) {
111844
+ if (server && name !== server)
111845
+ continue;
111846
+ for (const p of await this.promptsFor(name))
111847
+ out.push({ ...p, server: name });
111848
+ }
111849
+ return out;
111850
+ }
111851
+ /**
111852
+ * Expand one prompt to text. Positional values map, in order, onto the
111853
+ * arguments the server declared in prompts/list — the same positional form
111854
+ * Claude Code accepts (`/mcp__server__prompt first second`). Validation runs
111855
+ * here, before the request, so mistakes read as sentences instead of raw
111856
+ * JSON-RPC errors; the request itself is bounded by the same 60s timeout the
111857
+ * resource reader uses.
111858
+ */
111859
+ async getPrompt(server, name, values = []) {
111860
+ const client = this._clients.get(server);
111861
+ if (!client)
111862
+ throw new Error(`No connected MCP server named "${server}". Connected: ${[...this._clients.keys()].join(", ") || "(none)"}`);
111863
+ const list = await this.promptsFor(server);
111864
+ const meta = list.find((p) => p.name === name);
111865
+ if (!meta) {
111866
+ throw new Error(`No MCP prompt named "${name}" on server "${server}"${list.length ? ` (it offers: ${list.map((p) => p.name).join(", ")})` : ""}.`);
111867
+ }
111868
+ const declared = meta.arguments ?? [];
111869
+ if (values.length > declared.length) {
111870
+ throw new Error(`MCP prompt "${server}:${name}" takes ${declared.length} argument(s)${declared.length ? ` (${declared.map((a) => a.name).join(", ")})` : ""} \u2014 got ${values.length}.`);
111871
+ }
111872
+ const args = {};
111873
+ declared.forEach((a, i2) => {
111874
+ const v = values[i2];
111875
+ if (v !== void 0 && v !== "")
111876
+ args[a.name] = v;
111877
+ });
111878
+ const missing = declared.filter((a) => a.required && args[a.name] === void 0);
111879
+ if (missing.length)
111880
+ throw new Error(`MCP prompt "${server}:${name}" is missing required argument(s): ${missing.map((a) => a.name).join(", ")}.`);
111881
+ let timer;
111882
+ const timeout = new Promise((_2, rej) => {
111883
+ timer = setTimeout(() => rej(new Error(`MCP prompt ${server}:${name} timed out after 60s`)), 6e4);
111884
+ });
111885
+ try {
111886
+ return await Promise.race([client.getPrompt(name, args), timeout]);
111887
+ } finally {
111888
+ if (timer)
111889
+ clearTimeout(timer);
111890
+ }
111891
+ }
111386
111892
  /** True if this tool name belongs to an MCP server. */
111387
111893
  isMcpTool(name) {
111388
111894
  return this._toolMap.has(name);
@@ -111493,6 +111999,7 @@ var require_manager = __commonJS({
111493
111999
  if (this._toolMap.get(key) === name)
111494
112000
  this._toolMap.delete(key);
111495
112001
  }
112002
+ this._promptCache.delete(name);
111496
112003
  this.stats.reset();
111497
112004
  }
111498
112005
  // ─── Config file persistence (for the management UI) ──────────────────────────
@@ -141804,8 +142311,8 @@ var require_strip = __commonJS({
141804
142311
  var require_src4 = __commonJS({
141805
142312
  "../../node_modules/.pnpm/sisteransi@1.0.5/node_modules/sisteransi/src/index.js"(exports2, module2) {
141806
142313
  "use strict";
141807
- var ESC3 = "\x1B";
141808
- var CSI2 = `${ESC3}[`;
142314
+ var ESC4 = "\x1B";
142315
+ var CSI2 = `${ESC4}[`;
141809
142316
  var beep2 = "\x07";
141810
142317
  var cursor = {
141811
142318
  to(x2, y) {
@@ -141834,8 +142341,8 @@ var require_src4 = __commonJS({
141834
142341
  left: `${CSI2}G`,
141835
142342
  hide: `${CSI2}?25l`,
141836
142343
  show: `${CSI2}?25h`,
141837
- save: `${ESC3}7`,
141838
- restore: `${ESC3}8`
142344
+ save: `${ESC4}7`,
142345
+ restore: `${ESC4}8`
141839
142346
  };
141840
142347
  var scroll = {
141841
142348
  up: (count = 1) => `${CSI2}S`.repeat(count),
@@ -155147,7 +155654,7 @@ import * as os18 from "os";
155147
155654
  import * as crypto7 from "crypto";
155148
155655
  import { createRequire } from "module";
155149
155656
  import { fileURLToPath as fileURLToPath2 } from "url";
155150
- import { execSync, execFileSync as execFileSync5 } from "child_process";
155657
+ import { execFile as execFile2, execFileSync as execFileSync5 } from "child_process";
155151
155658
 
155152
155659
  // src/permissions/handler.ts
155153
155660
  import * as readline2 from "readline";
@@ -166671,6 +167178,208 @@ function askForTrust(workDir, signals) {
166671
167178
  });
166672
167179
  }
166673
167180
 
167181
+ // src/ui/inputReports.ts
167182
+ var ESC3 = "\x1B";
167183
+ var BEL3 = "\x07";
167184
+ var REPORT_DEADLINE_MS = 120;
167185
+ var OSC_CAP = 4096;
167186
+ function newInputReportState() {
167187
+ return { pending: "", oscSkipLeft: 0 };
167188
+ }
167189
+ function filterInput(state, chunk, opts = {}) {
167190
+ const s2 = state.pending + chunk;
167191
+ state.pending = "";
167192
+ let out = "";
167193
+ let i2 = 0;
167194
+ if (state.oscSkipLeft > 0) {
167195
+ let j = i2;
167196
+ let terminated = false;
167197
+ while (j < s2.length) {
167198
+ if (s2[j] === BEL3) {
167199
+ terminated = true;
167200
+ j += 1;
167201
+ break;
167202
+ }
167203
+ if (s2[j] === ESC3 && s2[j + 1] === "\\") {
167204
+ terminated = true;
167205
+ j += 2;
167206
+ break;
167207
+ }
167208
+ if (s2[j] === ESC3 && j === s2.length - 1)
167209
+ break;
167210
+ j += 1;
167211
+ }
167212
+ const split = !terminated && j < s2.length && s2[j] === ESC3;
167213
+ if (terminated)
167214
+ state.oscSkipLeft = 0;
167215
+ else
167216
+ state.oscSkipLeft = Math.max(0, state.oscSkipLeft - (j - i2));
167217
+ if (split) {
167218
+ state.pending = s2.slice(j);
167219
+ return out;
167220
+ }
167221
+ i2 = j;
167222
+ }
167223
+ while (i2 < s2.length) {
167224
+ if (s2[i2] !== ESC3) {
167225
+ out += s2[i2];
167226
+ i2 += 1;
167227
+ continue;
167228
+ }
167229
+ if (i2 === s2.length - 1) {
167230
+ out += ESC3;
167231
+ i2 += 1;
167232
+ continue;
167233
+ }
167234
+ const next = s2[i2 + 1];
167235
+ if (next === "]") {
167236
+ const end = oscEnd(s2, i2 + 2);
167237
+ if (end >= 0) {
167238
+ i2 = end;
167239
+ continue;
167240
+ }
167241
+ if (s2.length - (i2 + 2) > OSC_CAP) {
167242
+ state.oscSkipLeft = OSC_CAP;
167243
+ i2 = s2.length;
167244
+ continue;
167245
+ }
167246
+ state.pending = s2.slice(i2);
167247
+ break;
167248
+ }
167249
+ if (next === "[") {
167250
+ const r2 = scanCsi(s2, i2, opts);
167251
+ if (r2.kind === "hold") {
167252
+ state.pending = s2.slice(i2);
167253
+ break;
167254
+ }
167255
+ if (r2.kind === "pass")
167256
+ out += s2.slice(i2, r2.end);
167257
+ i2 = r2.end;
167258
+ continue;
167259
+ }
167260
+ out += ESC3 + next;
167261
+ i2 += 2;
167262
+ }
167263
+ return out;
167264
+ }
167265
+ function releasePendingOnDeadline(state, _opts = {}) {
167266
+ const held = state.pending;
167267
+ state.pending = "";
167268
+ if (held === "")
167269
+ return "";
167270
+ if (held.startsWith(ESC3 + "]"))
167271
+ return "";
167272
+ if (held.startsWith(ESC3 + "[")) {
167273
+ const rest = held.slice(2);
167274
+ if (rest.startsWith("M") || rest.startsWith("<"))
167275
+ return "";
167276
+ return ESC3;
167277
+ }
167278
+ return "";
167279
+ }
167280
+ function scanCsi(s2, start, opts) {
167281
+ let j = start + 2;
167282
+ while (j < s2.length) {
167283
+ const c = s2.charCodeAt(j);
167284
+ if (c >= 64 && c <= 126)
167285
+ break;
167286
+ if (c >= 48 && c <= 63) {
167287
+ j += 1;
167288
+ continue;
167289
+ }
167290
+ if (c >= 32 && c <= 47) {
167291
+ j += 1;
167292
+ continue;
167293
+ }
167294
+ return { kind: "drop", end: start + 2 };
167295
+ }
167296
+ if (j >= s2.length)
167297
+ return { kind: "hold" };
167298
+ const final = s2[j];
167299
+ const body = s2.slice(start + 2, j);
167300
+ const seqEnd = j + 1;
167301
+ if (final === "M" && body === "") {
167302
+ if (s2.length < seqEnd + 3)
167303
+ return { kind: "hold" };
167304
+ return opts.passMouse ? { kind: "pass", end: seqEnd + 3 } : { kind: "drop", end: seqEnd + 3 };
167305
+ }
167306
+ if (body.startsWith("<")) {
167307
+ return opts.passMouse ? { kind: "pass", end: seqEnd } : { kind: "drop", end: seqEnd };
167308
+ }
167309
+ if (/^[?>=!]/.test(body))
167310
+ return { kind: "drop", end: seqEnd };
167311
+ if (body === "" && (final === "I" || final === "O"))
167312
+ return { kind: "drop", end: seqEnd };
167313
+ if (final === "R" && /^\d+;\d+$/.test(body))
167314
+ return { kind: "drop", end: seqEnd };
167315
+ return { kind: "pass", end: seqEnd };
167316
+ }
167317
+ function oscEnd(s2, from) {
167318
+ for (let i2 = from; i2 < s2.length; i2 += 1) {
167319
+ if (s2[i2] === BEL3)
167320
+ return i2 + 1;
167321
+ if (s2[i2] === ESC3 && s2[i2 + 1] === "\\")
167322
+ return i2 + 2;
167323
+ }
167324
+ return -1;
167325
+ }
167326
+ var mousePassthrough = () => false;
167327
+ var installed = false;
167328
+ function setMousePassthrough(fn) {
167329
+ mousePassthrough = fn;
167330
+ }
167331
+ function installInputReportFilter(stream = process.stdin) {
167332
+ const isDefault = stream === process.stdin;
167333
+ if (isDefault && installed)
167334
+ return () => {
167335
+ };
167336
+ if (isDefault)
167337
+ installed = true;
167338
+ const stdin = stream;
167339
+ const origRead = stdin.read.bind(stdin);
167340
+ const state = newInputReportState();
167341
+ let outbox = "";
167342
+ let timer = null;
167343
+ const clearTimer = () => {
167344
+ if (timer) {
167345
+ clearTimeout(timer);
167346
+ timer = null;
167347
+ }
167348
+ };
167349
+ const onDeadline = () => {
167350
+ timer = null;
167351
+ outbox += releasePendingOnDeadline(state, { passMouse: mousePassthrough() });
167352
+ stdin.emit("readable");
167353
+ };
167354
+ stdin.read = function patchedRead(...args) {
167355
+ if (args.length > 0)
167356
+ return origRead(...args);
167357
+ let chunk;
167358
+ while ((chunk = origRead()) !== null && chunk !== void 0) {
167359
+ const s2 = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
167360
+ outbox += filterInput(state, s2, { passMouse: mousePassthrough() });
167361
+ }
167362
+ if (state.pending !== "") {
167363
+ clearTimer();
167364
+ timer = setTimeout(onDeadline, REPORT_DEADLINE_MS);
167365
+ timer.unref?.();
167366
+ } else {
167367
+ clearTimer();
167368
+ }
167369
+ if (outbox === "")
167370
+ return null;
167371
+ const out = outbox;
167372
+ outbox = "";
167373
+ return out;
167374
+ };
167375
+ return () => {
167376
+ clearTimer();
167377
+ stdin.read = origRead;
167378
+ if (isDefault)
167379
+ installed = false;
167380
+ };
167381
+ }
167382
+
166674
167383
  // src/ui/inkTerminal.tsx
166675
167384
  var import_react36 = __toESM(require_react(), 1);
166676
167385
 
@@ -166908,6 +167617,32 @@ function skillEntries(skills, builtins = BUILTIN_SLASH_COMMANDS) {
166908
167617
  }
166909
167618
  return out;
166910
167619
  }
167620
+ function promptEntries(prompts2, builtins = BUILTIN_SLASH_COMMANDS) {
167621
+ const taken = /* @__PURE__ */ new Set();
167622
+ for (const b of builtins) {
167623
+ taken.add(b.name);
167624
+ for (const a of b.aliases ?? [])
167625
+ taken.add(a);
167626
+ }
167627
+ const out = [];
167628
+ for (const p of prompts2) {
167629
+ const name = `${p.server}:${p.name}`;
167630
+ if (taken.has(name))
167631
+ continue;
167632
+ taken.add(name);
167633
+ const args = p.arguments ?? [];
167634
+ const description = (p.description ?? "").replace(/\s+/g, " ").trim() || `MCP prompt from ${p.server}`;
167635
+ out.push({
167636
+ name,
167637
+ description,
167638
+ argHint: args.length ? args.map((a) => a.required ? `<${a.name}>` : `[${a.name}]`).join(" ") : void 0,
167639
+ requiresArg: args.some((a) => a.required) || void 0,
167640
+ kind: "mcp",
167641
+ source: p.server
167642
+ });
167643
+ }
167644
+ return out;
167645
+ }
166911
167646
  function matchScore(query, name, aliases, description) {
166912
167647
  if (!query)
166913
167648
  return 0;
@@ -166948,7 +167683,7 @@ function slashQuery(line, cursor) {
166948
167683
  return null;
166949
167684
  if (line.includes("\n"))
166950
167685
  return null;
166951
- const m2 = /^\/([\w-]*)$/.exec(line);
167686
+ const m2 = /^\/([\w:-]*)$/.exec(line);
166952
167687
  if (!m2)
166953
167688
  return null;
166954
167689
  if (cursor < 1)
@@ -167758,7 +168493,7 @@ function describeOpen(spec, target, cwd2) {
167758
168493
  // src/ui/fileMention.ts
167759
168494
  import * as fs26 from "fs";
167760
168495
  import * as path26 from "path";
167761
- import { execFileSync as execFileSync4 } from "child_process";
168496
+ import { execFile, execFileSync as execFileSync4 } from "child_process";
167762
168497
 
167763
168498
  // src/ui/contextFiles.ts
167764
168499
  import * as fs25 from "fs";
@@ -167973,6 +168708,9 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
167973
168708
  ]);
167974
168709
  var MAX_INDEXED = 5e4;
167975
168710
  function listProjectFiles(cwd2) {
168711
+ return listProjectFilesFrom(cwd2).files;
168712
+ }
168713
+ function listProjectFilesFrom(cwd2) {
167976
168714
  let files = null;
167977
168715
  try {
167978
168716
  const out = execFileSync4("git", ["-C", cwd2, "ls-files", "-co", "--exclude-standard", "-z"], {
@@ -167984,34 +168722,41 @@ function listProjectFiles(cwd2) {
167984
168722
  files = [...new Set(out.split("\0").filter(Boolean))];
167985
168723
  } catch {
167986
168724
  }
168725
+ const source2 = files ? "git" : "walk";
167987
168726
  if (!files)
167988
168727
  files = walk(cwd2);
167989
- return files.filter((f3) => !/\s/.test(f3)).slice(0, MAX_INDEXED);
168728
+ return { files: files.filter((f3) => !/\s/.test(f3)).slice(0, MAX_INDEXED), source: source2 };
167990
168729
  }
167991
- function walk(root) {
167992
- const out = [];
167993
- const visit = (dir, depth) => {
167994
- if (out.length >= MAX_INDEXED || depth > 8)
167995
- return;
168730
+ function* walkEntries(root) {
168731
+ const queue = [{ dir: root, depth: 0 }];
168732
+ for (let i2 = 0; i2 < queue.length; i2++) {
168733
+ const { dir, depth } = queue[i2];
168734
+ if (depth > 8)
168735
+ continue;
167996
168736
  let entries;
167997
168737
  try {
167998
168738
  entries = fs26.readdirSync(dir, { withFileTypes: true });
167999
168739
  } catch {
168000
- return;
168740
+ continue;
168001
168741
  }
168002
168742
  for (const e2 of entries) {
168003
- if (out.length >= MAX_INDEXED)
168004
- return;
168005
168743
  const full = path26.join(dir, e2.name);
168006
168744
  if (e2.isDirectory()) {
168007
168745
  if (!IGNORED_DIRS.has(e2.name) && !e2.isSymbolicLink())
168008
- visit(full, depth + 1);
168746
+ queue.push({ dir: full, depth: depth + 1 });
168009
168747
  } else if (e2.isFile()) {
168010
- out.push(path26.relative(root, full).split(path26.sep).join("/"));
168748
+ yield path26.relative(root, full).split(path26.sep).join("/");
168011
168749
  }
168012
168750
  }
168013
- };
168014
- visit(root, 0);
168751
+ }
168752
+ }
168753
+ function walk(root) {
168754
+ const out = [];
168755
+ for (const f3 of walkEntries(root)) {
168756
+ out.push(f3);
168757
+ if (out.length >= MAX_INDEXED)
168758
+ break;
168759
+ }
168015
168760
  return out;
168016
168761
  }
168017
168762
  function buildIndex(files) {
@@ -168023,20 +168768,111 @@ function buildIndex(files) {
168023
168768
  }
168024
168769
  return [...dirs, ...files].sort();
168025
168770
  }
168771
+ var GIT_CACHE_MS = 5e3;
168772
+ var WALK_CACHE_MS = 6e4;
168026
168773
  var cache3 = null;
168027
- var CACHE_MS = 5e3;
168028
168774
  var excludedPatterns = [];
168775
+ var excludedKey = "[]";
168029
168776
  function setExcludedPatterns(patterns) {
168030
168777
  excludedPatterns = [...patterns];
168031
- cache3 = null;
168778
+ excludedKey = JSON.stringify(excludedPatterns);
168779
+ }
168780
+ function derivedIndex(raw) {
168781
+ return buildIndex(raw.filter((f3) => !isExcluded(f3, excludedPatterns)));
168032
168782
  }
168033
168783
  function projectIndex(cwd2, now = Date.now()) {
168034
- if (cache3 && cache3.cwd === cwd2 && now - cache3.at < CACHE_MS)
168784
+ if (cache3 && cache3.cwd === cwd2 && now - cache3.at < cache3.ttl) {
168785
+ if (cache3.patterns !== excludedKey)
168786
+ cache3 = { ...cache3, patterns: excludedKey, index: derivedIndex(cache3.raw) };
168035
168787
  return cache3.index;
168036
- const files = listProjectFiles(cwd2).filter((f3) => !isExcluded(f3, excludedPatterns));
168037
- const index = buildIndex(files);
168038
- cache3 = { cwd: cwd2, at: now, index };
168039
- return index;
168788
+ }
168789
+ const { files, source: source2 } = listProjectFilesFrom(cwd2);
168790
+ cache3 = {
168791
+ cwd: cwd2,
168792
+ at: now,
168793
+ ttl: source2 === "git" ? GIT_CACHE_MS : WALK_CACHE_MS,
168794
+ raw: files,
168795
+ patterns: excludedKey,
168796
+ index: derivedIndex(files)
168797
+ };
168798
+ return cache3.index;
168799
+ }
168800
+ var WALK_SLICE_MS = 8;
168801
+ var indexListener = null;
168802
+ function setMentionIndexListener(cb) {
168803
+ indexListener = cb;
168804
+ }
168805
+ var building = null;
168806
+ function walkInSlices(root, job, done) {
168807
+ const entries = walkEntries(root);
168808
+ const out = [];
168809
+ const step = () => {
168810
+ if (job.cancelled)
168811
+ return;
168812
+ const t0 = Date.now();
168813
+ for (; ; ) {
168814
+ if (out.length >= MAX_INDEXED)
168815
+ return done(out);
168816
+ const next = entries.next();
168817
+ if (next.done)
168818
+ return done(out);
168819
+ out.push(next.value);
168820
+ if (Date.now() - t0 >= WALK_SLICE_MS)
168821
+ break;
168822
+ }
168823
+ setImmediate(step);
168824
+ };
168825
+ setImmediate(step);
168826
+ }
168827
+ function finishBuild(job, files, source2) {
168828
+ if (job.cancelled)
168829
+ return;
168830
+ if (building === job)
168831
+ building = null;
168832
+ const raw = files.filter((f3) => !/\s/.test(f3)).slice(0, MAX_INDEXED);
168833
+ cache3 = {
168834
+ cwd: job.cwd,
168835
+ at: Date.now(),
168836
+ ttl: source2 === "git" ? GIT_CACHE_MS : WALK_CACHE_MS,
168837
+ raw,
168838
+ patterns: excludedKey,
168839
+ index: derivedIndex(raw)
168840
+ };
168841
+ indexListener?.();
168842
+ }
168843
+ function primeMentionIndex(cwd2) {
168844
+ if (cache3 && cache3.cwd === cwd2 && Date.now() - cache3.at < cache3.ttl)
168845
+ return;
168846
+ if (building && building.cwd === cwd2 && !building.cancelled)
168847
+ return;
168848
+ if (building)
168849
+ building.cancelled = true;
168850
+ const job = { cwd: cwd2, cancelled: false };
168851
+ building = job;
168852
+ execFile("git", ["-C", cwd2, "ls-files", "-co", "--exclude-standard", "-z"], {
168853
+ encoding: "utf-8",
168854
+ maxBuffer: 64 * 1024 * 1024,
168855
+ timeout: 5e3
168856
+ }, (err, stdout) => {
168857
+ if (job.cancelled)
168858
+ return;
168859
+ if (!err) {
168860
+ finishBuild(job, [...new Set(stdout.split("\0").filter(Boolean))], "git");
168861
+ return;
168862
+ }
168863
+ walkInSlices(cwd2, job, (files) => finishBuild(job, files, "walk"));
168864
+ });
168865
+ }
168866
+ function mentionIndexForRender(cwd2) {
168867
+ if (!cache3 || cache3.cwd !== cwd2) {
168868
+ primeMentionIndex(cwd2);
168869
+ return [];
168870
+ }
168871
+ if (cache3.patterns !== excludedKey)
168872
+ cache3 = { ...cache3, patterns: excludedKey, index: derivedIndex(cache3.raw) };
168873
+ if (Date.now() - cache3.at >= cache3.ttl)
168874
+ primeMentionIndex(cwd2);
168875
+ return cache3.index;
168040
168876
  }
168041
168877
  function subsequence(hay, needle) {
168042
168878
  let i2 = 0;
@@ -168079,13 +168915,13 @@ function rankMentions(query, index, limit = 200) {
168079
168915
  }
168080
168916
  return index.map((p, i2) => ({ p, i: i2, s: score(p, q) })).filter((x2) => x2.s !== Infinity).sort((a, b) => a.s - b.s || a.p.length - b.p.length || a.i - b.i).slice(0, limit).map((x2) => x2.p);
168081
168917
  }
168082
- function computeMention(line, cursor, index, dismissedFor) {
168918
+ function computeMention(line, cursor, getIndex, dismissedFor) {
168083
168919
  if (dismissedFor !== null && dismissedFor === line)
168084
168920
  return null;
168085
168921
  const at = mentionAt(line, cursor);
168086
168922
  if (!at)
168087
168923
  return null;
168088
- const items = rankMentions(at.query, index);
168924
+ const items = rankMentions(at.query, getIndex());
168089
168925
  return items.length ? { at, items } : null;
168090
168926
  }
168091
168927
  function decideMentionEnter(query, pick) {
@@ -168562,7 +169398,7 @@ function slashMenuView(p) {
168562
169398
  return {
168563
169399
  rows: shown.map((e2, i2) => {
168564
169400
  const label = `/${e2.name}${e2.argHint ? " " + e2.argHint : ""}`;
168565
- const tag = e2.kind === "skill" ? ` (${e2.source === "project" || !e2.source ? "skill" : e2.source + " skill"})` : "";
169401
+ const tag = e2.kind === "mcp" ? " (MCP)" : e2.kind === "skill" ? ` (${e2.source === "project" || !e2.source ? "skill" : e2.source + " skill"})` : "";
168566
169402
  const alias = e2.aliases?.length ? ` (${e2.aliases.map((a) => "/" + a).join(", ")})` : "";
168567
169403
  const seen = e2.recent ? " \xB7 recent" : "";
168568
169404
  return {
@@ -168617,7 +169453,7 @@ function fullscreenView(p) {
168617
169453
  function mentionMenuView(p) {
168618
169454
  if (p.blocked)
168619
169455
  return null;
168620
- const m2 = computeMention(p.line, p.cursorPos, projectIndex(p.root), p.dismissedFor);
169456
+ const m2 = computeMention(p.line, p.cursorPos, () => mentionIndexForRender(p.root), p.dismissedFor);
168621
169457
  if (!m2)
168622
169458
  return null;
168623
169459
  const sel = Math.min(p.sel.query === m2.at.query ? p.sel.idx : 0, m2.items.length - 1);
@@ -168806,12 +169642,12 @@ var App2 = ({ onReady }) => {
168806
169642
  const mentionRootRef = (0, import_react36.useRef)(process.cwd());
168807
169643
  const setMentionRoot = (0, import_react36.useCallback)((dir) => {
168808
169644
  mentionRootRef.current = dir;
168809
- setImmediate(() => {
168810
- try {
168811
- projectIndex(dir);
168812
- } catch {
168813
- }
168814
- });
169645
+ primeMentionIndex(dir);
169646
+ }, []);
169647
+ const [, bumpMentionIndex] = (0, import_react36.useState)(0);
169648
+ import_react36.default.useEffect(() => {
169649
+ setMentionIndexListener(() => bumpMentionIndex((n) => n + 1));
169650
+ return () => setMentionIndexListener(null);
168815
169651
  }, []);
168816
169652
  const setSlashEntries = (0, import_react36.useCallback)((entries) => {
168817
169653
  slashEntriesRef.current = entries;
@@ -169254,7 +170090,7 @@ var App2 = ({ onReady }) => {
169254
170090
  }
169255
170091
  }
169256
170092
  }
169257
- const mention = askResolverRef.current || choiceRef.current || searchRef.current || historyRef.current.browsing || busyRef.current ? null : computeMention(lineRef.current, cursorRef.current, projectIndex(mentionRootRef.current), mentionDismissedRef.current);
170093
+ const mention = askResolverRef.current || choiceRef.current || searchRef.current || historyRef.current.browsing || busyRef.current ? null : computeMention(lineRef.current, cursorRef.current, () => mentionIndexForRender(mentionRootRef.current), mentionDismissedRef.current);
169258
170094
  if (mention) {
169259
170095
  const sel = mentionSelRef.current.query === mention.at.query ? mentionSelRef.current.idx : 0;
169260
170096
  const pick = mention.items[Math.min(sel, mention.items.length - 1)];
@@ -169626,6 +170462,9 @@ var App2 = ({ onReady }) => {
169626
170462
  if (fullscreenRef.current)
169627
170463
  process.stdout.write(on ? MOUSE_ON : MOUSE_OFF);
169628
170464
  }, []);
170465
+ import_react36.default.useEffect(() => {
170466
+ setMousePassthrough(() => fullscreenRef.current && mouseRef.current);
170467
+ }, []);
169629
170468
  import_react36.default.useEffect(() => () => {
169630
170469
  if (fullscreenRef.current) {
169631
170470
  fullscreenRef.current = false;
@@ -169982,6 +170821,38 @@ var InkReadlineAdapter = class extends EventEmitter3 {
169982
170821
  }
169983
170822
  };
169984
170823
 
170824
+ // src/ui/mcpPrompt.ts
170825
+ function parseMcpPromptRef(cmd, serverNames) {
170826
+ const bare = cmd.startsWith("/") ? cmd.slice(1) : cmd;
170827
+ if (bare.startsWith("mcp__")) {
170828
+ const rest = bare.slice("mcp__".length);
170829
+ const candidates = serverNames.filter((s2) => rest.startsWith(s2 + "__")).sort((a, b) => b.length - a.length);
170830
+ const server = candidates[0];
170831
+ if (!server)
170832
+ return null;
170833
+ const name = rest.slice(server.length + 2);
170834
+ return name ? { server, name } : null;
170835
+ }
170836
+ const colon = bare.indexOf(":");
170837
+ if (colon > 0) {
170838
+ const server = bare.slice(0, colon);
170839
+ const name = bare.slice(colon + 1);
170840
+ if (name && serverNames.includes(server))
170841
+ return { server, name };
170842
+ }
170843
+ return null;
170844
+ }
170845
+ function splitPromptArgs(arg) {
170846
+ const out = [];
170847
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
170848
+ let m2;
170849
+ while (m2 = re.exec(arg)) {
170850
+ const v = m2[1] ?? m2[2] ?? m2[3];
170851
+ out.push(v);
170852
+ }
170853
+ return out;
170854
+ }
170855
+
169985
170856
  // src/ui/recents.ts
169986
170857
  import fs27 from "node:fs";
169987
170858
  import os17 from "node:os";
@@ -171845,12 +172716,16 @@ function resolveModelLabel(alias) {
171845
172716
  const id = normaliseModelId(alias);
171846
172717
  return (0, import_code_core18.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
171847
172718
  }
171848
- function tryExec(cmd, cwd2) {
171849
- try {
171850
- return execSync(cmd, { cwd: cwd2, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim() || void 0;
171851
- } catch {
171852
- return void 0;
171853
- }
172719
+ async function tryExecAsync(file, args, cwd2) {
172720
+ return new Promise((resolve12) => {
172721
+ try {
172722
+ execFile2(file, args, { cwd: cwd2, encoding: "utf-8" }, (err, stdout) => {
172723
+ resolve12(err ? void 0 : String(stdout).trim() || void 0);
172724
+ });
172725
+ } catch {
172726
+ resolve12(void 0);
172727
+ }
172728
+ });
171854
172729
  }
171855
172730
  var BILLING_URL = "https://app.nexrall.com/?settings=billing";
171856
172731
  function hyperlink(label, url) {
@@ -171878,28 +172753,29 @@ function currentUserLabel() {
171878
172753
  return "there";
171879
172754
  }
171880
172755
  }
171881
- function collectEnv(workDir) {
172756
+ async function collectEnv(workDir) {
171882
172757
  return {
171883
172758
  cwd: workDir,
171884
172759
  platform: `${os18.platform()} ${os18.release()}`,
171885
172760
  shell: process.env.SHELL ?? process.env.ComSpec ?? "unknown",
171886
172761
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
171887
- ...collectGitEnv(workDir)
172762
+ ...await collectGitEnv(workDir)
171888
172763
  };
171889
172764
  }
171890
- function collectGitEnv(workDir) {
171891
- return {
171892
- gitBranch: tryExec("git branch --show-current", workDir),
171893
- gitStatus: tryExec("git status --short", workDir),
171894
- gitDiff: tryExec("git diff --stat HEAD", workDir),
171895
- recentCommits: tryExec("git log --oneline -5", workDir)
171896
- };
172765
+ async function collectGitEnv(workDir) {
172766
+ const [gitBranch2, gitStatus, gitDiff, recentCommits] = await Promise.all([
172767
+ tryExecAsync("git", ["branch", "--show-current"], workDir),
172768
+ tryExecAsync("git", ["status", "--short"], workDir),
172769
+ tryExecAsync("git", ["diff", "--stat", "HEAD"], workDir),
172770
+ tryExecAsync("git", ["log", "--oneline", "-5"], workDir)
172771
+ ]);
172772
+ return { gitBranch: gitBranch2, gitStatus, gitDiff, recentCommits };
171897
172773
  }
171898
- function refreshEnvForTurn(env4, workDir) {
172774
+ async function refreshEnvForTurn(env4, workDir) {
171899
172775
  return {
171900
172776
  ...env4,
171901
172777
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
171902
- ...collectGitEnv(workDir)
172778
+ ...await collectGitEnv(workDir)
171903
172779
  };
171904
172780
  }
171905
172781
  var noDiscovery = false;
@@ -172083,6 +172959,10 @@ var Spinner = class {
172083
172959
  verbIdx = 0;
172084
172960
  thinkTokens = 0;
172085
172961
  thinkText = "";
172962
+ // A running hook's `statusMessage` (Claude Code). While set it REPLACES the label —
172963
+ // the whole point is telling the user which hook is producing the wait — and the
172964
+ // normal label returns the moment the hook finishes (listener sends null).
172965
+ hookStatus = "";
172086
172966
  start(text, opts) {
172087
172967
  this.stop();
172088
172968
  this.baseText = text;
@@ -172137,13 +173017,24 @@ var Spinner = class {
172137
173017
  return;
172138
173018
  this.baseText = text;
172139
173019
  }
173020
+ /**
173021
+ * A hook with `statusMessage` started (message) or finished (null). Renders immediately
173022
+ * rather than on the next tick: the whole point of the message is that a long hook is
173023
+ * RUNNING, and waiting up to 100ms to admit that is the kind of silence it exists to fix.
173024
+ * No-op while the spinner is stopped — an idle session keeps its prompt, not a status line.
173025
+ */
173026
+ setHookStatus(message) {
173027
+ this.hookStatus = message ?? "";
173028
+ if (this.interval !== null)
173029
+ this.render();
173030
+ }
172140
173031
  render() {
172141
173032
  const elapsedSec = Math.floor((Date.now() - this.startedAt) / 1e3);
172142
173033
  if (this.verbs && elapsedSec > 0 && elapsedSec % VERB_ROTATE_SEC === 0 && this.lastRotateSec !== elapsedSec) {
172143
173034
  this.lastRotateSec = elapsedSec;
172144
173035
  this.verbIdx++;
172145
173036
  }
172146
- const label = this.verbs ? `${WORKING_VERBS[this.verbIdx % WORKING_VERBS.length]}\u2026` : this.baseText;
173037
+ const label = this.hookStatus || (this.verbs ? `${WORKING_VERBS[this.verbIdx % WORKING_VERBS.length]}\u2026` : this.baseText);
172147
173038
  const status = composeStatusLine({
172148
173039
  frame: this.frames[this.i % this.frames.length],
172149
173040
  text: label,
@@ -172192,6 +173083,7 @@ async function initMcp(workDir, override, strict) {
172192
173083
  if (noDiscovery && !override) {
172193
173084
  _mcpConfig = { mcpServers: {} };
172194
173085
  _mcpManager = null;
173086
+ wireHookMcpCaller();
172195
173087
  return;
172196
173088
  }
172197
173089
  _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core18.McpManager.loadConfig(workDir);
@@ -172199,6 +173091,7 @@ async function initMcp(workDir, override, strict) {
172199
173091
  Object.assign(_mcpConfig.mcpServers, override.mcpServers);
172200
173092
  if (Object.keys(_mcpConfig.mcpServers).length === 0) {
172201
173093
  _mcpManager = null;
173094
+ wireHookMcpCaller();
172202
173095
  return;
172203
173096
  }
172204
173097
  try {
@@ -172208,6 +173101,18 @@ async function initMcp(workDir, override, strict) {
172208
173101
  const manager = new import_code_core18.McpManager();
172209
173102
  await manager.connectAll(_mcpConfig);
172210
173103
  _mcpManager = manager;
173104
+ wireHookMcpCaller();
173105
+ }
173106
+ function wireHookMcpCaller() {
173107
+ const mgr = _mcpManager;
173108
+ if (!mgr) {
173109
+ (0, import_code_core19.setHookMcpCaller)(null);
173110
+ return;
173111
+ }
173112
+ (0, import_code_core19.setHookMcpCaller)(async (server, tool, input) => {
173113
+ const text = await mgr.callTool(`${server}__${tool}`, input);
173114
+ return text === null ? { error: `MCP server "${server}" has no tool "${tool}" (or the server is not connected)` } : { text };
173115
+ });
172211
173116
  }
172212
173117
  function formatAgentsList(workDir) {
172213
173118
  const { types: types3, warnings } = (0, import_code_core18.loadAgentTypesWithWarnings)(workDir);
@@ -172583,6 +173488,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
172583
173488
  const turnStart = Date.now();
172584
173489
  userActiveThisTurn = false;
172585
173490
  const spinner = new Spinner();
173491
+ (0, import_code_core19.setHookStatusListener)((m2) => spinner.setHookStatus(m2));
172586
173492
  const mdRender = new MarkdownStreamRenderer();
172587
173493
  const toolStream = new ToolStreamPrinter(PREVIEW_LINES);
172588
173494
  let toolStartTime = 0;
@@ -172609,7 +173515,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
172609
173515
  workDir,
172610
173516
  sessionId: activeSessionIdGetter(),
172611
173517
  model: modelAlias,
172612
- env: refreshEnvForTurn(env4, workDir),
173518
+ env: await refreshEnvForTurn(env4, workDir),
172613
173519
  nexrallMd,
172614
173520
  mode,
172615
173521
  // `/mode plan` previously only hinted to the backend — the footer said "plan
@@ -172962,7 +173868,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
172962
173868
  workDir,
172963
173869
  sessionId: activeSessionIdGetter(),
172964
173870
  model: modelAlias,
172965
- env: refreshEnvForTurn(env4, workDir),
173871
+ env: await refreshEnvForTurn(env4, workDir),
172966
173872
  nexrallMd,
172967
173873
  mode,
172968
173874
  planMode: mode === "plan",
@@ -173254,7 +174160,7 @@ function printHelp() {
173254
174160
  ["/skills", "List skills (.nexrall/skills/<name>/SKILL.md or .nexrall/commands/*.md) \u2014 the model can also auto-invoke these"],
173255
174161
  ["/commands", "List your custom commands (.nexrall/commands/*.md)"],
173256
174162
  ["/plugins", "List installed plugins (.nexrall/plugins/)"],
173257
- ["/mcp [name|verb]", "List servers; <name> inspects tools/latency; enable|disable persists, connect|disconnect lasts the session"],
174163
+ ["/mcp [name|verb]", "List servers; <name> inspects tools/latency; enable|disable persists, connect|disconnect lasts the session; prompts lists /server:prompt templates"],
173258
174164
  ["/open <path[:line]>", 'Open a file in your editor (bare /open picks from files this session touched; finds "auth.ts" anywhere)'],
173259
174165
  ["/doctor", "Check Node, sign-in, network, terminal, MCP and storage; shows how to fix each problem"],
173260
174166
  ["/fullscreen", "Draw the session on the terminal's alternate screen: it re-wraps on resize and PgUp/PgDn page through it"],
@@ -173383,6 +174289,7 @@ function printSessions() {
173383
174289
  console.log();
173384
174290
  }
173385
174291
  async function startChatSession(options) {
174292
+ installInputReportFilter();
173386
174293
  if (options.bare)
173387
174294
  setNoDiscovery(true);
173388
174295
  _cliAgentTypes = (options.agents ?? []).map((a) => ({ ...a, source: "programmatic" }));
@@ -173487,7 +174394,7 @@ Resume it with: nex --worktree ${worktreeState.name}
173487
174394
  void (0, import_code_core19.fireObserverHook)(requestedWorkDir, "WorktreeRemove", { name: worktreeState.name, path: worktreeState.worktreePath }, worktreeState.name);
173488
174395
  });
173489
174396
  }
173490
- const env4 = collectEnv(workDir);
174397
+ const env4 = await collectEnv(workDir);
173491
174398
  let briefBase = options.systemPrompt?.trim() ?? readNexrallMd(workDir);
173492
174399
  const agentPrefix = options.selectedAgent?.prompt?.trim();
173493
174400
  const briefParts = [agentPrefix, briefBase, options.appendSystemPrompt?.trim(), addDirNote(options.addDirs ?? []), options.jsonSchema ? schemaInstruction(options.jsonSchema) : ""];
@@ -173952,13 +174859,32 @@ ${text}` : "");
173952
174859
  console.log(paint.green(` \u2713 Saved to ${saved.scope} memory`) + paint.dim(` \u2014 ${saved.file}`) + (saved.superseded ? paint.dim(" (the older fact moved to Superseded)") : ""));
173953
174860
  }
173954
174861
  };
173955
- const allSlashEntries = () => [...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core18.userInvokableSkills)(skills))];
174862
+ let mcpPromptList = [];
174863
+ const allSlashEntries = () => {
174864
+ const base = [...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core18.userInvokableSkills)(skills))];
174865
+ return [...base, ...promptEntries(mcpPromptList, base)];
174866
+ };
173956
174867
  let slashRecents = loadRecents();
173957
174868
  const syncSlashMenu = () => {
173958
174869
  if (interactive)
173959
174870
  getInkTerminal().setSlashEntries(orderByRecents(allSlashEntries(), slashRecents));
173960
174871
  };
174872
+ const refreshMcpPrompts = async () => {
174873
+ if (!_mcpManager)
174874
+ return;
174875
+ let list;
174876
+ try {
174877
+ list = await _mcpManager.listPrompts();
174878
+ } catch {
174879
+ return;
174880
+ }
174881
+ if (JSON.stringify(list) === JSON.stringify(mcpPromptList))
174882
+ return;
174883
+ mcpPromptList = list;
174884
+ syncSlashMenu();
174885
+ };
173961
174886
  syncSlashMenu();
174887
+ void mcpStarted.then(() => refreshMcpPrompts());
173962
174888
  if (interactive)
173963
174889
  getInkTerminal().setMentionRoot(workDir);
173964
174890
  const doRewind = (id) => {
@@ -174251,6 +175177,7 @@ ${text}` : "");
174251
175177
  }
174252
175178
  }
174253
175179
  }
175180
+ void refreshMcpPrompts();
174254
175181
  const outcome = {};
174255
175182
  if (error !== void 0)
174256
175183
  outcome.error = error;
@@ -174590,6 +175517,7 @@ ${text}` : "");
174590
175517
  activeGoal = null;
174591
175518
  criteriaLastLine = "";
174592
175519
  cliBackgroundAgents.stopAll({ dropPending: true });
175520
+ (0, import_code_core19.resetHookOnceState)();
174593
175521
  inputBuffer = "";
174594
175522
  if (interactive)
174595
175523
  await getInkTerminal().clearTranscript();
@@ -174796,7 +175724,7 @@ ${summaryText}` }] },
174796
175724
  }
174797
175725
  const readme = tryRead(path29.join(workDir, "README.md"))?.slice(0, 2e3) ?? "";
174798
175726
  const pkgJson = tryRead(path29.join(workDir, "package.json"))?.slice(0, 1e3) ?? "";
174799
- const dirList = tryExec("ls -la", workDir)?.slice(0, 500) ?? "";
175727
+ const dirList = (await tryExecAsync("ls", ["-la"], workDir))?.slice(0, 500) ?? "";
174800
175728
  const initPrompt = `Analyze this project and write a concise nexrall.md (project-specific instructions for an AI coding assistant).
174801
175729
  Include: project overview, tech stack, coding conventions, important files/folders, what to avoid.
174802
175730
  Keep it under 400 words. Use markdown. Be specific and actionable.
@@ -176134,6 +177062,26 @@ ${text}
176134
177062
  rl.prompt();
176135
177063
  return;
176136
177064
  }
177065
+ const promptArg = /^prompts?(?:\s+(\S+))?$/i.exec(arg.trim());
177066
+ if (promptArg) {
177067
+ await refreshMcpPrompts();
177068
+ const rows = promptArg[1] ? mcpPromptList.filter((p) => p.server === promptArg[1]) : mcpPromptList;
177069
+ console.log(paint.bold(" MCP prompts:"));
177070
+ if (!rows.length) {
177071
+ console.log(" " + paint.dim(promptArg[1] ? `No prompts offered by "${promptArg[1]}".` : "No prompts offered by the connected servers."));
177072
+ } else {
177073
+ const width = Math.max(...rows.map((p) => `${p.server}:${p.name}`.length));
177074
+ for (const p of rows) {
177075
+ const args = (p.arguments ?? []).map((a) => a.required ? `<${a.name}>` : `[${a.name}]`).join(" ");
177076
+ console.log(" " + paint.cyan(`/${p.server}:${p.name}`.padEnd(width + 1)) + " " + (args ? paint.yellow(args) + " " : "") + paint.dim((p.description ?? "").replace(/\s+/g, " ").trim() || `MCP prompt from ${p.server}`));
177077
+ }
177078
+ console.log();
177079
+ console.log(" " + paint.dim("Run one: type /server:prompt with its arguments (positional; quotes keep spaces together)."));
177080
+ }
177081
+ console.log();
177082
+ rl.prompt();
177083
+ return;
177084
+ }
176137
177085
  console.log(paint.bold(" MCP servers:"));
176138
177086
  await handleMcp(arg);
176139
177087
  console.log();
@@ -176572,7 +177520,43 @@ ${text}
176572
177520
  return;
176573
177521
  }
176574
177522
  default: {
177523
+ const bare = cmd.slice(1);
177524
+ if (bare.startsWith("mcp__") || bare.includes(":"))
177525
+ await mcpReady();
176575
177526
  const custom = (0, import_code_core18.findSkill)(skills, cmd);
177527
+ const mcpRef = custom && !bare.startsWith("mcp__") ? null : parseMcpPromptRef(bare, _mcpManager?.serverNames ?? []);
177528
+ if (mcpRef && _mcpManager) {
177529
+ let promptText;
177530
+ try {
177531
+ promptText = await _mcpManager.getPrompt(mcpRef.server, mcpRef.name, splitPromptArgs(arg));
177532
+ } catch (err) {
177533
+ console.log(paint.red(` ${err.message}`));
177534
+ rl.prompt();
177535
+ return;
177536
+ }
177537
+ console.log(paint.dim(` Running /${cmd} (MCP prompt from ${mcpRef.server})...`));
177538
+ checkpoints.beginTurn(`/${cmd} ${arg}`.trim(), messages.length);
177539
+ messages.push({ role: "user", content: [{ type: "text", text: promptText }] });
177540
+ rl.pause();
177541
+ abortSignal.aborted = false;
177542
+ agentRunning = true;
177543
+ try {
177544
+ const result = await runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, takePendingInput, worktreeState, peerCtx);
177545
+ messages = result.messages;
177546
+ updateTitle();
177547
+ saveSession2(sessionId, sessionTitle, workDir, messages);
177548
+ } catch (err) {
177549
+ messages = recoverTurn(err, messages);
177550
+ updateTitle();
177551
+ saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
177552
+ } finally {
177553
+ agentRunning = false;
177554
+ commitCheckpoint();
177555
+ }
177556
+ rl.resume();
177557
+ rl.prompt();
177558
+ return;
177559
+ }
176576
177560
  if (!custom) {
176577
177561
  console.log(paint.red(` Unknown command: ${cmd}`));
176578
177562
  console.log(paint.dim(" Type /help for built-ins or /skills for custom ones."));
@@ -176666,6 +177650,7 @@ ${text}
176666
177650
  rl.resume();
176667
177651
  rl.prompt();
176668
177652
  deliverBackgroundWhenIdle();
177653
+ deliverHookWakeWhenIdle();
176669
177654
  });
176670
177655
  async function runGoalLoop() {
176671
177656
  while (activeGoal) {
@@ -176746,6 +177731,20 @@ ${text}
176746
177731
  bgLastCount = n;
176747
177732
  deliverBackgroundWhenIdle();
176748
177733
  });
177734
+ let hookWakeTimer = null;
177735
+ function deliverHookWakeWhenIdle() {
177736
+ if (hookWakeTimer || agentRunning || !(0, import_code_core19.hasPendingHookWake)())
177737
+ return;
177738
+ hookWakeTimer = setTimeout(() => {
177739
+ hookWakeTimer = null;
177740
+ if (agentRunning || !(0, import_code_core19.hasPendingHookWake)())
177741
+ return;
177742
+ const wakes = (0, import_code_core19.takeHookWakes)();
177743
+ console.log(paint.dim(` \u2699 hook wake \u2014 continuing`));
177744
+ rl.emit("line", (0, import_code_core19.formatHookDeliveries)(wakes));
177745
+ }, 400);
177746
+ }
177747
+ (0, import_code_core19.setHookWakeListener)(deliverHookWakeWhenIdle);
176749
177748
  const loopTimer = setInterval(() => {
176750
177749
  if (loopScheduler.size === 0)
176751
177750
  return;