nexrall-code 0.5.177 → 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 +464 -45
  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)
@@ -155286,7 +155654,7 @@ import * as os18 from "os";
155286
155654
  import * as crypto7 from "crypto";
155287
155655
  import { createRequire } from "module";
155288
155656
  import { fileURLToPath as fileURLToPath2 } from "url";
155289
- import { execSync, execFileSync as execFileSync5 } from "child_process";
155657
+ import { execFile as execFile2, execFileSync as execFileSync5 } from "child_process";
155290
155658
 
155291
155659
  // src/permissions/handler.ts
155292
155660
  import * as readline2 from "readline";
@@ -172348,12 +172716,16 @@ function resolveModelLabel(alias) {
172348
172716
  const id = normaliseModelId(alias);
172349
172717
  return (0, import_code_core18.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
172350
172718
  }
172351
- function tryExec(cmd, cwd2) {
172352
- try {
172353
- return execSync(cmd, { cwd: cwd2, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim() || void 0;
172354
- } catch {
172355
- return void 0;
172356
- }
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
+ });
172357
172729
  }
172358
172730
  var BILLING_URL = "https://app.nexrall.com/?settings=billing";
172359
172731
  function hyperlink(label, url) {
@@ -172381,28 +172753,29 @@ function currentUserLabel() {
172381
172753
  return "there";
172382
172754
  }
172383
172755
  }
172384
- function collectEnv(workDir) {
172756
+ async function collectEnv(workDir) {
172385
172757
  return {
172386
172758
  cwd: workDir,
172387
172759
  platform: `${os18.platform()} ${os18.release()}`,
172388
172760
  shell: process.env.SHELL ?? process.env.ComSpec ?? "unknown",
172389
172761
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
172390
- ...collectGitEnv(workDir)
172762
+ ...await collectGitEnv(workDir)
172391
172763
  };
172392
172764
  }
172393
- function collectGitEnv(workDir) {
172394
- return {
172395
- gitBranch: tryExec("git branch --show-current", workDir),
172396
- gitStatus: tryExec("git status --short", workDir),
172397
- gitDiff: tryExec("git diff --stat HEAD", workDir),
172398
- recentCommits: tryExec("git log --oneline -5", workDir)
172399
- };
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 };
172400
172773
  }
172401
- function refreshEnvForTurn(env4, workDir) {
172774
+ async function refreshEnvForTurn(env4, workDir) {
172402
172775
  return {
172403
172776
  ...env4,
172404
172777
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
172405
- ...collectGitEnv(workDir)
172778
+ ...await collectGitEnv(workDir)
172406
172779
  };
172407
172780
  }
172408
172781
  var noDiscovery = false;
@@ -172586,6 +172959,10 @@ var Spinner = class {
172586
172959
  verbIdx = 0;
172587
172960
  thinkTokens = 0;
172588
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 = "";
172589
172966
  start(text, opts) {
172590
172967
  this.stop();
172591
172968
  this.baseText = text;
@@ -172640,13 +173017,24 @@ var Spinner = class {
172640
173017
  return;
172641
173018
  this.baseText = text;
172642
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
+ }
172643
173031
  render() {
172644
173032
  const elapsedSec = Math.floor((Date.now() - this.startedAt) / 1e3);
172645
173033
  if (this.verbs && elapsedSec > 0 && elapsedSec % VERB_ROTATE_SEC === 0 && this.lastRotateSec !== elapsedSec) {
172646
173034
  this.lastRotateSec = elapsedSec;
172647
173035
  this.verbIdx++;
172648
173036
  }
172649
- 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);
172650
173038
  const status = composeStatusLine({
172651
173039
  frame: this.frames[this.i % this.frames.length],
172652
173040
  text: label,
@@ -172695,6 +173083,7 @@ async function initMcp(workDir, override, strict) {
172695
173083
  if (noDiscovery && !override) {
172696
173084
  _mcpConfig = { mcpServers: {} };
172697
173085
  _mcpManager = null;
173086
+ wireHookMcpCaller();
172698
173087
  return;
172699
173088
  }
172700
173089
  _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core18.McpManager.loadConfig(workDir);
@@ -172702,6 +173091,7 @@ async function initMcp(workDir, override, strict) {
172702
173091
  Object.assign(_mcpConfig.mcpServers, override.mcpServers);
172703
173092
  if (Object.keys(_mcpConfig.mcpServers).length === 0) {
172704
173093
  _mcpManager = null;
173094
+ wireHookMcpCaller();
172705
173095
  return;
172706
173096
  }
172707
173097
  try {
@@ -172711,6 +173101,18 @@ async function initMcp(workDir, override, strict) {
172711
173101
  const manager = new import_code_core18.McpManager();
172712
173102
  await manager.connectAll(_mcpConfig);
172713
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
+ });
172714
173116
  }
172715
173117
  function formatAgentsList(workDir) {
172716
173118
  const { types: types3, warnings } = (0, import_code_core18.loadAgentTypesWithWarnings)(workDir);
@@ -173086,6 +173488,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
173086
173488
  const turnStart = Date.now();
173087
173489
  userActiveThisTurn = false;
173088
173490
  const spinner = new Spinner();
173491
+ (0, import_code_core19.setHookStatusListener)((m2) => spinner.setHookStatus(m2));
173089
173492
  const mdRender = new MarkdownStreamRenderer();
173090
173493
  const toolStream = new ToolStreamPrinter(PREVIEW_LINES);
173091
173494
  let toolStartTime = 0;
@@ -173112,7 +173515,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
173112
173515
  workDir,
173113
173516
  sessionId: activeSessionIdGetter(),
173114
173517
  model: modelAlias,
173115
- env: refreshEnvForTurn(env4, workDir),
173518
+ env: await refreshEnvForTurn(env4, workDir),
173116
173519
  nexrallMd,
173117
173520
  mode,
173118
173521
  // `/mode plan` previously only hinted to the backend — the footer said "plan
@@ -173465,7 +173868,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
173465
173868
  workDir,
173466
173869
  sessionId: activeSessionIdGetter(),
173467
173870
  model: modelAlias,
173468
- env: refreshEnvForTurn(env4, workDir),
173871
+ env: await refreshEnvForTurn(env4, workDir),
173469
173872
  nexrallMd,
173470
173873
  mode,
173471
173874
  planMode: mode === "plan",
@@ -173991,7 +174394,7 @@ Resume it with: nex --worktree ${worktreeState.name}
173991
174394
  void (0, import_code_core19.fireObserverHook)(requestedWorkDir, "WorktreeRemove", { name: worktreeState.name, path: worktreeState.worktreePath }, worktreeState.name);
173992
174395
  });
173993
174396
  }
173994
- const env4 = collectEnv(workDir);
174397
+ const env4 = await collectEnv(workDir);
173995
174398
  let briefBase = options.systemPrompt?.trim() ?? readNexrallMd(workDir);
173996
174399
  const agentPrefix = options.selectedAgent?.prompt?.trim();
173997
174400
  const briefParts = [agentPrefix, briefBase, options.appendSystemPrompt?.trim(), addDirNote(options.addDirs ?? []), options.jsonSchema ? schemaInstruction(options.jsonSchema) : ""];
@@ -175114,6 +175517,7 @@ ${text}` : "");
175114
175517
  activeGoal = null;
175115
175518
  criteriaLastLine = "";
175116
175519
  cliBackgroundAgents.stopAll({ dropPending: true });
175520
+ (0, import_code_core19.resetHookOnceState)();
175117
175521
  inputBuffer = "";
175118
175522
  if (interactive)
175119
175523
  await getInkTerminal().clearTranscript();
@@ -175320,7 +175724,7 @@ ${summaryText}` }] },
175320
175724
  }
175321
175725
  const readme = tryRead(path29.join(workDir, "README.md"))?.slice(0, 2e3) ?? "";
175322
175726
  const pkgJson = tryRead(path29.join(workDir, "package.json"))?.slice(0, 1e3) ?? "";
175323
- const dirList = tryExec("ls -la", workDir)?.slice(0, 500) ?? "";
175727
+ const dirList = (await tryExecAsync("ls", ["-la"], workDir))?.slice(0, 500) ?? "";
175324
175728
  const initPrompt = `Analyze this project and write a concise nexrall.md (project-specific instructions for an AI coding assistant).
175325
175729
  Include: project overview, tech stack, coding conventions, important files/folders, what to avoid.
175326
175730
  Keep it under 400 words. Use markdown. Be specific and actionable.
@@ -177246,6 +177650,7 @@ ${text}
177246
177650
  rl.resume();
177247
177651
  rl.prompt();
177248
177652
  deliverBackgroundWhenIdle();
177653
+ deliverHookWakeWhenIdle();
177249
177654
  });
177250
177655
  async function runGoalLoop() {
177251
177656
  while (activeGoal) {
@@ -177326,6 +177731,20 @@ ${text}
177326
177731
  bgLastCount = n;
177327
177732
  deliverBackgroundWhenIdle();
177328
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);
177329
177748
  const loopTimer = setInterval(() => {
177330
177749
  if (loopScheduler.size === 0)
177331
177750
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.177",
3
+ "version": "0.5.178",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",