nexrall-code 0.5.167 → 0.5.169

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 +902 -216
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11355,7 +11355,7 @@ var require_memory = __commonJS({
11355
11355
  }();
11356
11356
  Object.defineProperty(exports2, "__esModule", { value: true });
11357
11357
  exports2.AGENT_MEMORY_MAX_BYTES = exports2.AGENT_MEMORY_INJECT_MAX = exports2.MEMORY_ARCHIVE_MAX_BYTES = exports2.MEMORY_HARD_CAP_BYTES = exports2.MEMORY_COMPACT_TRIGGER_BYTES = exports2.MEMORY_MAX_BYTES = exports2.MEMORY_ENTRY_MAX_CHARS = void 0;
11358
- exports2.memoryFilePath = memoryFilePath;
11358
+ exports2.memoryFilePath = memoryFilePath2;
11359
11359
  exports2.writeMemory = writeMemory2;
11360
11360
  exports2.readMemory = readMemory2;
11361
11361
  exports2.readAllMemory = readAllMemory2;
@@ -11374,7 +11374,7 @@ var require_memory = __commonJS({
11374
11374
  var crypto9 = __importStar(__require("crypto"));
11375
11375
  var MEMORY_ROOT = process.env.NEXRALL_MEMORY_DIR || path28.join(os17.homedir(), ".nexrall", "memory");
11376
11376
  var GLOBAL_FILE = path28.join(MEMORY_ROOT, "global.md");
11377
- function memoryFilePath(scope, workDir) {
11377
+ function memoryFilePath2(scope, workDir) {
11378
11378
  if (scope === "global" || !workDir)
11379
11379
  return GLOBAL_FILE;
11380
11380
  const key = crypto9.createHash("sha1").update(path28.resolve(workDir)).digest("hex").slice(0, 16);
@@ -11468,7 +11468,7 @@ ${SUPERSEDED_HEADER}
11468
11468
  trimmed = trimmed.slice(0, budget - 1).trimEnd() + "\u2026";
11469
11469
  }
11470
11470
  const body = prefix2 && budget > 0 ? prefix2 + trimmed : trimmed;
11471
- const file = memoryFilePath(scope, workDir);
11471
+ const file = memoryFilePath2(scope, workDir);
11472
11472
  return withLock(file, async () => {
11473
11473
  const existing = readMemoryFile(file);
11474
11474
  let { active, archived } = parseSections(existing);
@@ -11503,7 +11503,7 @@ ${SUPERSEDED_HEADER}
11503
11503
  });
11504
11504
  }
11505
11505
  function readMemory2(scope, workDir, opts = {}) {
11506
- const file = memoryFilePath(scope, workDir);
11506
+ const file = memoryFilePath2(scope, workDir);
11507
11507
  const { active, archived } = parseSections(readMemoryFile(file));
11508
11508
  if (!opts.includeArchived)
11509
11509
  return active.length ? serializeSections(active, []).trim() : "";
@@ -11524,14 +11524,14 @@ ${projMem}`);
11524
11524
  return parts.join("\n\n");
11525
11525
  }
11526
11526
  function clearMemory2(scope, workDir) {
11527
- const file = memoryFilePath(scope, workDir);
11527
+ const file = memoryFilePath2(scope, workDir);
11528
11528
  try {
11529
11529
  fs28.rmSync(file, { force: true });
11530
11530
  } catch {
11531
11531
  }
11532
11532
  }
11533
11533
  function memoryStats2(scope, workDir) {
11534
- const file = memoryFilePath(scope, workDir);
11534
+ const file = memoryFilePath2(scope, workDir);
11535
11535
  const content = readMemoryFile(file);
11536
11536
  const { active, archived } = parseSections(content);
11537
11537
  return {
@@ -11545,7 +11545,7 @@ ${projMem}`);
11545
11545
  return reply.split("\n").map((l2) => l2.trimEnd()).filter((l2) => /^-\s\[\d{4}-\d{2}-\d{2}\]\s*\S/.test(l2));
11546
11546
  }
11547
11547
  async function compactMemoryIfNeeded2(scope, workDir, summarize) {
11548
- const file = memoryFilePath(scope, workDir);
11548
+ const file = memoryFilePath2(scope, workDir);
11549
11549
  return withLock(file, async () => {
11550
11550
  const content = readMemoryFile(file);
11551
11551
  if (Buffer.byteLength(content, "utf-8") <= exports2.MEMORY_COMPACT_TRIGGER_BYTES)
@@ -106910,8 +106910,11 @@ var require_loop = __commonJS({
106910
106910
  }();
106911
106911
  Object.defineProperty(exports2, "__esModule", { value: true });
106912
106912
  exports2.VERIFY_CMD_RE = exports2.WRITE_TOOL_NAMES = exports2.ToolNotAllowedError = exports2.AGENT_MEMORY_TOOL_SCHEMA = exports2.AGENT_MEMORY_TOOL = exports2._stallLimits = exports2._emptyTurnRetry = exports2.bashNeedsRepoLock = void 0;
106913
+ exports2.loadHooks = loadHooks;
106914
+ exports2.parsePromptHookReply = parsePromptHookReply;
106915
+ exports2.classifyStopFailure = classifyStopFailure;
106913
106916
  exports2.runLifecycleHooks = runLifecycleHooks;
106914
- exports2.runPermissionRequestHooks = runPermissionRequestHooks2;
106917
+ exports2.runPermissionRequestHooks = runPermissionRequestHooks3;
106915
106918
  exports2.fireNotificationHooks = fireNotificationHooks2;
106916
106919
  exports2.fireManualPostCompactHook = fireManualPostCompactHook2;
106917
106920
  exports2.fireManualPreCompactHook = fireManualPreCompactHook2;
@@ -107027,8 +107030,42 @@ var require_loop = __commonJS({
107027
107030
  ...extra
107028
107031
  ];
107029
107032
  }
107033
+ for (const phase of ENTRY_LIST_PHASES) {
107034
+ const list = merged[phase];
107035
+ if (Array.isArray(list) && list.length) {
107036
+ merged[phase] = toHookEntries(list);
107037
+ }
107038
+ }
107030
107039
  return merged;
107031
107040
  }
107041
+ var ENTRY_LIST_PHASES = [
107042
+ "PreToolUse",
107043
+ "PostToolUse",
107044
+ "SessionStart",
107045
+ "UserPromptSubmit",
107046
+ "PreCompact",
107047
+ "PermissionRequest",
107048
+ "Notification",
107049
+ "PostToolUseFailure",
107050
+ "SubagentStart",
107051
+ "PostCompact",
107052
+ "SessionEnd",
107053
+ "FileChanged",
107054
+ "InstructionsLoaded",
107055
+ "StopFailure"
107056
+ ];
107057
+ function toHookEntries(list) {
107058
+ return list.map((it) => {
107059
+ const e2 = it;
107060
+ if (Array.isArray(e2.hooks))
107061
+ return { matcher: e2.matcher, hooks: e2.hooks };
107062
+ const { matcher, ...rest } = e2;
107063
+ const hook = { ...rest };
107064
+ if (!isKnownHandlerType(hook.type))
107065
+ hook.type = "command";
107066
+ return { matcher, hooks: [hook] };
107067
+ });
107068
+ }
107032
107069
  function spawnHook(command, opts) {
107033
107070
  return new Promise((resolve11) => {
107034
107071
  let stdout = "";
@@ -107098,8 +107135,206 @@ var require_loop = __commonJS({
107098
107135
  async function runHookHandler(hook, payload, opts) {
107099
107136
  if (hook.type === "http")
107100
107137
  return httpHook(hook, payload, opts.timeoutMs);
107138
+ if (hook.type === "prompt" || hook.type === "agent") {
107139
+ return promptHook(hook, payload, { ...opts, envCtx: opts.hookEnv });
107140
+ }
107101
107141
  return spawnHook(String(hook.command ?? ""), { cwd: opts.cwd, timeout: opts.timeoutMs, input: payload, env: opts.env });
107102
107142
  }
107143
+ var HOOK_AGENT_TOOLS = [
107144
+ {
107145
+ name: "read_file",
107146
+ description: "Read a file from the working directory.",
107147
+ input_schema: {
107148
+ type: "object",
107149
+ properties: {
107150
+ path: { type: "string", description: "File path, relative to the working directory." },
107151
+ offset: { type: "number", description: "0-based line to start from." },
107152
+ limit: { type: "number", description: "Maximum number of lines." }
107153
+ },
107154
+ required: ["path"]
107155
+ }
107156
+ },
107157
+ {
107158
+ name: "search_files",
107159
+ description: "Search file contents (regex) or find files by name in the working directory.",
107160
+ input_schema: {
107161
+ type: "object",
107162
+ properties: {
107163
+ pattern: { type: "string", description: "Regex or literal string to search for." },
107164
+ path: { type: "string", description: "Directory to search in." },
107165
+ type: { type: "string", description: "files (paths only) | content (matching lines) | filename." }
107166
+ },
107167
+ required: ["pattern"]
107168
+ }
107169
+ },
107170
+ {
107171
+ name: "list_directory",
107172
+ description: "List the files and directories at a path.",
107173
+ input_schema: { type: "object", properties: { path: { type: "string" } } }
107174
+ },
107175
+ {
107176
+ name: "glob",
107177
+ description: 'Find files matching a glob pattern, e.g. "**/*.ts".',
107178
+ input_schema: { type: "object", properties: { pattern: { type: "string" }, path: { type: "string" } }, required: ["pattern"] }
107179
+ }
107180
+ ];
107181
+ var NO_TOOLS_MARKER = "\0hook-no-tools";
107182
+ function parsePromptHookReply(reply) {
107183
+ const text = String(reply ?? "");
107184
+ const start = text.indexOf("{");
107185
+ const end = text.lastIndexOf("}");
107186
+ if (start < 0 || end <= start)
107187
+ return null;
107188
+ try {
107189
+ const j = JSON.parse(text.slice(start, end + 1));
107190
+ if (typeof j.ok !== "boolean" && typeof j.decision !== "string")
107191
+ return null;
107192
+ return {
107193
+ ok: typeof j.ok === "boolean" ? j.ok : void 0,
107194
+ reason: typeof j.reason === "string" ? j.reason : void 0,
107195
+ additionalContext: typeof j.additionalContext === "string" ? j.additionalContext : void 0,
107196
+ decision: typeof j.decision === "string" ? j.decision : void 0
107197
+ };
107198
+ } catch {
107199
+ return null;
107200
+ }
107201
+ }
107202
+ var PROMPT_HOOK_RULES = `You are a hook evaluator for a coding agent. The hook input JSON is given below, followed by the hook's own instruction. Decide whether the agent action described in the input should proceed. Reply with ONLY one JSON object and nothing else: {"ok":true} to allow, or {"ok":false,"reason":"short explanation"} to block.`;
107203
+ async function promptHook(hook, payload, opts) {
107204
+ const instruction = String(hook.prompt ?? "");
107205
+ if (!instruction)
107206
+ return { status: 1, stdout: "", stderr: 'prompt hook has no "prompt"' };
107207
+ const body = instruction.replace(/\\\$ARGUMENTS/g, "").replace(/\$ARGUMENTS/g, payload).replace(/\u0001/g, "$ARGUMENTS");
107208
+ const ctl = { aborted: false };
107209
+ const timer = setTimeout(() => {
107210
+ ctl.aborted = true;
107211
+ }, Math.max(1e3, opts.timeoutMs));
107212
+ const outerPoll = opts.abortSignal ? setInterval(() => {
107213
+ if (opts.abortSignal?.aborted)
107214
+ ctl.aborted = true;
107215
+ }, 100) : void 0;
107216
+ if (opts.abortSignal?.aborted) {
107217
+ clearTimeout(timer);
107218
+ if (outerPoll)
107219
+ clearInterval(outerPoll);
107220
+ return { status: 1, stdout: "", stderr: "aborted" };
107221
+ }
107222
+ try {
107223
+ return await runModel();
107224
+ } finally {
107225
+ clearTimeout(timer);
107226
+ if (outerPoll)
107227
+ clearInterval(outerPoll);
107228
+ }
107229
+ async function runModel() {
107230
+ const messages = [{
107231
+ role: "user",
107232
+ content: [{ type: "text", text: `${PROMPT_HOOK_RULES}
107233
+
107234
+ Hook input:
107235
+ ${payload}
107236
+
107237
+ Hook instruction:
107238
+ ${body}` }]
107239
+ }];
107240
+ const isAgent = hook.type === "agent";
107241
+ try {
107242
+ let replyText = "";
107243
+ for (let round = 0; round < (isAgent ? 6 : 1); round++) {
107244
+ const msg = await (0, client_1.streamChat)(messages, {
107245
+ model: hook.model ?? opts.model ?? "turbo",
107246
+ env: opts.envCtx ?? { cwd: opts.cwd },
107247
+ abortSignal: ctl,
107248
+ // Wire the tools by allowlist rather than a separate channel: the backend
107249
+ // filters builtins+extra against it, so an agent hook sees ONLY the four
107250
+ // read-only tools and a prompt hook sees none (the marker matches nothing).
107251
+ toolAllowlist: isAgent ? HOOK_AGENT_TOOLS.map((t2) => t2.name) : [NO_TOOLS_MARKER],
107252
+ extraTools: isAgent ? HOOK_AGENT_TOOLS : void 0
107253
+ }, () => {
107254
+ });
107255
+ const blocks = Array.isArray(msg.content) ? msg.content : [];
107256
+ const toolUses = blocks.filter((b) => b.type === "tool_use");
107257
+ if (!toolUses.length || !isAgent) {
107258
+ replyText = blocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
107259
+ break;
107260
+ }
107261
+ messages.push({ role: "assistant", content: msg.content });
107262
+ const results = [];
107263
+ for (const tu of toolUses) {
107264
+ let out;
107265
+ try {
107266
+ out = getHookToolExecutors().includes(String(tu.name)) ? await (0, executor_1.executeTool)(String(tu.name), tu.input ?? {}, ctl, void 0, opts.cwd) : { error: `Tool ${String(tu.name)} is not available to a hook \u2014 only ${getHookToolExecutors().join(", ")}.` };
107267
+ } catch (err) {
107268
+ out = { error: err.message || "tool failed" };
107269
+ }
107270
+ results.push({
107271
+ type: "tool_result",
107272
+ tool_use_id: String(tu.id),
107273
+ content: out.error ? out.output ? `Error: ${out.error}
107274
+
107275
+ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107276
+ is_error: out.error !== void 0
107277
+ });
107278
+ }
107279
+ messages.push({ role: "user", content: results });
107280
+ }
107281
+ const parsed = parsePromptHookReply(replyText);
107282
+ if (!parsed) {
107283
+ return { status: 1, stdout: "", stderr: "prompt hook returned no decision" };
107284
+ }
107285
+ const decided = parsed.ok === false || parsed.decision === "block" || parsed.decision === "deny";
107286
+ if (decided)
107287
+ return { status: 2, stdout: "", stderr: parsed.reason || `Blocked by ${hook.type} hook` };
107288
+ const passthrough2 = parsed.additionalContext ? JSON.stringify({ additionalContext: parsed.additionalContext }) : "";
107289
+ return { status: 0, stdout: passthrough2, stderr: "" };
107290
+ } catch (err) {
107291
+ if (ctl.aborted)
107292
+ return { status: 1, stdout: "", stderr: "prompt hook timed out" };
107293
+ return { status: 1, stdout: "", stderr: `prompt hook failed: ${err.message}` };
107294
+ }
107295
+ }
107296
+ }
107297
+ function getHookToolExecutors() {
107298
+ return ["read_file", "search_files", "list_directory", "glob"];
107299
+ }
107300
+ function hookRunOptsFor(options) {
107301
+ return { model: options.model ?? "turbo", abortSignal: options.abortSignal, hookEnv: options.env };
107302
+ }
107303
+ function classifyStopFailure(err) {
107304
+ const status = err?.status;
107305
+ const code = err?.code;
107306
+ const details = [status ? `HTTP ${status}` : "", code ? String(code) : ""].filter(Boolean).join(" ") || void 0;
107307
+ if (status === 429)
107308
+ return { error: "rate_limit", error_details: details };
107309
+ if (status === 529)
107310
+ return { error: "overloaded", error_details: details };
107311
+ if (status === 401)
107312
+ return { error: "authentication_failed", error_details: details };
107313
+ if (status === 402)
107314
+ return { error: "billing_error", error_details: details };
107315
+ if (status === 403)
107316
+ return { error: "authentication_failed", error_details: details };
107317
+ if (status === 400 || status === 422)
107318
+ return { error: "invalid_request", error_details: details };
107319
+ if (status === 404)
107320
+ return { error: "model_not_found", error_details: details };
107321
+ if (typeof status === "number" && status >= 500)
107322
+ return { error: "server_error", error_details: details };
107323
+ return { error: "unknown", error_details: details };
107324
+ }
107325
+ function apiFailureForStopReason(stopReason) {
107326
+ if (stopReason === "output-limit")
107327
+ return { error: "max_output_tokens" };
107328
+ if (stopReason === "no-balance")
107329
+ return { error: "billing_error", error_details: "wallet empty (402)" };
107330
+ if (stopReason === "no-team-budget")
107331
+ return { error: "billing_error", error_details: "team pool empty (402)" };
107332
+ if (stopReason === "reported-elsewhere")
107333
+ return { error: "billing_error", error_details: "pre-flight 402" };
107334
+ if (stopReason === "team-unavailable")
107335
+ return { error: "account_on_hold", error_details: "team billing unusable (403)" };
107336
+ return null;
107337
+ }
107103
107338
  function hookTimeoutMs(hook, fallbackMs = 6e4) {
107104
107339
  if (typeof hook.timeout_ms === "number" && hook.timeout_ms > 0)
107105
107340
  return Math.min(hook.timeout_ms, 6e5);
@@ -107115,7 +107350,7 @@ var require_loop = __commonJS({
107115
107350
  return norm === toolName || norm === alt && toolName.includes(alt);
107116
107351
  });
107117
107352
  }
107118
- async function runToolHooks(entries, phase, toolName, input, workDir, result) {
107353
+ async function runToolHooks(entries, phase, toolName, input, workDir, result, hookOpts) {
107119
107354
  const outcome = { block: false };
107120
107355
  if (!entries?.length)
107121
107356
  return outcome;
@@ -107129,11 +107364,7 @@ var require_loop = __commonJS({
107129
107364
  if (!hookMatches(entry.matcher, toolName))
107130
107365
  continue;
107131
107366
  for (const hook of entry.hooks ?? []) {
107132
- if (hook.type !== "command" && hook.type !== "http")
107133
- continue;
107134
- if (hook.type === "command" && !hook.command)
107135
- continue;
107136
- if (hook.type === "http" && !hook.url)
107367
+ if (!isRunnableHook(hook))
107137
107368
  continue;
107138
107369
  const hookTimeout = hookTimeoutMs(hook, 6e4);
107139
107370
  const r2 = await runHookHandler(hook, payload, {
@@ -107144,7 +107375,8 @@ var require_loop = __commonJS({
107144
107375
  NEXRALL_TOOL_NAME: toolName,
107145
107376
  NEXRALL_TOOL_INPUT: JSON.stringify(input),
107146
107377
  NEXRALL_HOOK_PHASE: phase
107147
- }
107378
+ },
107379
+ ...hookOpts
107148
107380
  });
107149
107381
  const out = (r2.stdout ?? "").toString().trim();
107150
107382
  if (out.startsWith("{")) {
@@ -107169,17 +107401,13 @@ var require_loop = __commonJS({
107169
107401
  }
107170
107402
  return outcome;
107171
107403
  }
107172
- async function runSimpleHooks(defs, workDir, extraEnv) {
107404
+ async function runSimpleHooks(defs, workDir, extraEnv, hookOpts) {
107173
107405
  if (!defs?.length)
107174
107406
  return;
107175
107407
  for (const hook of defs) {
107176
- if (hook.type !== "command" && hook.type !== "http")
107408
+ if (!isRunnableHook(hook))
107177
107409
  continue;
107178
- if (hook.type === "command" && !hook.command)
107179
- continue;
107180
- if (hook.type === "http" && !hook.url)
107181
- continue;
107182
- await runHookHandler(hook, "", { cwd: workDir, timeoutMs: hookTimeoutMs(hook, 1e4), env: extraEnv ? { ...process.env, ...extraEnv } : void 0 });
107410
+ await runHookHandler(hook, "", { cwd: workDir, timeoutMs: hookTimeoutMs(hook, 1e4), env: extraEnv ? { ...process.env, ...extraEnv } : void 0, ...hookOpts });
107183
107411
  }
107184
107412
  }
107185
107413
  function flattenHookDefs(items, matchValue) {
@@ -107190,26 +107418,34 @@ var require_loop = __commonJS({
107190
107418
  if (m2 && m2 !== "*" && matchValue !== void 0 && !m2.split("|").map((x2) => x2.trim()).includes(matchValue))
107191
107419
  continue;
107192
107420
  out.push(...it.hooks);
107193
- } else if (it && (it.type === "command" || it.type === "http"))
107421
+ } else if (it && isKnownHandlerType(it.type))
107194
107422
  out.push(it);
107195
107423
  }
107196
107424
  return out;
107197
107425
  }
107198
- async function runLifecycleHooks(items, event, workDir, payload, matchValue) {
107426
+ function isKnownHandlerType(t2) {
107427
+ return t2 === "command" || t2 === "http" || t2 === "prompt" || t2 === "agent";
107428
+ }
107429
+ function isRunnableHook(hook) {
107430
+ if (!isKnownHandlerType(hook.type))
107431
+ return false;
107432
+ if (hook.type === "command")
107433
+ return !!hook.command;
107434
+ if (hook.type === "http")
107435
+ return !!hook.url;
107436
+ return !!hook.prompt;
107437
+ }
107438
+ async function runLifecycleHooks(items, event, workDir, payload, matchValue, hookOpts) {
107199
107439
  const outcome = { block: false };
107200
107440
  const defs = flattenHookDefs(items, matchValue);
107201
107441
  if (!defs.length)
107202
107442
  return outcome;
107203
107443
  const input = JSON.stringify({ hook_event_name: event, cwd: workDir, ...payload });
107204
107444
  for (const hook of defs) {
107205
- if (hook.type !== "command" && hook.type !== "http")
107206
- continue;
107207
- if (hook.type === "command" && !hook.command)
107208
- continue;
107209
- if (hook.type === "http" && !hook.url)
107445
+ if (!isRunnableHook(hook))
107210
107446
  continue;
107211
- const timeout = hookTimeoutMs(hook, 3e4);
107212
- const r2 = await runHookHandler(hook, input, { cwd: workDir, timeoutMs: timeout, env: { ...process.env, NEXRALL_HOOK_PHASE: event } });
107447
+ const timeout = hookTimeoutMs(hook, hook.type === "agent" ? 6e4 : hook.type === "prompt" ? 3e4 : 3e4);
107448
+ const r2 = await runHookHandler(hook, input, { cwd: workDir, timeoutMs: timeout, env: { ...process.env, NEXRALL_HOOK_PHASE: event }, ...hookOpts });
107213
107449
  const out = (r2.stdout ?? "").toString().trim();
107214
107450
  if (out.startsWith("{")) {
107215
107451
  try {
@@ -107229,6 +107465,10 @@ var require_loop = __commonJS({
107229
107465
  }
107230
107466
  if (typeof j.additionalContext === "string" && j.additionalContext)
107231
107467
  outcome.context = (outcome.context ? outcome.context + "\n" : "") + j.additionalContext;
107468
+ if (event === "FileChanged" && Array.isArray(j.watchPaths))
107469
+ outcome.watchPaths = j.watchPaths.filter((p) => typeof p === "string");
107470
+ if (typeof j.systemMessage === "string" && j.systemMessage)
107471
+ outcome.systemMessage = j.systemMessage;
107232
107472
  } catch {
107233
107473
  }
107234
107474
  } else if (out && (event === "SessionStart" || event === "UserPromptSubmit") && r2.status === 0) {
@@ -107241,7 +107481,7 @@ var require_loop = __commonJS({
107241
107481
  }
107242
107482
  return outcome;
107243
107483
  }
107244
- async function runPermissionRequestHooks2(workDir, tool, input, sessionId = "") {
107484
+ async function runPermissionRequestHooks3(workDir, tool, input, sessionId = "") {
107245
107485
  try {
107246
107486
  const items = loadHooks(workDir).PermissionRequest;
107247
107487
  if (!items?.length)
@@ -108089,7 +108329,7 @@ ${stats}${isoNote}`;
108089
108329
  const subAgentName = agent?.name ?? (typeof input.subagent_type === "string" && input.subagent_type ? input.subagent_type : "general-purpose");
108090
108330
  const startItems = loadHooks(options.workDir).SubagentStart;
108091
108331
  if (startItems?.length) {
108092
- const o = await runLifecycleHooks(startItems, "SubagentStart", options.workDir, { session_id: options.sessionId ?? "", agent_type: subAgentName, description: String(input.description ?? "") }, subAgentName).catch(() => ({ block: false }));
108332
+ const o = await runLifecycleHooks(startItems, "SubagentStart", options.workDir, { session_id: options.sessionId ?? "", agent_type: subAgentName, description: String(input.description ?? "") }, subAgentName, hookRunOptsFor(options)).catch(() => ({ block: false }));
108093
108333
  if (o.context) {
108094
108334
  const last = subMessages[subMessages.length - 1];
108095
108335
  if (last && Array.isArray(last.content)) {
@@ -108649,7 +108889,7 @@ Do NOT call any tools and do NOT continue the task. Reply with text only: a conc
108649
108889
  if (cut < 2)
108650
108890
  return false;
108651
108891
  if ((options._depth ?? 0) === 0) {
108652
- await runLifecycleHooks(loadHooks(options.workDir).PreCompact, "PreCompact", options.workDir, { trigger: "auto", session_id: options.sessionId ?? "", messages_to_summarize: cut }, "auto").catch(() => void 0);
108892
+ await runLifecycleHooks(loadHooks(options.workDir).PreCompact, "PreCompact", options.workDir, { trigger: "auto", session_id: options.sessionId ?? "", messages_to_summarize: cut }, "auto", hookRunOptsFor(options)).catch(() => void 0);
108653
108893
  }
108654
108894
  const beforeCompact = messages.length;
108655
108895
  const afterCompact = cut > 0 ? messages.length - cut + 1 : messages.length;
@@ -108662,7 +108902,7 @@ Do NOT call any tools and do NOT continue the task. Reply with text only: a conc
108662
108902
  options.onCompact?.({ trigger: "auto", before: beforeCompact, after: afterCompact });
108663
108903
  } catch {
108664
108904
  }
108665
- await runLifecycleHooks(loadHooks(options.workDir).PostCompact, "PostCompact", options.workDir, { trigger: "auto", session_id: options.sessionId ?? "", messages_summarized: cut }, "auto").catch(() => void 0);
108905
+ await runLifecycleHooks(loadHooks(options.workDir).PostCompact, "PostCompact", options.workDir, { trigger: "auto", session_id: options.sessionId ?? "", messages_summarized: cut }, "auto", hookRunOptsFor(options)).catch(() => void 0);
108666
108906
  };
108667
108907
  const originalTask = originalTaskText(toSummarize);
108668
108908
  const summaryPrompt = `Summarize this coding-session transcript into concise bullet points the assistant needs to continue the work: key decisions, files changed (and how), commands run, unresolved problems, and user preferences. Max 400 words.
@@ -108910,6 +109150,26 @@ Continue the work from here.` }] });
108910
109150
  const messages = [...initialMessages];
108911
109151
  const model = options.model ?? "turbo";
108912
109152
  const hooks = options.disableHooks ? withAgentHooks({}, options._agentHooks) : withAgentHooks(loadHooks(options.workDir), options._agentHooks);
109153
+ const fireStopFailure = async (failure) => {
109154
+ if (depth !== 0)
109155
+ return;
109156
+ const items = hooks.StopFailure;
109157
+ if (!items?.length)
109158
+ return;
109159
+ let last = "";
109160
+ for (let i2 = messages.length - 1; i2 >= 0 && !last; i2--) {
109161
+ const m2 = messages[i2];
109162
+ if (m2?.role === "assistant" && Array.isArray(m2.content)) {
109163
+ last = m2.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n").trim().slice(0, 2e3);
109164
+ }
109165
+ }
109166
+ await runLifecycleHooks(items, "StopFailure", options.workDir, {
109167
+ session_id: options.sessionId ?? "",
109168
+ error: failure.error,
109169
+ ...failure.error_details ? { error_details: failure.error_details } : {},
109170
+ last_assistant_message: last
109171
+ }, failure.error, hookRunOptsFor(options)).catch(() => void 0);
109172
+ };
108913
109173
  const depth = options._depth ?? 0;
108914
109174
  const agentScope = options._agentScope ?? "root";
108915
109175
  const isMainAgent = depth === 0;
@@ -108930,13 +109190,13 @@ ${ctx}
108930
109190
  const sid = options.sessionId || PROCESS_BUDGET_KEY;
108931
109191
  if (isPrompt && !_sessionStarted.has(sid) && (hooks.SessionStart?.length ?? 0) > 0) {
108932
109192
  _sessionStarted.add(sid);
108933
- const o = await runLifecycleHooks(hooks.SessionStart, "SessionStart", options.workDir, { session_id: options.sessionId ?? "", source: messages.length > 1 ? "resume" : "startup" }, messages.length > 1 ? "resume" : "startup");
109193
+ const o = await runLifecycleHooks(hooks.SessionStart, "SessionStart", options.workDir, { session_id: options.sessionId ?? "", source: messages.length > 1 ? "resume" : "startup" }, messages.length > 1 ? "resume" : "startup", hookRunOptsFor(options));
108934
109194
  if (o.context)
108935
109195
  addContext(o.context);
108936
109196
  } else if (isPrompt)
108937
109197
  _sessionStarted.add(sid);
108938
109198
  if (isPrompt && (hooks.UserPromptSubmit?.length ?? 0) > 0) {
108939
- const o = await runLifecycleHooks(hooks.UserPromptSubmit, "UserPromptSubmit", options.workDir, { session_id: options.sessionId ?? "", prompt: promptText });
109199
+ const o = await runLifecycleHooks(hooks.UserPromptSubmit, "UserPromptSubmit", options.workDir, { session_id: options.sessionId ?? "", prompt: promptText }, void 0, hookRunOptsFor(options));
108940
109200
  if (o.block) {
108941
109201
  options.onText(`
108942
109202
  \u26D4 Prompt blocked by a UserPromptSubmit hook: ${o.reason ?? "no reason given"}
@@ -109204,7 +109464,8 @@ ${ctx}
109204
109464
  stopReason = "team-unavailable";
109205
109465
  break;
109206
109466
  }
109207
- await runSimpleHooks(hooks.OnError, options.workDir);
109467
+ await runSimpleHooks(hooks.OnError, options.workDir, void 0, hookRunOptsFor(options));
109468
+ await fireStopFailure(classifyStopFailure(err));
109208
109469
  throw new types_1.AgentTurnError(`Stream failed: ${err.message}`, trimToResumableBoundary(messages), completedRounds, err);
109209
109470
  }
109210
109471
  if (options.abortSignal?.aborted) {
@@ -109245,7 +109506,7 @@ ${ctx}
109245
109506
  continue;
109246
109507
  }
109247
109508
  if (depth === 0)
109248
- await runSimpleHooks(hooks.PostMessageComplete, options.workDir);
109509
+ await runSimpleHooks(hooks.PostMessageComplete, options.workDir, void 0, hookRunOptsFor(options));
109249
109510
  stopReason = assistantMessage.stopReason === "max_tokens" ? "output-limit" : "empty-response";
109250
109511
  break;
109251
109512
  }
@@ -109329,13 +109590,14 @@ ${ctx}
109329
109590
  }
109330
109591
  }
109331
109592
  if (depth === 0)
109332
- await runSimpleHooks(hooks.PostMessageComplete, options.workDir);
109593
+ await runSimpleHooks(hooks.PostMessageComplete, options.workDir, void 0, hookRunOptsFor(options));
109333
109594
  stopReason = "clean";
109334
109595
  break;
109335
109596
  }
109336
109597
  const prefetchSafe = prefetchOn && (0, toolPrefetch_1.isPrefetchSafeMessage)(toolUseBlocks.map((b) => b.name));
109337
109598
  const toolResults = await Promise.all(toolUseBlocks.map(async (block) => {
109338
- const { id, name, input } = block;
109599
+ const { id, name } = block;
109600
+ let input = block.input;
109339
109601
  const toolMeta = { id };
109340
109602
  options.onToolUse(name, input, void 0, toolMeta);
109341
109603
  let result;
@@ -109366,7 +109628,31 @@ ${ctx}
109366
109628
  let permitted;
109367
109629
  let deniedReason = null;
109368
109630
  try {
109369
- permitted = await options.requestPermission({ tool: name, input, description });
109631
+ const pd = await options.requestPermission({ tool: name, input, description });
109632
+ if (typeof pd === "boolean") {
109633
+ permitted = pd;
109634
+ } else {
109635
+ permitted = pd.granted;
109636
+ if (permitted && pd.updatedInput && typeof pd.updatedInput === "object") {
109637
+ input = pd.updatedInput;
109638
+ if (options.planMode) {
109639
+ const r2 = (0, planMode_1.checkPlanMode)(name, input);
109640
+ if (r2) {
109641
+ permitted = false;
109642
+ deniedReason = r2.message;
109643
+ }
109644
+ }
109645
+ if (permitted && options.worktree) {
109646
+ const r2 = (0, worktreeEnforcement_1.checkWorktreeIsolation)(name, input, options.worktree, options.workDir);
109647
+ if (r2) {
109648
+ permitted = false;
109649
+ deniedReason = r2.message;
109650
+ }
109651
+ }
109652
+ }
109653
+ if (!permitted && pd.message)
109654
+ deniedReason = deniedReason ?? pd.message;
109655
+ }
109370
109656
  } catch (err) {
109371
109657
  permitted = false;
109372
109658
  if (err instanceof ToolNotAllowedError)
@@ -109377,12 +109663,12 @@ ${ctx}
109377
109663
  } else if (name === "task") {
109378
109664
  const wantsBackground = input.run_in_background === true || input.run_in_background !== false && !!(0, agentTypes_1.findAgentType)(agentTypes, String(input.subagent_type ?? ""))?.background;
109379
109665
  const taskOptions = { ...auditOptions, _taskToolUseId: id };
109380
- const preTask = await runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir);
109666
+ const preTask = await runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir, void 0, hookRunOptsFor(options));
109381
109667
  if (preTask.block) {
109382
109668
  result = { error: `Blocked by PreToolUse hook: ${preTask.reason}` };
109383
109669
  } else {
109384
109670
  result = wantsBackground && depth === 0 && options.backgroundAgents && !input.resume_agent_id ? dispatchBackgroundSubAgent(input, taskOptions, agentTypes, options.backgroundAgents) : await dispatchSubAgent(input, taskOptions, agentTypes);
109385
- const postTask = await runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result);
109671
+ const postTask = await runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result, hookRunOptsFor(options));
109386
109672
  const injectedTask = [preTask.context, postTask.context].filter(Boolean).join("\n");
109387
109673
  if (injectedTask) {
109388
109674
  if (result.error !== void 0)
@@ -109399,7 +109685,7 @@ ${ctx}
109399
109685
  }
109400
109686
  }
109401
109687
  } else {
109402
- const pre = await runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir);
109688
+ const pre = await runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir, void 0, hookRunOptsFor(options));
109403
109689
  if (pre.block) {
109404
109690
  result = { error: `Blocked by PreToolUse hook: ${pre.reason}` };
109405
109691
  } else {
@@ -109445,9 +109731,9 @@ ${ctx}
109445
109731
  const scope = input.scope === "global" ? "global" : "project";
109446
109732
  void maybeCompactMemory(scope, options);
109447
109733
  }
109448
- const post = await runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result);
109734
+ const post = await runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result, hookRunOptsFor(options));
109449
109735
  if (result.error !== void 0 && (hooks.PostToolUseFailure?.length ?? 0) > 0) {
109450
- const f3 = await runLifecycleHooks(hooks.PostToolUseFailure, "PostToolUseFailure", options.workDir, { session_id: options.sessionId ?? "", tool_name: name, tool_input: input, error: String(result.error).slice(0, 4e3) }, name).catch(() => ({ block: false }));
109736
+ const f3 = await runLifecycleHooks(hooks.PostToolUseFailure, "PostToolUseFailure", options.workDir, { session_id: options.sessionId ?? "", tool_name: name, tool_input: input, error: String(result.error).slice(0, 4e3) }, name, hookRunOptsFor(options)).catch(() => ({ block: false }));
109451
109737
  if (f3.context)
109452
109738
  post.context = [post.context, f3.context].filter(Boolean).join("\n");
109453
109739
  if (f3.block) {
@@ -109569,6 +109855,9 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
109569
109855
  stopReason = "budget";
109570
109856
  if (options.abortSignal?.aborted)
109571
109857
  stopReason = "aborted";
109858
+ const apiFailure = apiFailureForStopReason(stopReason);
109859
+ if (apiFailure)
109860
+ await fireStopFailure(apiFailure);
109572
109861
  const notice = stopReasonNotice(stopReason, { budget, repeatError: stalledRepeatError });
109573
109862
  if (options._onStopReason)
109574
109863
  options._onStopReason(stopReason, notice || null);
@@ -109583,12 +109872,12 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
109583
109872
  throw new types_1.AgentTurnError(err?.message || "The turn ended unexpectedly", trimToResumableBoundary(messages), completedRounds, err);
109584
109873
  } finally {
109585
109874
  if (depth === 0 && (hooks.SessionEnd?.length ?? 0) > 0) {
109586
- await runLifecycleHooks(hooks.SessionEnd, "SessionEnd", options.workDir, { session_id: options.sessionId ?? "", reason: stopReason }, stopReason).catch(() => void 0);
109875
+ await runLifecycleHooks(hooks.SessionEnd, "SessionEnd", options.workDir, { session_id: options.sessionId ?? "", reason: stopReason }, stopReason, hookRunOptsFor(options)).catch(() => void 0);
109587
109876
  }
109588
109877
  if (depth === 0)
109589
- await runSimpleHooks(hooks.OnStop, options.workDir);
109878
+ await runSimpleHooks(hooks.OnStop, options.workDir, void 0, hookRunOptsFor(options));
109590
109879
  else
109591
- await runSimpleHooks(hooks.SubagentStop, options.workDir, { NEXRALL_AGENT_NAME: options._agentTypeName ?? "general-purpose" });
109880
+ await runSimpleHooks(hooks.SubagentStop, options.workDir, { NEXRALL_AGENT_NAME: options._agentTypeName ?? "general-purpose" }, hookRunOptsFor(options));
109592
109881
  }
109593
109882
  return messages;
109594
109883
  }
@@ -109610,6 +109899,251 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
109610
109899
  }
109611
109900
  });
109612
109901
 
109902
+ // ../core/dist/agent/lifecycleHost.js
109903
+ var require_lifecycleHost = __commonJS({
109904
+ "../core/dist/agent/lifecycleHost.js"(exports2) {
109905
+ "use strict";
109906
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
109907
+ if (k2 === void 0)
109908
+ k2 = k;
109909
+ var desc = Object.getOwnPropertyDescriptor(m2, k);
109910
+ if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
109911
+ desc = { enumerable: true, get: function() {
109912
+ return m2[k];
109913
+ } };
109914
+ }
109915
+ Object.defineProperty(o, k2, desc);
109916
+ } : function(o, m2, k, k2) {
109917
+ if (k2 === void 0)
109918
+ k2 = k;
109919
+ o[k2] = m2[k];
109920
+ });
109921
+ var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
109922
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
109923
+ } : function(o, v) {
109924
+ o["default"] = v;
109925
+ });
109926
+ var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
109927
+ var ownKeys = function(o) {
109928
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
109929
+ var ar = [];
109930
+ for (var k in o2)
109931
+ if (Object.prototype.hasOwnProperty.call(o2, k))
109932
+ ar[ar.length] = k;
109933
+ return ar;
109934
+ };
109935
+ return ownKeys(o);
109936
+ };
109937
+ return function(mod) {
109938
+ if (mod && mod.__esModule)
109939
+ return mod;
109940
+ var result = {};
109941
+ if (mod != null) {
109942
+ for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
109943
+ if (k[i2] !== "default")
109944
+ __createBinding(result, mod, k[i2]);
109945
+ }
109946
+ __setModuleDefault(result, mod);
109947
+ return result;
109948
+ };
109949
+ }();
109950
+ Object.defineProperty(exports2, "__esModule", { value: true });
109951
+ exports2.fireInstructionsLoadedHooks = fireInstructionsLoadedHooks2;
109952
+ exports2.startFileChangedWatcher = startFileChangedWatcher2;
109953
+ exports2.filesChangedSinceSnapshot = filesChangedSinceSnapshot;
109954
+ var fs28 = __importStar(__require("fs"));
109955
+ var path28 = __importStar(__require("path"));
109956
+ var loop_1 = require_loop();
109957
+ async function fireInstructionsLoadedHooks2(workDir, sessionId, files) {
109958
+ if (!files.length)
109959
+ return;
109960
+ try {
109961
+ const items = (0, loop_1.loadHooks)(workDir).InstructionsLoaded;
109962
+ if (!items?.length)
109963
+ return;
109964
+ for (const f3 of files) {
109965
+ await (0, loop_1.runLifecycleHooks)(items, "InstructionsLoaded", workDir, {
109966
+ session_id: sessionId,
109967
+ file_path: f3.file_path,
109968
+ memory_type: f3.memory_type,
109969
+ load_reason: f3.load_reason,
109970
+ ...f3.globs ? { globs: f3.globs } : {}
109971
+ }, f3.load_reason).catch(() => void 0);
109972
+ }
109973
+ } catch {
109974
+ }
109975
+ }
109976
+ function isWatchableName(name) {
109977
+ return !!name && !name.includes("/") && !name.includes("\\") && !name.includes("*") && !name.includes("?") && name !== "." && name !== "..";
109978
+ }
109979
+ function startFileChangedWatcher2(workDir, sessionId, opts) {
109980
+ let items;
109981
+ try {
109982
+ items = (0, loop_1.loadHooks)(workDir).FileChanged;
109983
+ } catch {
109984
+ items = void 0;
109985
+ }
109986
+ if (!items?.length)
109987
+ return null;
109988
+ const staticNames = /* @__PURE__ */ new Set();
109989
+ for (const it of items) {
109990
+ if (it && Array.isArray(it.hooks)) {
109991
+ const m2 = it.matcher;
109992
+ if (!m2 || m2 === "*")
109993
+ continue;
109994
+ for (const raw of m2.split("|")) {
109995
+ const name = raw.trim();
109996
+ if (isWatchableName(name))
109997
+ staticNames.add(name);
109998
+ }
109999
+ }
110000
+ }
110001
+ if (!staticNames.size)
110002
+ return null;
110003
+ const dynamicNames = /* @__PURE__ */ new Set();
110004
+ const watched = () => /* @__PURE__ */ new Set([...staticNames, ...dynamicNames]);
110005
+ const known = /* @__PURE__ */ new Set();
110006
+ const seedKnown = (names) => {
110007
+ for (const n of names) {
110008
+ try {
110009
+ if (fs28.existsSync(path28.join(workDir, n)))
110010
+ known.add(n);
110011
+ } catch {
110012
+ }
110013
+ }
110014
+ };
110015
+ seedKnown(staticNames);
110016
+ const stamp = (name) => {
110017
+ try {
110018
+ const st = fs28.statSync(path28.join(workDir, name));
110019
+ return `${st.mtimeMs}:${st.size}`;
110020
+ } catch {
110021
+ return "";
110022
+ }
110023
+ };
110024
+ const seen = /* @__PURE__ */ new Map();
110025
+ const snapshot = (names) => {
110026
+ for (const n of names)
110027
+ seen.set(n, stamp(n));
110028
+ };
110029
+ snapshot(staticNames);
110030
+ const pending = /* @__PURE__ */ new Map();
110031
+ let closed = false;
110032
+ const scheduleFire = (name) => {
110033
+ const prev = pending.get(name);
110034
+ if (prev)
110035
+ clearTimeout(prev);
110036
+ pending.set(name, setTimeout(() => {
110037
+ pending.delete(name);
110038
+ let exists = true;
110039
+ try {
110040
+ exists = fs28.existsSync(path28.join(workDir, name));
110041
+ } catch {
110042
+ exists = false;
110043
+ }
110044
+ const event = !exists ? "unlink" : known.has(name) ? "change" : "add";
110045
+ if (exists)
110046
+ known.add(name);
110047
+ else
110048
+ known.delete(name);
110049
+ seen.set(name, stamp(name));
110050
+ void fireChanged(workDir, sessionId, name, event, items, dynamicNames.has(name)).then((o) => {
110051
+ if (!o)
110052
+ return;
110053
+ if (o.systemMessage && opts?.onSystemMessage) {
110054
+ try {
110055
+ opts.onSystemMessage(o.systemMessage);
110056
+ } catch {
110057
+ }
110058
+ }
110059
+ if (o.watchPaths) {
110060
+ dynamicNames.clear();
110061
+ for (const p of o.watchPaths) {
110062
+ const n = isWatchableName(path28.basename(p)) ? path28.basename(p) : "";
110063
+ if (n)
110064
+ dynamicNames.add(n);
110065
+ }
110066
+ seedKnown(dynamicNames);
110067
+ snapshot(dynamicNames);
110068
+ }
110069
+ }).catch(() => {
110070
+ });
110071
+ }, 250));
110072
+ };
110073
+ let watcher = null;
110074
+ try {
110075
+ watcher = fs28.watch(workDir, { persistent: false }, (_eventType, filename) => {
110076
+ if (closed)
110077
+ return;
110078
+ const name = filename ? path28.basename(String(filename)) : "";
110079
+ if (!name || !watched().has(name))
110080
+ return;
110081
+ scheduleFire(name);
110082
+ });
110083
+ watcher.on("error", () => {
110084
+ });
110085
+ } catch {
110086
+ return null;
110087
+ }
110088
+ const armGraceMs = opts?.armGraceMs ?? 400;
110089
+ const reconcileTimer = armGraceMs > 0 ? setTimeout(() => {
110090
+ if (closed)
110091
+ return;
110092
+ for (const n of filesChangedSinceSnapshot(watched(), seen, stamp))
110093
+ scheduleFire(n);
110094
+ }, armGraceMs) : null;
110095
+ return {
110096
+ stop() {
110097
+ closed = true;
110098
+ if (reconcileTimer)
110099
+ clearTimeout(reconcileTimer);
110100
+ for (const t2 of pending.values())
110101
+ clearTimeout(t2);
110102
+ pending.clear();
110103
+ try {
110104
+ watcher?.close();
110105
+ } catch {
110106
+ }
110107
+ watcher = null;
110108
+ },
110109
+ updateWatchPaths(paths) {
110110
+ dynamicNames.clear();
110111
+ for (const p of paths) {
110112
+ const n = isWatchableName(path28.basename(p)) ? path28.basename(p) : "";
110113
+ if (n)
110114
+ dynamicNames.add(n);
110115
+ }
110116
+ seedKnown(dynamicNames);
110117
+ snapshot(dynamicNames);
110118
+ },
110119
+ watchedFiles() {
110120
+ return [...watched()].sort();
110121
+ }
110122
+ };
110123
+ }
110124
+ function filesChangedSinceSnapshot(names, seen, stamp) {
110125
+ const out = [];
110126
+ for (const n of names)
110127
+ if (stamp(n) !== (seen.get(n) ?? ""))
110128
+ out.push(n);
110129
+ return out;
110130
+ }
110131
+ async function fireChanged(workDir, sessionId, name, event, items, isDynamic) {
110132
+ const abs = path28.join(workDir, name);
110133
+ try {
110134
+ const o = await (0, loop_1.runLifecycleHooks)(items, "FileChanged", workDir, {
110135
+ session_id: sessionId,
110136
+ file_path: abs,
110137
+ event
110138
+ }, isDynamic ? void 0 : name);
110139
+ return { watchPaths: o.watchPaths, systemMessage: o.systemMessage };
110140
+ } catch {
110141
+ return null;
110142
+ }
110143
+ }
110144
+ }
110145
+ });
110146
+
109613
110147
  // ../core/dist/mcp/client.js
109614
110148
  var require_client2 = __commonJS({
109615
110149
  "../core/dist/mcp/client.js"(exports2) {
@@ -111826,6 +112360,7 @@ var require_dist3 = __commonJS({
111826
112360
  __exportStar(require_client(), exports2);
111827
112361
  __exportStar(require_executor(), exports2);
111828
112362
  __exportStar(require_loop(), exports2);
112363
+ __exportStar(require_lifecycleHost(), exports2);
111829
112364
  __exportStar(require_modelCatalogue(), exports2);
111830
112365
  __exportStar(require_testIntegrity(), exports2);
111831
112366
  __exportStar(require_editCompleteness(), exports2);
@@ -145693,7 +146228,7 @@ var {
145693
146228
  } = import_index.default;
145694
146229
 
145695
146230
  // src/index.ts
145696
- var import_code_core20 = __toESM(require_dist3(), 1);
146231
+ var import_code_core21 = __toESM(require_dist3(), 1);
145697
146232
  import * as path27 from "path";
145698
146233
  import * as fs27 from "fs";
145699
146234
  import { createRequire as createRequire2 } from "module";
@@ -151602,7 +152137,7 @@ function confirmMatches(env4, typed) {
151602
152137
  }
151603
152138
 
151604
152139
  // src/commands/chat.ts
151605
- var import_code_core16 = __toESM(require_dist3(), 1);
152140
+ var import_code_core17 = __toESM(require_dist3(), 1);
151606
152141
  import * as os15 from "os";
151607
152142
  import * as crypto7 from "crypto";
151608
152143
  import { createRequire } from "module";
@@ -152512,8 +153047,82 @@ async function askFlow(req) {
152512
153047
  return await choose({ title: `Allow ${tool}?`, risk: riskOf(tool, input), alwaysLabel: null }) !== "n";
152513
153048
  }
152514
153049
 
152515
- // src/audit.ts
153050
+ // src/permissions/promptTool.ts
153051
+ function parsePromptToolAnswer(raw) {
153052
+ if (raw === null)
153053
+ return { error: "the permission tool is not connected (check `nex mcp list`)" };
153054
+ const text = String(raw).trim();
153055
+ let j;
153056
+ try {
153057
+ j = JSON.parse(text);
153058
+ } catch {
153059
+ return { error: `the permission tool returned non-JSON: ${text.slice(0, 200) || "(empty)"}` };
153060
+ }
153061
+ if (!j || typeof j !== "object" || Array.isArray(j)) {
153062
+ return { error: `the permission tool returned JSON that is not an object: ${text.slice(0, 200)}` };
153063
+ }
153064
+ const o = j;
153065
+ const behavior = o.behavior === "allow" || o.behavior === "deny" ? o.behavior : o.decision === "allow" || o.decision === "deny" ? o.decision : null;
153066
+ if (!behavior) {
153067
+ return { error: `the permission tool did not return a behavior/decision of "allow" or "deny": ${text.slice(0, 200)}` };
153068
+ }
153069
+ const message = typeof o.message === "string" && o.message ? o.message : typeof o.reason === "string" && o.reason ? o.reason : void 0;
153070
+ if (behavior === "allow" && o.updatedInput !== void 0) {
153071
+ if (!o.updatedInput || typeof o.updatedInput !== "object" || Array.isArray(o.updatedInput)) {
153072
+ return { error: `the permission tool returned an updatedInput that is not an object: ${text.slice(0, 200)}` };
153073
+ }
153074
+ return { behavior, updatedInput: o.updatedInput, message };
153075
+ }
153076
+ return { behavior, message };
153077
+ }
153078
+ async function askPermissionPromptTool(host, toolName, req, abortSignal) {
153079
+ if (!host)
153080
+ return { error: `--permission-prompt-tool "${toolName}" needs MCP, but no MCP servers are configured for this run` };
153081
+ if (!host.isMcpTool(toolName)) {
153082
+ return { error: `--permission-prompt-tool "${toolName}" is not a tool of any connected MCP server (check \`nex mcp list\`)` };
153083
+ }
153084
+ let raw;
153085
+ try {
153086
+ raw = await host.callTool(toolName, {
153087
+ tool_name: req.tool,
153088
+ input: req.input,
153089
+ ...req.description ? { description: req.description } : {},
153090
+ cwd: req.cwd,
153091
+ permission_mode: req.mode,
153092
+ ...req.sessionId ? { session_id: req.sessionId } : {}
153093
+ }, abortSignal);
153094
+ } catch (err) {
153095
+ return { error: `the permission tool failed: ${err instanceof Error ? err.message : String(err)}` };
153096
+ }
153097
+ return parsePromptToolAnswer(raw);
153098
+ }
153099
+
153100
+ // src/permissions/headlessHook.ts
152516
153101
  var import_code_core7 = __toESM(require_dist3(), 1);
153102
+ async function runHeadlessPermissionHooks(ctx, emit, emitEvent2) {
153103
+ const locked = ctx.policyDeny || ctx.reason === "rule";
153104
+ const destructive = ctx.reason === "destructive" && !!ctx.destructive;
153105
+ if (locked || destructive)
153106
+ return { handled: null };
153107
+ const hook = await (0, import_code_core7.runPermissionRequestHooks)(ctx.workDir, ctx.tool, ctx.input, ctx.sessionId);
153108
+ if (hook.answer === "allow") {
153109
+ emit({ type: "permission_granted", tool: ctx.tool, reason: "hook" });
153110
+ emitEvent2("permission.granted", { tool: ctx.tool, reason: "hook" });
153111
+ return { handled: "allow" };
153112
+ }
153113
+ if (hook.answer === "deny") {
153114
+ emit({ type: "permission_denied", tool: ctx.tool, reason: "hook" });
153115
+ emitEvent2("permission.denied", { tool: ctx.tool, reason: "hook" });
153116
+ return {
153117
+ handled: "deny",
153118
+ message: hook.reason ? `Permission denied by hook: ${hook.reason}` : "Permission denied by hook"
153119
+ };
153120
+ }
153121
+ return { handled: null };
153122
+ }
153123
+
153124
+ // src/audit.ts
153125
+ var import_code_core8 = __toESM(require_dist3(), 1);
152517
153126
  import * as path7 from "path";
152518
153127
  var _state;
152519
153128
  var _sinks = /* @__PURE__ */ new Map();
@@ -152533,7 +153142,7 @@ function currentAudit() {
152533
153142
  const target = resolveAuditPath(_state);
152534
153143
  let sink = _sinks.get(target);
152535
153144
  if (!sink) {
152536
- sink = (0, import_code_core7.createJsonlAuditSink)(target);
153145
+ sink = (0, import_code_core8.createJsonlAuditSink)(target);
152537
153146
  _sinks.set(target, sink);
152538
153147
  }
152539
153148
  const cfg = {
@@ -152546,14 +153155,14 @@ function currentAudit() {
152546
153155
  // request that caused it. Same trap as @nexrall/agent's and Desktop's.
152547
153156
  sessionId: _state.getSessionId()
152548
153157
  };
152549
- const email = (0, import_code_core7.loadAuth)()?.email;
153158
+ const email = (0, import_code_core8.loadAuth)()?.email;
152550
153159
  if (email)
152551
153160
  cfg.userId = email;
152552
153161
  return cfg;
152553
153162
  }
152554
153163
 
152555
153164
  // src/commands/chat.ts
152556
- var import_code_core17 = __toESM(require_dist3(), 1);
153165
+ var import_code_core18 = __toESM(require_dist3(), 1);
152557
153166
 
152558
153167
  // src/headlessFlags.ts
152559
153168
  import * as fs7 from "fs";
@@ -152699,6 +153308,8 @@ function checkHeadlessOnly(f3) {
152699
153308
  return "--no-session-persistence only works with --output-format (a one-shot, non-interactive run).";
152700
153309
  if (f3.maxTurns !== void 0)
152701
153310
  return "--max-turns only works with --output-format (a one-shot, non-interactive run).";
153311
+ if (f3.permissionPromptTool !== void 0)
153312
+ return "--permission-prompt-tool only works with --output-format (a one-shot, non-interactive run).";
152702
153313
  return null;
152703
153314
  }
152704
153315
  function headlessVerdict(i2) {
@@ -152887,6 +153498,15 @@ function readPromptFile(file, cwd2, flag = "--append-system-prompt-file") {
152887
153498
  return { ok: false, error: `${flag}: "${file}" is empty` };
152888
153499
  return { ok: true, value: text.trim() };
152889
153500
  }
153501
+ function normalizePermissionPromptTool(raw) {
153502
+ let name = String(raw).trim();
153503
+ if (name.startsWith("mcp__"))
153504
+ name = name.slice("mcp__".length);
153505
+ if (!name || !name.includes("__")) {
153506
+ return { ok: false, error: `--permission-prompt-tool: "${raw}" is not an MCP tool name. Use mcp__server__tool (the name shown in /mcp or \`nex mcp list\`)` };
153507
+ }
153508
+ return { ok: true, value: name };
153509
+ }
152890
153510
  var CLAUDE_TOOL_ALIASES = {
152891
153511
  Read: ["read_file", "list_directory", "search_files", "glob", "notebook_read"],
152892
153512
  Edit: ["edit_file", "multi_edit", "notebook_edit"],
@@ -153093,7 +153713,7 @@ function parseAgentsArg(raw, cwd2) {
153093
153713
  }
153094
153714
 
153095
153715
  // src/commands/chat.ts
153096
- var import_code_core18 = __toESM(require_dist3(), 1);
153716
+ var import_code_core19 = __toESM(require_dist3(), 1);
153097
153717
 
153098
153718
  // src/debugLog.ts
153099
153719
  import fs8 from "fs";
@@ -153140,14 +153760,14 @@ function debugLog(event, fields) {
153140
153760
  }
153141
153761
 
153142
153762
  // src/commands/doctor.ts
153143
- var import_code_core9 = __toESM(require_dist3(), 1);
153763
+ var import_code_core10 = __toESM(require_dist3(), 1);
153144
153764
  import fs10 from "fs";
153145
153765
  import os4 from "os";
153146
153766
  import path11 from "path";
153147
153767
  import { spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
153148
153768
 
153149
153769
  // src/commands/sessionTools.ts
153150
- var import_code_core8 = __toESM(require_dist3(), 1);
153770
+ var import_code_core9 = __toESM(require_dist3(), 1);
153151
153771
  import { spawnSync, execFileSync } from "child_process";
153152
153772
  import fs9 from "fs";
153153
153773
  import path10 from "path";
@@ -153196,7 +153816,7 @@ function nextMode(current) {
153196
153816
  return MODE_CYCLE[(i2 + 1) % MODE_CYCLE.length];
153197
153817
  }
153198
153818
  function textOf(m2) {
153199
- return m2.content.filter((b) => b.type === "text" && !(0, import_code_core8.isRuntimeContextBlock)(b)).map((b) => b.text).join("\n").trim();
153819
+ return m2.content.filter((b) => b.type === "text" && !(0, import_code_core9.isRuntimeContextBlock)(b)).map((b) => b.text).join("\n").trim();
153200
153820
  }
153201
153821
  function lastAssistantText(messages) {
153202
153822
  for (let i2 = messages.length - 1; i2 >= 0; i2--) {
@@ -153222,7 +153842,7 @@ function exportMarkdown(messages, meta) {
153222
153842
  for (const m2 of messages) {
153223
153843
  const parts = [];
153224
153844
  for (const b of m2.content) {
153225
- if ((0, import_code_core8.isRuntimeContextBlock)(b))
153845
+ if ((0, import_code_core9.isRuntimeContextBlock)(b))
153226
153846
  continue;
153227
153847
  if (b.type === "text") {
153228
153848
  if (b.text.trim())
@@ -153580,17 +154200,17 @@ function realProbes(c) {
153580
154200
  return { inRepo: false };
153581
154201
  }
153582
154202
  },
153583
- hasToken: () => !!(0, import_code_core9.getToken)(),
154203
+ hasToken: () => !!(0, import_code_core10.getToken)(),
153584
154204
  verifyAuth: async () => {
153585
154205
  try {
153586
- await (0, import_code_core9.getBalance)();
154206
+ await (0, import_code_core10.getBalance)();
153587
154207
  return { ok: true, detail: "signed in, token accepted" };
153588
154208
  } catch (e2) {
153589
154209
  return { ok: false, detail: `token rejected \u2014 ${e2.message.slice(0, 120)}` };
153590
154210
  }
153591
154211
  },
153592
- reach: () => reachHost(import_code_core9.API_BASE),
153593
- apiBase: import_code_core9.API_BASE,
154212
+ reach: () => reachHost(import_code_core10.API_BASE),
154213
+ apiBase: import_code_core10.API_BASE,
153594
154214
  isTTY: !!process.stdout.isTTY,
153595
154215
  columns: process.stdout.columns,
153596
154216
  rows: process.stdout.rows,
@@ -153598,7 +154218,7 @@ function realProbes(c) {
153598
154218
  mcp: () => {
153599
154219
  let configured = 0;
153600
154220
  try {
153601
- configured = Object.keys(import_code_core9.McpManager.loadConfig(c.workDir).mcpServers).length;
154221
+ configured = Object.keys(import_code_core10.McpManager.loadConfig(c.workDir).mcpServers).length;
153602
154222
  } catch {
153603
154223
  }
153604
154224
  return { configured, rows: c.mcpRows ?? [] };
@@ -163456,7 +164076,7 @@ var useBoxMetrics = (ref) => {
163456
164076
  var use_box_metrics_default = useBoxMetrics;
163457
164077
 
163458
164078
  // src/ui/themeSettings.ts
163459
- var import_code_core10 = __toESM(require_dist3(), 1);
164079
+ var import_code_core11 = __toESM(require_dist3(), 1);
163460
164080
  import * as fs17 from "fs";
163461
164081
  import * as os9 from "os";
163462
164082
  import * as path17 from "path";
@@ -163490,13 +164110,13 @@ function applyThemeConfig(cfg, opts = {}) {
163490
164110
  return { resolved, notes };
163491
164111
  }
163492
164112
  function applyTrustedTheme(env4 = process.env) {
163493
- const settings = (0, import_code_core10.loadSettings)(os9.homedir());
164113
+ const settings = (0, import_code_core11.loadSettings)(os9.homedir());
163494
164114
  const cfg = readThemeConfig(settings.raw);
163495
164115
  const { resolved, notes } = applyThemeConfig(cfg, { env: env4 });
163496
164116
  return { ...cfg, resolved, notes };
163497
164117
  }
163498
164118
  function loadAndApplyTheme(workDir, env4 = process.env) {
163499
- const settings = (0, import_code_core10.loadSettings)(workDir);
164119
+ const settings = (0, import_code_core11.loadSettings)(workDir);
163500
164120
  const cfg = readThemeConfig(settings.raw);
163501
164121
  const { resolved, notes } = applyThemeConfig(cfg, { env: env4 });
163502
164122
  return { ...cfg, resolved, notes };
@@ -167212,7 +167832,7 @@ function orderByRecents(entries, recents) {
167212
167832
  }
167213
167833
 
167214
167834
  // src/ui/memoryNote.ts
167215
- var import_code_core11 = __toESM(require_dist3(), 1);
167835
+ var import_code_core12 = __toESM(require_dist3(), 1);
167216
167836
  function stripEscape(line) {
167217
167837
  return line.startsWith("\\#") ? line.slice(1) : line;
167218
167838
  }
@@ -167229,7 +167849,7 @@ function parseMemoryNote(line) {
167229
167849
  return text ? { scope: "project", text } : null;
167230
167850
  }
167231
167851
  async function saveNote(note, workDir) {
167232
- const r2 = await (0, import_code_core11.writeMemory)(note.text, note.scope, workDir, { source: "cli" });
167852
+ const r2 = await (0, import_code_core12.writeMemory)(note.text, note.scope, workDir, { source: "cli" });
167233
167853
  return { ok: r2.ok, already: r2.already, superseded: r2.superseded === true, scope: r2.scope, file: r2.file, text: note.text };
167234
167854
  }
167235
167855
  function parseMemoryAdd(arg) {
@@ -167251,7 +167871,7 @@ function interpretPrompt(line) {
167251
167871
  }
167252
167872
 
167253
167873
  // src/ui/mcpPanel.ts
167254
- var import_code_core12 = __toESM(require_dist3(), 1);
167874
+ var import_code_core13 = __toESM(require_dist3(), 1);
167255
167875
  function parseMcpArgs(arg) {
167256
167876
  const parts = arg.trim().split(/\s+/).filter(Boolean);
167257
167877
  if (parts.length === 0)
@@ -167352,7 +167972,7 @@ function formatMcpDetail(input) {
167352
167972
  const desc = (t2.description ?? "").split("\n")[0].trim();
167353
167973
  const head = ` ${t2.name.padEnd(nameWidth)}`;
167354
167974
  const maxSuffix = Math.max(0, budget - head.length);
167355
- let suffix = (0, import_code_core12.describeStats)(byTool.get(t2.name));
167975
+ let suffix = (0, import_code_core13.describeStats)(byTool.get(t2.name));
167356
167976
  if (suffix) {
167357
167977
  suffix = ` ${suffix}`;
167358
167978
  if (suffix.length > maxSuffix) {
@@ -167566,9 +168186,9 @@ function planModelSwitch(next, effort, clamp) {
167566
168186
  }
167567
168187
 
167568
168188
  // src/ui/keybindingsStore.ts
167569
- var import_code_core13 = __toESM(require_dist3(), 1);
168189
+ var import_code_core14 = __toESM(require_dist3(), 1);
167570
168190
  function loadAndApplyKeybindings(workDir) {
167571
- const settings = (0, import_code_core13.loadSettings)(workDir);
168191
+ const settings = (0, import_code_core14.loadSettings)(workDir);
167572
168192
  const raw = settings.raw.keybindings;
167573
168193
  const result = parseKeybindings(raw);
167574
168194
  setBindings(result);
@@ -167618,7 +168238,7 @@ function resetKeybindings(workDir, scope = "user") {
167618
168238
  return result;
167619
168239
  }
167620
168240
  function readRawBlock(workDir) {
167621
- const settings = (0, import_code_core13.loadSettings)(workDir);
168241
+ const settings = (0, import_code_core14.loadSettings)(workDir);
167622
168242
  const raw = settings.raw.keybindings;
167623
168243
  return raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {};
167624
168244
  }
@@ -167818,7 +168438,7 @@ function formatTaskGraph(input) {
167818
168438
  }
167819
168439
 
167820
168440
  // src/ui/contextPanel.ts
167821
- var import_code_core14 = __toESM(require_dist3(), 1);
168441
+ var import_code_core15 = __toESM(require_dist3(), 1);
167822
168442
  var KIND_LABEL2 = {
167823
168443
  tool_result: "tool results",
167824
168444
  reply: "model replies",
@@ -167893,8 +168513,8 @@ function textHead(block) {
167893
168513
  var TOP_DEFAULT = 3;
167894
168514
  var TOP_ALL = 10;
167895
168515
  function measureContext(messages, topN = TOP_DEFAULT) {
167896
- const bytes = (0, import_code_core14.estimateBodyBytes)(messages);
167897
- const tokens = (0, import_code_core14.estimateTokensRough)(messages);
168516
+ const bytes = (0, import_code_core15.estimateBodyBytes)(messages);
168517
+ const tokens = (0, import_code_core15.estimateTokensRough)(messages);
167898
168518
  const toolNames = /* @__PURE__ */ new Map();
167899
168519
  const buckets = /* @__PURE__ */ new Map();
167900
168520
  const all = [];
@@ -168074,7 +168694,7 @@ function formatContextPanel(input) {
168074
168694
  }
168075
168695
 
168076
168696
  // src/ui/permDashboard.ts
168077
- var import_code_core15 = __toESM(require_dist3(), 1);
168697
+ var import_code_core16 = __toESM(require_dist3(), 1);
168078
168698
  var DASHBOARD_ACTIONS = [
168079
168699
  { group: "Filesystem", label: "read a project file", tool: "read_file", input: { path: "src/app.ts" } },
168080
168700
  { group: "Filesystem", label: "edit a project file", tool: "edit_file", input: { path: "src/app.ts", old_string: "a", new_string: "b" } },
@@ -168105,15 +168725,15 @@ var NOT_A_READ = /* @__PURE__ */ new Set(["todo_write", "use_skill"]);
168105
168725
  function capOf(tool) {
168106
168726
  if (tool === "task")
168107
168727
  return "delegate";
168108
- if (import_code_core15.BASH_TOOLS.has(tool))
168728
+ if (import_code_core16.BASH_TOOLS.has(tool))
168109
168729
  return "shell";
168110
- if (import_code_core15.FILE_WRITE_TOOLS.has(tool) || import_code_core15.FILE_MUTATE_TOOLS.has(tool))
168730
+ if (import_code_core16.FILE_WRITE_TOOLS.has(tool) || import_code_core16.FILE_MUTATE_TOOLS.has(tool))
168111
168731
  return "write";
168112
168732
  if (NETWORK_TOOLS.has(tool))
168113
168733
  return "network";
168114
168734
  if (tool === "bash_output" || tool === "kill_shell")
168115
168735
  return "shell";
168116
- if (import_code_core15.READ_ONLY_TOOLS.has(tool) && !NOT_A_READ.has(tool))
168736
+ if (import_code_core16.READ_ONLY_TOOLS.has(tool) && !NOT_A_READ.has(tool))
168117
168737
  return "read";
168118
168738
  return null;
168119
168739
  }
@@ -168141,19 +168761,19 @@ function capPhrase(caps) {
168141
168761
  return parts.length ? parts.join(", ") : "nothing";
168142
168762
  }
168143
168763
  function explainAction(rules, mode, workDir, action, opts = {}) {
168144
- const rule = (0, import_code_core15.evaluatePermission)(rules, action.tool, action.input, workDir);
168764
+ const rule = (0, import_code_core16.evaluatePermission)(rules, action.tool, action.input, workDir);
168145
168765
  if (rule === "deny")
168146
168766
  return { mark: "\u2717", reason: "denied by a permission rule" };
168147
- if ((0, import_code_core15.isDestructiveBash)(action.tool, action.input) && !opts.allowDestructive) {
168767
+ if ((0, import_code_core16.isDestructiveBash)(action.tool, action.input) && !opts.allowDestructive) {
168148
168768
  return { mark: "\u26A0", reason: 'irreversible \u2014 always asks (type "yes")' };
168149
168769
  }
168150
168770
  if (rule === "allow")
168151
168771
  return { mark: "\u2713", reason: "allowed by a permission rule" };
168152
- const verdict = (0, import_code_core15.decide)({ tool: action.tool, input: action.input, mode, bypass: opts.yolo, destructive: false });
168772
+ const verdict = (0, import_code_core16.decide)({ tool: action.tool, input: action.input, mode, bypass: opts.yolo, destructive: false });
168153
168773
  if (verdict === "allow") {
168154
168774
  if (capOf(action.tool) === "read")
168155
168775
  return { mark: "\u2713", reason: "read-only \u2014 allowed in every mode" };
168156
- if (import_code_core15.BASH_TOOLS.has(action.tool) && (0, import_code_core15.isReadOnlyBash)(String(action.input.command ?? ""))) {
168776
+ if (import_code_core16.BASH_TOOLS.has(action.tool) && (0, import_code_core16.isReadOnlyBash)(String(action.input.command ?? ""))) {
168157
168777
  return { mark: "\u2713", reason: "read-only command \u2014 allowed in every mode" };
168158
168778
  }
168159
168779
  if (action.tool === "fetch_url" || action.tool === "web_search") {
@@ -168611,7 +169231,7 @@ function agentTypeModel(type, workDir) {
168611
169231
  if (!agentTypeCache || agentTypeCache.workDir !== workDir || now - agentTypeCache.at > 1e3) {
168612
169232
  const models = /* @__PURE__ */ new Map();
168613
169233
  try {
168614
- for (const t2 of (0, import_code_core16.loadAgentTypesWithWarnings)(workDir).types)
169234
+ for (const t2 of (0, import_code_core17.loadAgentTypesWithWarnings)(workDir).types)
168615
169235
  if (t2.model)
168616
169236
  models.set(t2.name, t2.model);
168617
169237
  } catch {
@@ -168676,13 +169296,13 @@ function agentPanelContext(v, cols, now) {
168676
169296
  }
168677
169297
  let maxConcurrent;
168678
169298
  try {
168679
- maxConcurrent = (0, import_code_core18.resolveMaxConcurrentSubtasks)((0, import_code_core18.loadSettings)(v.workDir).raw);
169299
+ maxConcurrent = (0, import_code_core19.resolveMaxConcurrentSubtasks)((0, import_code_core19.loadSettings)(v.workDir).raw);
168680
169300
  } catch {
168681
169301
  maxConcurrent = void 0;
168682
169302
  }
168683
169303
  const types3 = (() => {
168684
169304
  try {
168685
- return (0, import_code_core16.loadAgentTypesWithWarnings)(v.workDir).types.map((t2) => ({
169305
+ return (0, import_code_core17.loadAgentTypesWithWarnings)(v.workDir).types.map((t2) => ({
168686
169306
  name: t2.name,
168687
169307
  description: t2.description,
168688
169308
  ...t2.model ? { model: t2.model } : {},
@@ -168782,7 +169402,7 @@ function printMergeReview(dir, cols) {
168782
169402
  function printTaskGraph(workDir, cols) {
168783
169403
  let tasks = [];
168784
169404
  try {
168785
- tasks = (0, import_code_core18.listSharedTasks)(workDir);
169405
+ tasks = (0, import_code_core19.listSharedTasks)(workDir);
168786
169406
  } catch (e2) {
168787
169407
  console.log(paint.red(" Could not read the shared task list: " + e2.message));
168788
169408
  return;
@@ -168897,13 +169517,13 @@ var NO_PDF_MODELS = /* @__PURE__ */ new Set([
168897
169517
  ...NO_VISION_MODELS
168898
169518
  ]);
168899
169519
  function needsVisionSidecar(model) {
168900
- return !(0, import_code_core16.liveSupportsImageInput)(model, !NO_VISION_MODELS.has(model));
169520
+ return !(0, import_code_core17.liveSupportsImageInput)(model, !NO_VISION_MODELS.has(model));
168901
169521
  }
168902
169522
  function needsPdfSidecar(model) {
168903
- return !(0, import_code_core16.liveSupportsPdfInput)(model, !NO_PDF_MODELS.has(model));
169523
+ return !(0, import_code_core17.liveSupportsPdfInput)(model, !NO_PDF_MODELS.has(model));
168904
169524
  }
168905
169525
  function selectableModelIds() {
168906
- return (0, import_code_core16.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
169526
+ return (0, import_code_core17.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
168907
169527
  }
168908
169528
  function normaliseModelId(model) {
168909
169529
  const raw = (model ?? "").trim();
@@ -168964,9 +169584,9 @@ var _BACKEND_STYLE_TO_LOCAL = {
168964
169584
  };
168965
169585
  function effortStyleFor(modelId) {
168966
169586
  const id = normaliseModelId(modelId);
168967
- if ((0, import_code_core16.liveVendorFor)(id) === "Anthropic")
169587
+ if ((0, import_code_core17.liveVendorFor)(id) === "Anthropic")
168968
169588
  return "anthropic";
168969
- const liveStyle = (0, import_code_core16.liveReasoningEffortStyle)(id);
169589
+ const liveStyle = (0, import_code_core17.liveReasoningEffortStyle)(id);
168970
169590
  if (liveStyle && _BACKEND_STYLE_TO_LOCAL[liveStyle])
168971
169591
  return _BACKEND_STYLE_TO_LOCAL[liveStyle];
168972
169592
  return MODEL_EFFORT_STYLE[id] ?? "anthropic";
@@ -168986,7 +169606,7 @@ function defaultEffortForModel(modelId) {
168986
169606
  }
168987
169607
  function resolveModelLabel(alias) {
168988
169608
  const id = normaliseModelId(alias);
168989
- return (0, import_code_core16.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
169609
+ return (0, import_code_core17.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
168990
169610
  }
168991
169611
  function tryExec(cmd, cwd2) {
168992
169612
  try {
@@ -169063,22 +169683,39 @@ var tryRead = (p) => {
169063
169683
  return null;
169064
169684
  }
169065
169685
  };
169686
+ var lastLoadedInstructions = [];
169066
169687
  function readNexrallMd(workDir) {
169688
+ lastLoadedInstructions = [];
169067
169689
  if (noDiscovery)
169068
169690
  return void 0;
169069
169691
  const parts = [];
169070
- const memContent = (0, import_code_core16.readAllMemory)(workDir);
169071
- if (memContent)
169692
+ const noteLoaded = (file, memoryType) => {
169693
+ try {
169694
+ if (fs25.existsSync(file))
169695
+ lastLoadedInstructions.push({ file_path: file, memory_type: memoryType, load_reason: "session_start" });
169696
+ } catch {
169697
+ }
169698
+ };
169699
+ const memContent = (0, import_code_core17.readAllMemory)(workDir);
169700
+ if (memContent) {
169072
169701
  parts.push(memContent);
169073
- const globalContent = tryRead(path25.join(os15.homedir(), ".nexrall", "nexrall.md"));
169074
- if (globalContent)
169702
+ noteLoaded((0, import_code_core17.memoryFilePath)("global"), "User");
169703
+ if (workDir)
169704
+ noteLoaded((0, import_code_core17.memoryFilePath)("project", workDir), "Project");
169705
+ }
169706
+ const globalNexrall = path25.join(os15.homedir(), ".nexrall", "nexrall.md");
169707
+ const globalContent = tryRead(globalNexrall);
169708
+ if (globalContent) {
169075
169709
  parts.push(`[Global instructions]
169076
169710
  ${globalContent}`);
169711
+ noteLoaded(globalNexrall, "User");
169712
+ }
169077
169713
  for (const candidate of NEXRALL_MD_CANDIDATES) {
169078
169714
  const content = tryRead(path25.join(workDir, candidate));
169079
169715
  if (content) {
169080
169716
  parts.push(`[Project instructions \u2014 ${candidate}]
169081
169717
  ${content}`);
169718
+ noteLoaded(path25.join(workDir, candidate), "Project");
169082
169719
  break;
169083
169720
  }
169084
169721
  }
@@ -169301,7 +169938,7 @@ var Spinner = class {
169301
169938
  return this.interval !== null;
169302
169939
  }
169303
169940
  };
169304
- var cliBackgroundAgents = new import_code_core16.BackgroundAgentHub();
169941
+ var cliBackgroundAgents = new import_code_core17.BackgroundAgentHub();
169305
169942
  function nextBackgroundChange() {
169306
169943
  return new Promise((resolve11) => {
169307
169944
  const off = cliBackgroundAgents.onChange(() => {
@@ -169319,7 +169956,7 @@ async function initMcp(workDir, override, strict) {
169319
169956
  _mcpManager = null;
169320
169957
  return;
169321
169958
  }
169322
- _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core16.McpManager.loadConfig(workDir);
169959
+ _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core17.McpManager.loadConfig(workDir);
169323
169960
  if (override)
169324
169961
  Object.assign(_mcpConfig.mcpServers, override.mcpServers);
169325
169962
  if (Object.keys(_mcpConfig.mcpServers).length === 0) {
@@ -169330,12 +169967,12 @@ async function initMcp(workDir, override, strict) {
169330
169967
  await injectMcpTokens(_mcpConfig);
169331
169968
  } catch {
169332
169969
  }
169333
- const manager = new import_code_core16.McpManager();
169970
+ const manager = new import_code_core17.McpManager();
169334
169971
  await manager.connectAll(_mcpConfig);
169335
169972
  _mcpManager = manager;
169336
169973
  }
169337
169974
  function formatAgentsList(workDir) {
169338
- const { types: types3, warnings } = (0, import_code_core16.loadAgentTypesWithWarnings)(workDir);
169975
+ const { types: types3, warnings } = (0, import_code_core17.loadAgentTypesWithWarnings)(workDir);
169339
169976
  if (!types3.length)
169340
169977
  return paint.dim(" No agent types found (this should not happen \u2014 builtins always register).");
169341
169978
  const lines = types3.map((t2) => {
@@ -169520,7 +170157,7 @@ function startSessionLog(id, resumed, extra = {}) {
169520
170157
  }
169521
170158
  var failureLedger = new FailureLedger();
169522
170159
  var networkLedger = new NetworkLedger();
169523
- var networkApiHost = hostOf(import_code_core18.API_BASE) ?? import_code_core18.API_BASE;
170160
+ var networkApiHost = hostOf(import_code_core19.API_BASE) ?? import_code_core19.API_BASE;
169524
170161
  var notifier = new Notifier({ env: process.env, isTTY: Boolean(process.stdout.isTTY) });
169525
170162
  var userActiveThisTurn = false;
169526
170163
  var failureBannerShown = "";
@@ -169727,7 +170364,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
169727
170364
  setMode(mode);
169728
170365
  console.log();
169729
170366
  spinner.start("thinking\u2026", { hint: false, verbs: true });
169730
- const result = await (0, import_code_core16.runAgentLoop)(messages, {
170367
+ const result = await (0, import_code_core17.runAgentLoop)(messages, {
169731
170368
  workDir,
169732
170369
  sessionId: activeSessionIdGetter(),
169733
170370
  model: modelAlias,
@@ -170071,12 +170708,12 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
170071
170708
  sessionId: activeSessionIdGetter(),
170072
170709
  cwd: workDir,
170073
170710
  model: modelAlias,
170074
- tools: _headlessPolicy.onlyTools ? [..._headlessPolicy.onlyTools].sort() : (0, import_code_core16.knownToolNames)(),
170711
+ tools: _headlessPolicy.onlyTools ? [..._headlessPolicy.onlyTools].sort() : (0, import_code_core17.knownToolNames)(),
170075
170712
  permissionMode: _headlessPolicy.permissionMode
170076
170713
  }));
170077
170714
  emit({ type: "start", model: modelAlias, workDir });
170078
170715
  verbose(`[verbose] model=${modelAlias} cwd=${workDir} mode=${mode} session=${activeSessionIdGetter() ?? "-"}`);
170079
- const result = await (0, import_code_core16.runAgentLoop)(messages, {
170716
+ const result = await (0, import_code_core17.runAgentLoop)(messages, {
170080
170717
  workDir,
170081
170718
  sessionId: activeSessionIdGetter(),
170082
170719
  model: modelAlias,
@@ -170217,16 +170854,43 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
170217
170854
  emit({ type: "permission_denied", tool, reason: "tools" });
170218
170855
  return false;
170219
170856
  }
170220
- const d = (0, import_code_core16.isDestructiveBash)(tool, input);
170857
+ const d = (0, import_code_core17.isDestructiveBash)(tool, input);
170858
+ const policy = (0, import_code_core18.decide)({ tool, input, mode: (0, import_code_core19.parseMode)(mode), bypass: isYoloMode(), destructive: !!d });
170221
170859
  const v = headlessVerdict({
170222
- rule: (0, import_code_core18.evaluatePermission)((0, import_code_core18.loadSettings)(workDir).permissions, tool, input, workDir),
170860
+ rule: (0, import_code_core19.evaluatePermission)((0, import_code_core19.loadSettings)(workDir).permissions, tool, input, workDir),
170223
170861
  destructive: !!d,
170224
170862
  allowDestructive: process.env.NEXRALL_ALLOW_DESTRUCTIVE === "1",
170225
170863
  strict: _headlessPolicy.strict,
170226
- policy: (0, import_code_core17.decide)({ tool, input, mode: (0, import_code_core18.parseMode)(mode), bypass: isYoloMode(), destructive: !!d })
170864
+ policy
170227
170865
  });
170228
170866
  if (v.verdict === "allow")
170229
170867
  return true;
170868
+ const hookGate = await runHeadlessPermissionHooks(
170869
+ { tool, input, workDir, sessionId: activeSessionIdGetter() ?? "", reason: v.reason, policyDeny: policy === "deny", destructive: d },
170870
+ emit,
170871
+ emitEvent
170872
+ );
170873
+ if (hookGate.handled === "allow")
170874
+ return true;
170875
+ if (hookGate.handled === "deny")
170876
+ return { granted: false, message: hookGate.message };
170877
+ const toolFlag = _headlessPolicy.permissionPromptTool;
170878
+ if (toolFlag && v.reason !== "rule" && !(policy === "deny")) {
170879
+ const ans = await askPermissionPromptTool(_mcpManager, toolFlag, { tool, input, cwd: workDir, mode, sessionId: activeSessionIdGetter() ?? void 0 }, _abortSignal);
170880
+ if ("error" in ans) {
170881
+ emit({ type: "error", error: `--permission-prompt-tool ${toolFlag}: ${ans.error}` });
170882
+ emitEvent("permission.denied", { tool, reason: "prompt-tool-error" });
170883
+ return { granted: false, message: `Permission denied: ${ans.error}` };
170884
+ }
170885
+ if (ans.behavior === "allow") {
170886
+ emit({ type: "permission_granted", tool, reason: "prompt-tool" });
170887
+ emitEvent("permission.granted", { tool, reason: "prompt-tool" });
170888
+ return ans.updatedInput ? { granted: true, updatedInput: ans.updatedInput } : true;
170889
+ }
170890
+ emit({ type: "permission_denied", tool, reason: "prompt-tool" });
170891
+ emitEvent("permission.denied", { tool, reason: "prompt-tool" });
170892
+ return { granted: false, message: ans.message ? `Permission denied by ${toolFlag}: ${ans.message}` : `Permission denied by ${toolFlag}` };
170893
+ }
170230
170894
  if (v.reason === "destructive" && d) {
170231
170895
  emit({ type: "error", error: `Destructive command blocked in headless mode (${d.category}): ${d.reason}. Set NEXRALL_ALLOW_DESTRUCTIVE=1 to permit.` });
170232
170896
  } else {
@@ -170464,7 +171128,7 @@ async function startChatSession(options) {
170464
171128
  if (options.bare)
170465
171129
  setNoDiscovery(true);
170466
171130
  _cliAgentTypes = (options.agents ?? []).map((a) => ({ ...a, source: "programmatic" }));
170467
- (0, import_code_core16.enableAgentTranscriptPersistence)();
171131
+ (0, import_code_core17.enableAgentTranscriptPersistence)();
170468
171132
  const headless = options.outputFormat === "json" || options.outputFormat === "stream-json";
170469
171133
  const requestedWorkDir = options.workDir;
170470
171134
  let workDir = requestedWorkDir;
@@ -170473,7 +171137,7 @@ async function startChatSession(options) {
170473
171137
  let peerInbox;
170474
171138
  const peerInboxQueue = [];
170475
171139
  let modelAlias = options.model;
170476
- void (0, import_code_core16.getModelCatalogue)().catch(() => {
171140
+ void (0, import_code_core17.getModelCatalogue)().catch(() => {
170477
171141
  });
170478
171142
  const canPrompt = !headless && !options.prompt && !options.stdinText && process.stdin.isTTY === true && process.stdout.isTTY === true;
170479
171143
  if (!trustGrantedByEnv()) {
@@ -170501,21 +171165,21 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
170501
171165
  }
170502
171166
  const themeConfig = loadAndApplyTheme(workDir);
170503
171167
  const keyConfig = loadAndApplyKeybindings(workDir);
170504
- liveDiffEnabled = readLiveDiffSetting((0, import_code_core18.loadSettings)(workDir).raw);
170505
- mouseEnabled = readMouseSetting((0, import_code_core18.loadSettings)(workDir).raw);
170506
- notifier.setMode(readNotifySetting((0, import_code_core18.loadSettings)(workDir).raw));
171168
+ liveDiffEnabled = readLiveDiffSetting((0, import_code_core19.loadSettings)(workDir).raw);
171169
+ mouseEnabled = readMouseSetting((0, import_code_core19.loadSettings)(workDir).raw);
171170
+ notifier.setMode(readNotifySetting((0, import_code_core19.loadSettings)(workDir).raw));
170507
171171
  setAskNotifier((what) => {
170508
171172
  notifier.notify(needsYouNote(what));
170509
171173
  emitEvent("permission.requested", { what });
170510
- (0, import_code_core17.fireNotificationHooks)(workDir, `Nexrall needs your permission: ${what}`);
171174
+ (0, import_code_core18.fireNotificationHooks)(workDir, `Nexrall needs your permission: ${what}`);
170511
171175
  });
170512
171176
  setPermissionObserver((tool, granted) => emitEvent(granted ? "permission.granted" : "permission.denied", { tool }));
170513
171177
  let activeThemeName = themeConfig.theme;
170514
171178
  if (options.worktree !== void 0) {
170515
171179
  const requestedName = typeof options.worktree === "string" ? options.worktree : void 0;
170516
- const existing = requestedName ? (0, import_code_core16.findWorktreeByName)(requestedWorkDir, requestedName) : null;
171180
+ const existing = requestedName ? (0, import_code_core17.findWorktreeByName)(requestedWorkDir, requestedName) : null;
170517
171181
  if (existing) {
170518
- const entered = (0, import_code_core16.enterWorktree)(existing);
171182
+ const entered = (0, import_code_core17.enterWorktree)(existing);
170519
171183
  if (!entered.ok) {
170520
171184
  process.stderr.write(`Failed to resume worktree "${requestedName}": ${entered.error}
170521
171185
  `);
@@ -170525,7 +171189,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
170525
171189
  if (!headless)
170526
171190
  console.log(paint.dim(` Resumed worktree: ${paint.bold(existing.name)} (${existing.worktreePath})`));
170527
171191
  } else {
170528
- const created = (0, import_code_core16.createWorktree)(requestedWorkDir, requestedName);
171192
+ const created = (0, import_code_core17.createWorktree)(requestedWorkDir, requestedName);
170529
171193
  if (!created.ok || !created.state) {
170530
171194
  process.stderr.write(`Failed to create worktree: ${created.error ?? "unknown error"}
170531
171195
  `);
@@ -170544,7 +171208,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
170544
171208
  process.on("exit", () => {
170545
171209
  if (!worktreeState)
170546
171210
  return;
170547
- const hadWork = (0, import_code_core16.worktreeHasWork)(worktreeState);
171211
+ const hadWork = (0, import_code_core17.worktreeHasWork)(worktreeState);
170548
171212
  if (hadWork) {
170549
171213
  process.stderr.write(
170550
171214
  `
@@ -170555,7 +171219,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170555
171219
  );
170556
171220
  return;
170557
171221
  }
170558
- (0, import_code_core16.removeWorktree)(worktreeState);
171222
+ (0, import_code_core17.removeWorktree)(worktreeState);
170559
171223
  });
170560
171224
  }
170561
171225
  const env4 = collectEnv(workDir);
@@ -170564,7 +171228,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170564
171228
  const nexrallMd = [agentPrefix, briefBase, options.appendSystemPrompt?.trim(), addDirNote(options.addDirs ?? []), options.jsonSchema ? schemaInstruction(options.jsonSchema) : ""].filter(Boolean).join("\n\n") || void 0;
170565
171229
  const agentTools = options.selectedAgent?.tools;
170566
171230
  const toolAllow = agentTools && options.onlyTools ? options.onlyTools.filter((t2) => agentTools.includes(t2)) : options.onlyTools ?? agentTools;
170567
- _headlessPolicy = { strict: !!options.strictPermissions, budgetUsd: options.budgetUsd, spent: 0, schema: options.jsonSchema, maxTurns: options.maxTurns, onlyTools: toolAllow ? new Set(toolAllow) : void 0 };
171231
+ _headlessPolicy = { strict: !!options.strictPermissions, budgetUsd: options.budgetUsd, spent: 0, schema: options.jsonSchema, maxTurns: options.maxTurns, onlyTools: toolAllow ? new Set(toolAllow) : void 0, permissionPromptTool: options.permissionPromptTool };
170568
171232
  _headlessPolicy.verbose = !!options.verbose;
170569
171233
  _headlessPolicy.includePartial = !!options.includePartialMessages;
170570
171234
  _headlessPolicy.permissionMode = String(options.initialMode ?? "");
@@ -170573,8 +171237,8 @@ Resume it with: nex --worktree ${worktreeState.name}
170573
171237
  const interactive = !headless && !options.prompt && !options.stdinText;
170574
171238
  let peerHeartbeatTimer;
170575
171239
  if (interactive) {
170576
- const handle2 = (0, import_code_core16.registerPeer)({ clientType: "cli", workDir: requestedWorkDir });
170577
- const inbox = (0, import_code_core16.startPeerInbox)(handle2.record.id, { onMessage: (m2) => peerInboxQueue.push(m2) });
171240
+ const handle2 = (0, import_code_core17.registerPeer)({ clientType: "cli", workDir: requestedWorkDir });
171241
+ const inbox = (0, import_code_core17.startPeerInbox)(handle2.record.id, { onMessage: (m2) => peerInboxQueue.push(m2) });
170578
171242
  handle2.record.socketPath = inbox.socketPath;
170579
171243
  handle2.heartbeat();
170580
171244
  peerHandle = handle2;
@@ -170646,6 +171310,18 @@ Resume it with: nex --worktree ${worktreeState.name}
170646
171310
  let sessionId = options.newSessionId ?? crypto7.randomBytes(8).toString("hex");
170647
171311
  activeSessionIdGetter = () => sessionId;
170648
171312
  let sessionTitle = options.sessionName ?? "";
171313
+ const fileWatcher = noDiscovery ? null : (0, import_code_core17.startFileChangedWatcher)(workDir, sessionId, {
171314
+ // A hook's {"systemMessage":"…"} is the event's one user-visible field.
171315
+ onSystemMessage: (m2) => console.log(paint.dim(` ${m2}`))
171316
+ });
171317
+ if (fileWatcher) {
171318
+ const watching = fileWatcher.watchedFiles();
171319
+ if (watching.length && !headless)
171320
+ console.log(paint.dim(` FileChanged hook: watching ${watching.join(", ")}`));
171321
+ if (options.verbose)
171322
+ process.stderr.write("[verbose] FileChanged hook: " + watching.join(", ") + "\n");
171323
+ }
171324
+ void (0, import_code_core17.fireInstructionsLoadedHooks)(workDir, sessionId, lastLoadedInstructions);
170649
171325
  const modeLocked = options.lockMode === true;
170650
171326
  let agentMode = modeLocked ? "plan" : options.initialMode ?? "auto";
170651
171327
  const refuseIfLocked = (what) => {
@@ -170667,7 +171343,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170667
171343
  for (const n of themeConfig.notes)
170668
171344
  console.log(paint.dim(" theme: " + n));
170669
171345
  }
170670
- let skills = noDiscovery ? [] : (0, import_code_core16.loadSkills)(workDir);
171346
+ let skills = noDiscovery ? [] : (0, import_code_core17.loadSkills)(workDir);
170671
171347
  if (skills.length && !headless)
170672
171348
  console.log(paint.dim(` ${skills.length} skill(s) loaded`));
170673
171349
  let contextSet = loadContextSet(workDir);
@@ -170732,7 +171408,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170732
171408
  }
170733
171409
  startSessionLog(sessionId, true, options.forkSession ? { workDir, forkOf: stored.id } : { workDir });
170734
171410
  try {
170735
- const compacted = await (0, import_code_core16.compactMessagesForResume)(messages, {
171411
+ const compacted = await (0, import_code_core17.compactMessagesForResume)(messages, {
170736
171412
  workDir,
170737
171413
  model: modelAlias,
170738
171414
  clientType: "cli"
@@ -170748,7 +171424,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170748
171424
  }
170749
171425
  if (!(options.resume || options.continueLast))
170750
171426
  startSessionLog(sessionId, false, { workDir });
170751
- const checkpoints = new import_code_core16.CheckpointManager(workDir, sessionId);
171427
+ const checkpoints = new import_code_core17.CheckpointManager(workDir, sessionId);
170752
171428
  const commitCheckpoint = () => {
170753
171429
  checkpoints.commitTurn();
170754
171430
  emitEvent("checkpoint.created", { turns: checkpoints.list().length });
@@ -170770,7 +171446,7 @@ Resume it with: nex --worktree ${worktreeState.name}
170770
171446
  sessionTitle = raw.length > 60 ? raw.slice(0, 60) + "\u2026" : raw;
170771
171447
  };
170772
171448
  const recoverTurn = (err, current) => {
170773
- const salvaged = (0, import_code_core16.salvageHistory)(err);
171449
+ const salvaged = (0, import_code_core17.salvageHistory)(err);
170774
171450
  printErrorReport(err, abortSignal.aborted);
170775
171451
  printTurnSummary(abortSignal.aborted ? "interrupted" : "failed");
170776
171452
  if (!salvaged)
@@ -170828,7 +171504,7 @@ ${text}` : "");
170828
171504
  throw e2;
170829
171505
  const from = modelAlias;
170830
171506
  modelAlias = options.fallbackModel;
170831
- const salvagedMsgs = (0, import_code_core16.salvageHistory)(e2);
171507
+ const salvagedMsgs = (0, import_code_core17.salvageHistory)(e2);
170832
171508
  if (salvagedMsgs)
170833
171509
  messages = salvagedMsgs;
170834
171510
  const note = `Model ${from} failed (${String(e2.message).slice(0, 120)}); retrying on ${modelAlias}`;
@@ -170847,7 +171523,7 @@ ${text}` : "");
170847
171523
  continue;
170848
171524
  }
170849
171525
  const notes = cliBackgroundAgents.takeNotifications();
170850
- messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core16.formatBackgroundNotifications)(notes) }] });
171526
+ messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core17.formatBackgroundNotifications)(notes) }] });
170851
171527
  result = await runOnce();
170852
171528
  messages = result.messages;
170853
171529
  }
@@ -170865,7 +171541,7 @@ ${text}` : "");
170865
171541
  await nextBackgroundChange();
170866
171542
  continue;
170867
171543
  }
170868
- messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core16.formatBackgroundNotifications)(cliBackgroundAgents.takeNotifications()) }] });
171544
+ messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core17.formatBackgroundNotifications)(cliBackgroundAgents.takeNotifications()) }] });
170869
171545
  result = await runOnce();
170870
171546
  messages = result.messages;
170871
171547
  }
@@ -170878,7 +171554,7 @@ ${text}` : "");
170878
171554
  await new Promise((r2) => setImmediate(r2));
170879
171555
  process.exit(process.exitCode ?? 0);
170880
171556
  } catch (err) {
170881
- const salvaged = (0, import_code_core16.salvageHistory)(err);
171557
+ const salvaged = (0, import_code_core17.salvageHistory)(err);
170882
171558
  if (salvaged) {
170883
171559
  messages = salvaged;
170884
171560
  try {
@@ -170911,12 +171587,12 @@ ${text}` : "");
170911
171587
  console.log(paint.dim(" Type a message, /help for commands, or Ctrl+C to exit."));
170912
171588
  console.log();
170913
171589
  const footerExtras = () => {
170914
- const win = (0, import_code_core16.contextWindowFor)(modelAlias);
171590
+ const win = (0, import_code_core17.contextWindowFor)(modelAlias);
170915
171591
  return {
170916
171592
  nav: transcriptNav ? navLabel(transcriptNav) : void 0,
170917
171593
  model: resolveModelLabel(modelAlias),
170918
171594
  branch: gitBranch(workDir) ?? void 0,
170919
- ctxPct: messages.length ? Math.min(100, (0, import_code_core16.estimateTokensRough)(messages) / win * 100) : 0,
171595
+ ctxPct: messages.length ? Math.min(100, (0, import_code_core17.estimateTokensRough)(messages) / win * 100) : 0,
170920
171596
  costUsd: sessionUsage.costReported ? sessionUsage.cost : void 0,
170921
171597
  // Live sub-agents (spec §37). Recomputed per footer render, which is also what
170922
171598
  // retires a background agent's row the moment the hub drops it.
@@ -170986,7 +171662,7 @@ ${text}` : "");
170986
171662
  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)") : ""));
170987
171663
  }
170988
171664
  };
170989
- const allSlashEntries = () => [...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core16.userInvokableSkills)(skills))];
171665
+ const allSlashEntries = () => [...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core17.userInvokableSkills)(skills))];
170990
171666
  let slashRecents = loadRecents();
170991
171667
  const syncSlashMenu = () => {
170992
171668
  if (interactive)
@@ -171028,7 +171704,7 @@ ${text}` : "");
171028
171704
  };
171029
171705
  const modelPickerNote = (id) => {
171030
171706
  const parts = [];
171031
- const mult = (0, import_code_core16.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
171707
+ const mult = (0, import_code_core17.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
171032
171708
  if (mult !== void 0 && mult !== 1)
171033
171709
  parts.push(`${mult}x`);
171034
171710
  if (needsVisionSidecar(id))
@@ -171223,7 +171899,7 @@ ${text}` : "");
171223
171899
  return;
171224
171900
  }
171225
171901
  const row2 = matches[0];
171226
- const scope = import_code_core16.McpManager.serverScope(workDir, row2.name);
171902
+ const scope = import_code_core17.McpManager.serverScope(workDir, row2.name);
171227
171903
  if (action.kind === "detail") {
171228
171904
  const st = _mcpManager?.getStatus().find((x2) => x2.name === row2.name);
171229
171905
  const tools = (st?.tools ?? []).map((t2) => ({ name: t2.name, description: t2.description }));
@@ -171247,7 +171923,7 @@ ${text}` : "");
171247
171923
  let signedIn = false;
171248
171924
  let attempted = false;
171249
171925
  if (!plan.refuse && plan.persist) {
171250
- import_code_core16.McpManager.setServerDisabled(workDir, row2.name, plan.persist.disabled, plan.persist.scope);
171926
+ import_code_core17.McpManager.setServerDisabled(workDir, row2.name, plan.persist.disabled, plan.persist.scope);
171251
171927
  noteDisabledInConfig(row2.name, plan.persist.disabled);
171252
171928
  }
171253
171929
  if (!plan.refuse) {
@@ -171432,12 +172108,12 @@ ${text}` : "");
171432
172108
  rl.prompt();
171433
172109
  return;
171434
172110
  }
171435
- if ((0, import_code_core18.evaluatePermission)(permRules, "bash", { command: cmd }, workDir) === "deny") {
172111
+ if ((0, import_code_core19.evaluatePermission)(permRules, "bash", { command: cmd }, workDir) === "deny") {
171436
172112
  console.log(paint.red(" \u2716 Denied by permission rule: bash"));
171437
172113
  rl.prompt();
171438
172114
  return;
171439
172115
  }
171440
- if (modeLocked && !(0, import_code_core18.isReadOnlyBash)(cmd)) {
172116
+ if (modeLocked && !(0, import_code_core19.isReadOnlyBash)(cmd)) {
171441
172117
  console.log(paint.yellow(" \u2298 Read-only session (--readonly): only read-only commands can run with !."));
171442
172118
  rl.prompt();
171443
172119
  return;
@@ -171592,7 +172268,7 @@ ${text}` : "");
171592
172268
  emitEvent("session.completed", { id: liveSessionId, reason: "clear" });
171593
172269
  sessionId = crypto7.randomBytes(8).toString("hex");
171594
172270
  sessionTitle = "";
171595
- (0, import_code_core18.resetSessionSubAgentBudget)();
172271
+ (0, import_code_core19.resetSessionSubAgentBudget)();
171596
172272
  sessionUsage.reset();
171597
172273
  sessionStartedAt = Date.now();
171598
172274
  sessionToolCalls = 0;
@@ -171688,7 +172364,7 @@ ${text}` : "");
171688
172364
  startSessionLog(sessionId, true, { workDir });
171689
172365
  const beforeCompact = messages.length;
171690
172366
  try {
171691
- await (0, import_code_core16.compactMessagesForResume)(messages, {
172367
+ await (0, import_code_core17.compactMessagesForResume)(messages, {
171692
172368
  workDir,
171693
172369
  model: modelAlias,
171694
172370
  clientType: "cli"
@@ -171742,7 +172418,7 @@ ${text}` : "");
171742
172418
  }
171743
172419
  rl.pause();
171744
172420
  console.log(paint.dim(" Compacting conversation\u2026"));
171745
- await (0, import_code_core17.fireManualPreCompactHook)(workDir, sessionId, Math.max(0, messages.length - 4));
172421
+ await (0, import_code_core18.fireManualPreCompactHook)(workDir, sessionId, Math.max(0, messages.length - 4));
171746
172422
  const KEEP = 4;
171747
172423
  const toSummarize = messages.slice(0, -KEEP);
171748
172424
  const kept = messages.slice(-KEEP);
@@ -171788,7 +172464,7 @@ ${summaryText}` }] },
171788
172464
  saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
171789
172465
  console.log(paint.green(` Compacted ${toSummarize.length} messages \u2192 ${messages.length} messages.`));
171790
172466
  emitEvent("context.compacted", { where: "manual", before: toSummarize.length + KEEP, after: messages.length });
171791
- await (0, import_code_core17.fireManualPostCompactHook)(workDir, sessionId, toSummarize.length);
172467
+ await (0, import_code_core18.fireManualPostCompactHook)(workDir, sessionId, toSummarize.length);
171792
172468
  if (pin.attachments.length)
171793
172469
  console.log(paint.dim(` ${pin.attachments.length} pinned file(s) re-attached.`));
171794
172470
  for (const n of pin.notes)
@@ -171864,13 +172540,13 @@ ${dirList}`;
171864
172540
  const sub = arg.trim().toLowerCase();
171865
172541
  console.log();
171866
172542
  if (sub === "list") {
171867
- const all = (0, import_code_core16.listWorktrees)(requestedWorkDir);
172543
+ const all = (0, import_code_core17.listWorktrees)(requestedWorkDir);
171868
172544
  if (!all.length) {
171869
172545
  console.log(paint.dim(" No worktrees under this project."));
171870
172546
  } else {
171871
172547
  console.log(paint.bold(` Worktrees (${all.length})`));
171872
172548
  for (const w of all) {
171873
- const dirty = (0, import_code_core16.worktreeHasWork)(w) ? paint.yellow("dirty") : paint.dim("clean");
172549
+ const dirty = (0, import_code_core17.worktreeHasWork)(w) ? paint.yellow("dirty") : paint.dim("clean");
171874
172550
  console.log(` ${paint.cyan(w.name)} ${paint.dim(w.worktreePath)} [${dirty}]`);
171875
172551
  }
171876
172552
  console.log();
@@ -171908,7 +172584,7 @@ ${dirList}`;
171908
172584
  if (!targetName || !text) {
171909
172585
  console.log(paint.dim(" Usage: /peers send <name> <message>"));
171910
172586
  } else {
171911
- const target = (0, import_code_core16.listPeers)().find((p) => p.name === targetName);
172587
+ const target = (0, import_code_core17.listPeers)().find((p) => p.name === targetName);
171912
172588
  if (!target) {
171913
172589
  console.log(paint.red(` No live session named "${targetName}". Use /peers to list who's reachable.`));
171914
172590
  } else if (!target.socketPath) {
@@ -171925,7 +172601,7 @@ ${dirList}`;
171925
172601
  console.log(paint.dim(" Usage: /peers broadcast <message>"));
171926
172602
  } else {
171927
172603
  const { broadcastPeerMessage } = await Promise.resolve().then(() => __toESM(require_dist3(), 1));
171928
- const targets = (0, import_code_core16.listPeers)(peerHandle.record.id).filter((p) => p.id !== peerHandle.record.id && p.socketPath && p.inbound !== "refuse").map((p) => ({ name: p.name, socketPath: p.socketPath }));
172604
+ const targets = (0, import_code_core17.listPeers)(peerHandle.record.id).filter((p) => p.id !== peerHandle.record.id && p.socketPath && p.inbound !== "refuse").map((p) => ({ name: p.name, socketPath: p.socketPath }));
171929
172605
  if (!targets.length) {
171930
172606
  console.log(paint.dim(" No other reachable Nexrall Code sessions to broadcast to."));
171931
172607
  } else {
@@ -171937,7 +172613,7 @@ ${dirList}`;
171937
172613
  }
171938
172614
  }
171939
172615
  } else {
171940
- const peers = (0, import_code_core16.listPeers)(peerHandle.record.id);
172616
+ const peers = (0, import_code_core17.listPeers)(peerHandle.record.id);
171941
172617
  if (peers.length <= 1) {
171942
172618
  console.log(paint.dim(" No other Nexrall Code sessions are currently discoverable on this machine."));
171943
172619
  } else {
@@ -171979,7 +172655,7 @@ ${dirList}`;
171979
172655
  const label = memScope === "global" ? "GLOBAL" : "this PROJECT's";
171980
172656
  const answer = await new Promise((resolve11) => rl.question(paint.yellow(` Clear ${label} memory? This cannot be undone. [y/n] `), (a) => resolve11(a.trim())));
171981
172657
  if (answer === "y" || answer === "yes") {
171982
- (0, import_code_core16.clearMemory)(memScope, workDir);
172658
+ (0, import_code_core17.clearMemory)(memScope, workDir);
171983
172659
  console.log(paint.green(` ${memScope === "global" ? "Global" : "Project"} memory cleared.`));
171984
172660
  } else {
171985
172661
  console.log(paint.dim(" Cancelled."));
@@ -171990,20 +172666,20 @@ ${dirList}`;
171990
172666
  }
171991
172667
  console.log();
171992
172668
  if (wantsGlobalOnly) {
171993
- const stats = (0, import_code_core16.memoryStats)("global");
171994
- const content = (0, import_code_core16.readMemory)("global", void 0, { includeArchived: wantsArchived });
172669
+ const stats = (0, import_code_core17.memoryStats)("global");
172670
+ const content = (0, import_code_core17.readMemory)("global", void 0, { includeArchived: wantsArchived });
171995
172671
  const archivedNote = stats.archived > 0 ? `, ${stats.archived} archived` : "";
171996
172672
  console.log(paint.bold(" Global memory ") + paint.dim(`(${stats.entries} entries${archivedNote}, ${(stats.bytes / 1024).toFixed(1)}KB) \u2014 ${stats.file}`));
171997
172673
  console.log();
171998
172674
  console.log(content || paint.dim(" No global memories saved yet."));
171999
172675
  } else {
172000
- const projStats = (0, import_code_core16.memoryStats)("project", workDir);
172001
- const globalStats = (0, import_code_core16.memoryStats)("global");
172676
+ const projStats = (0, import_code_core17.memoryStats)("project", workDir);
172677
+ const globalStats = (0, import_code_core17.memoryStats)("global");
172002
172678
  const projArchived = projStats.archived > 0 ? `+${projStats.archived} archived ` : "";
172003
172679
  const globalArchived = globalStats.archived > 0 ? `+${globalStats.archived} archived ` : "";
172004
172680
  console.log(paint.bold(" Memory") + paint.dim(` \u2014 project: ${projStats.entries} ${projArchived}entries (${(projStats.bytes / 1024).toFixed(1)}KB) \xB7 global: ${globalStats.entries} ${globalArchived}entries (${(globalStats.bytes / 1024).toFixed(1)}KB)`));
172005
172681
  console.log();
172006
- const merged = (0, import_code_core16.readAllMemory)(workDir, { includeArchived: wantsArchived });
172682
+ const merged = (0, import_code_core17.readAllMemory)(workDir, { includeArchived: wantsArchived });
172007
172683
  console.log(merged || paint.dim(" No memories saved yet."));
172008
172684
  }
172009
172685
  console.log();
@@ -172038,12 +172714,12 @@ ${dirList}`;
172038
172714
  console.log(paint.dim(` Current: ${paint.cyan(agentMode)}`));
172039
172715
  for (const m2 of ["ask", "edit", "auto", "plan"]) {
172040
172716
  const marker = m2 === agentMode ? paint.cyan("\u276F") : " ";
172041
- console.log(` ${marker} ${paint.bold(m2.padEnd(5))} ${paint.dim((0, import_code_core18.describeMode)(m2).replace(`${m2} \u2014 `, ""))}`);
172717
+ console.log(` ${marker} ${paint.bold(m2.padEnd(5))} ${paint.dim((0, import_code_core19.describeMode)(m2).replace(`${m2} \u2014 `, ""))}`);
172042
172718
  }
172043
172719
  } else if (["ask", "edit", "plan", "auto"].includes(modeArg)) {
172044
172720
  agentMode = modeArg;
172045
172721
  updateFooter();
172046
- console.log(paint.green(` Mode \u2192 ${paint.bold(modeArg)}`) + paint.dim(` ${(0, import_code_core18.describeMode)(modeArg).replace(`${modeArg} \u2014 `, "")}`));
172722
+ console.log(paint.green(` Mode \u2192 ${paint.bold(modeArg)}`) + paint.dim(` ${(0, import_code_core19.describeMode)(modeArg).replace(`${modeArg} \u2014 `, "")}`));
172047
172723
  } else {
172048
172724
  console.log(paint.red(` Unknown: ${modeArg}`));
172049
172725
  }
@@ -172084,22 +172760,22 @@ ${dirList}`;
172084
172760
  if (target === void 0) {
172085
172761
  console.log(paint.red(" Usage: /team use <team id|personal> (/team lists the ids)"));
172086
172762
  } else {
172087
- await (0, import_code_core16.setBillingTarget)(target);
172763
+ await (0, import_code_core17.setBillingTarget)(target);
172088
172764
  console.log(paint.green(` \u2713 Usage will now be billed to ${target === null ? "your personal wallet" : `team ${target}`}.`));
172089
172765
  if (target !== null)
172090
172766
  console.log(paint.dim(" This applies to every product and client on this account, not just the CLI."));
172091
172767
  }
172092
172768
  } else {
172093
172769
  try {
172094
- const orgs = await (0, import_code_core16.getOrgs)();
172095
- const pin = (0, import_code_core16.billingOverrideFields)();
172770
+ const orgs = await (0, import_code_core17.getOrgs)();
172771
+ const pin = (0, import_code_core17.billingOverrideFields)();
172096
172772
  for (const l2 of billingLines(orgs, "teamId" in pin ? pin.teamId : void 0)) {
172097
172773
  console.log(l2.startsWith("!") ? paint.yellow(" " + l2) : " " + l2);
172098
172774
  }
172099
172775
  } catch (orgErr) {
172100
172776
  if (cmd === "/team")
172101
172777
  throw orgErr;
172102
- const bal = await (0, import_code_core16.getBalance)();
172778
+ const bal = await (0, import_code_core17.getBalance)();
172103
172779
  console.log(paint.dim(" Balance: ") + paint.cyan(`$${bal.toFixed(4)}`));
172104
172780
  }
172105
172781
  }
@@ -172493,7 +173169,7 @@ ${dirList}`;
172493
173169
  }
172494
173170
  agentMode = "plan";
172495
173171
  updateFooter();
172496
- console.log(paint.green(` Mode \u2192 ${paint.bold("plan")}`) + paint.dim(` ${(0, import_code_core18.describeMode)("plan").replace("plan \u2014 ", "")} (/mode auto to leave)`));
173172
+ console.log(paint.green(` Mode \u2192 ${paint.bold("plan")}`) + paint.dim(` ${(0, import_code_core19.describeMode)("plan").replace("plan \u2014 ", "")} (/mode auto to leave)`));
172497
173173
  rl.prompt();
172498
173174
  return;
172499
173175
  }
@@ -172501,7 +173177,7 @@ ${dirList}`;
172501
173177
  const parts2 = arg.trim().split(/\s+/).filter(Boolean);
172502
173178
  const scope = parts2.includes("--project") ? "project" : "user";
172503
173179
  const want = (parts2.find((x2) => x2 !== "--project") ?? "").toLowerCase();
172504
- const customColorsNow = () => readThemeConfig((0, import_code_core18.loadSettings)(workDir).raw).custom;
173180
+ const customColorsNow = () => readThemeConfig((0, import_code_core19.loadSettings)(workDir).raw).custom;
172505
173181
  const switchTheme = (name, persist) => {
172506
173182
  const out = applyThemeConfig({ theme: name, custom: name === "custom" ? customColorsNow() : {}, errors: [] });
172507
173183
  activeThemeName = name;
@@ -172519,7 +173195,7 @@ ${dirList}`;
172519
173195
  const w = clearThemeKeys(workDir, scope);
172520
173196
  if (!w.ok)
172521
173197
  console.log(paint.yellow(` Could not write ${w.path}: ${w.error}`));
172522
- const after = readThemeConfig((0, import_code_core18.loadSettings)(workDir).raw);
173198
+ const after = readThemeConfig((0, import_code_core19.loadSettings)(workDir).raw);
172523
173199
  const out = applyThemeConfig(after);
172524
173200
  activeThemeName = after.theme;
172525
173201
  console.log(" " + paint.green(`Theme reset: ${out.resolved}`) + paint.dim(` \xB7 removed theme/themeColors from ${settingsPath(workDir, scope)}`));
@@ -172610,7 +173286,7 @@ ${dirList}`;
172610
173286
  }
172611
173287
  const types3 = (() => {
172612
173288
  try {
172613
- return (0, import_code_core16.loadAgentTypesWithWarnings)(workDir).types.map((t2) => ({
173289
+ return (0, import_code_core17.loadAgentTypesWithWarnings)(workDir).types.map((t2) => ({
172614
173290
  name: t2.name,
172615
173291
  description: t2.description,
172616
173292
  ...t2.tools ? { tools: t2.tools } : {},
@@ -172623,7 +173299,7 @@ ${dirList}`;
172623
173299
  for (const l2 of formatPermissions({
172624
173300
  rules: permRules,
172625
173301
  workDir,
172626
- mode: (0, import_code_core18.parseMode)(agentMode),
173302
+ mode: (0, import_code_core19.parseMode)(agentMode),
172627
173303
  yolo: isYoloMode(),
172628
173304
  agents: types3,
172629
173305
  // The guard the dashboard describes is the one the handler actually runs, so the
@@ -172828,7 +173504,7 @@ ${caption}` : `[Attached: ${rel}]`;
172828
173504
  if (needsSidecar) {
172829
173505
  console.log(paint.dim(` ${resolveModelLabel(modelAlias)} can't read ${isPdf ? "PDFs" : "images"} directly \u2014 describing ${rel} with Claude Sonnet 5.5 first\u2026`));
172830
173506
  try {
172831
- const { text } = await (0, import_code_core16.describeAttachment)(isPdf ? "pdf" : "image", data, mimeType, rel);
173507
+ const { text } = await (0, import_code_core17.describeAttachment)(isPdf ? "pdf" : "image", data, mimeType, rel);
172832
173508
  content.push({ type: "text", text: `<attachment_description>
172833
173509
  ${text}
172834
173510
  </attachment_description>` });
@@ -172872,7 +173548,7 @@ ${text}
172872
173548
  process.exit(0);
172873
173549
  }
172874
173550
  case "/plugins": {
172875
- const plugins = (0, import_code_core16.loadPlugins)(workDir);
173551
+ const plugins = (0, import_code_core17.loadPlugins)(workDir);
172876
173552
  if (!plugins.length) {
172877
173553
  console.log(paint.dim(" No plugins installed. Install one: nex plugin install owner/repo"));
172878
173554
  } else {
@@ -172885,7 +173561,7 @@ ${text}
172885
173561
  }
172886
173562
  console.log();
172887
173563
  }
172888
- skills = (0, import_code_core16.loadSkills)(workDir);
173564
+ skills = (0, import_code_core17.loadSkills)(workDir);
172889
173565
  syncSlashMenu();
172890
173566
  rl.prompt();
172891
173567
  return;
@@ -173076,7 +173752,7 @@ ${text}
173076
173752
  return;
173077
173753
  }
173078
173754
  case "/handoff": {
173079
- const types3 = (0, import_code_core16.loadAgentTypesWithWarnings)(workDir).types.map((t2) => t2.name);
173755
+ const types3 = (0, import_code_core17.loadAgentTypesWithWarnings)(workDir).types.map((t2) => t2.name);
173080
173756
  const parsed = parseHandoffArgs(arg, types3);
173081
173757
  if ("error" in parsed) {
173082
173758
  console.log(paint.red(" " + parsed.error));
@@ -173109,8 +173785,8 @@ ${text}
173109
173785
  return;
173110
173786
  }
173111
173787
  const measure = measureContext(messages, parsed.all ? TOP_ALL : TOP_DEFAULT);
173112
- const window2 = (0, import_code_core16.contextWindowFor)(modelAlias);
173113
- const thresholds = (0, import_code_core16.compactionThresholds)(window2);
173788
+ const window2 = (0, import_code_core17.contextWindowFor)(modelAlias);
173789
+ const thresholds = (0, import_code_core17.compactionThresholds)(window2);
173114
173790
  for (const line of formatContextPanel({
173115
173791
  measure,
173116
173792
  window: window2,
@@ -173127,9 +173803,9 @@ ${text}
173127
173803
  }
173128
173804
  case "/skills":
173129
173805
  case "/commands": {
173130
- skills = (0, import_code_core16.loadSkills)(workDir);
173806
+ skills = (0, import_code_core17.loadSkills)(workDir);
173131
173807
  syncSlashMenu();
173132
- const invocable = (0, import_code_core16.userInvokableSkills)(skills);
173808
+ const invocable = (0, import_code_core17.userInvokableSkills)(skills);
173133
173809
  if (!invocable.length) {
173134
173810
  console.log(paint.dim(" No skills. Add .nexrall/skills/<name>/SKILL.md (or .nexrall/commands/<name>.md) to create one."));
173135
173811
  } else {
@@ -173305,15 +173981,15 @@ ${text}
173305
173981
  return;
173306
173982
  }
173307
173983
  default: {
173308
- const custom = (0, import_code_core16.findSkill)(skills, cmd);
173984
+ const custom = (0, import_code_core17.findSkill)(skills, cmd);
173309
173985
  if (!custom) {
173310
173986
  console.log(paint.red(` Unknown command: ${cmd}`));
173311
173987
  console.log(paint.dim(" Type /help for built-ins or /skills for custom ones."));
173312
173988
  rl.prompt();
173313
173989
  return;
173314
173990
  }
173315
- const expanded = (0, import_code_core16.expandSkill)(custom, arg, workDir);
173316
- const turnModel = (0, import_code_core16.resolveSubAgentModel)(custom.model, modelAlias) ?? modelAlias;
173991
+ const expanded = (0, import_code_core17.expandSkill)(custom, arg, workDir);
173992
+ const turnModel = (0, import_code_core17.resolveSubAgentModel)(custom.model, modelAlias) ?? modelAlias;
173317
173993
  const turnMode = modeLocked ? "plan" : custom.mode ?? agentMode;
173318
173994
  console.log(paint.dim(` Running /${custom.name}\u2026`));
173319
173995
  checkpoints.beginTurn(`/${custom.name} ${arg}`.trim(), messages.length);
@@ -173348,7 +174024,7 @@ ${text}
173348
174024
  }
173349
174025
  checkpoints.beginTurn(userInput, messages.length);
173350
174026
  const mentions = expandMentions(userInput, workDir, {
173351
- isDenied: (abs) => (0, import_code_core18.evaluatePermission)(permRules, "read_file", { path: abs }, workDir) === "deny"
174027
+ isDenied: (abs) => (0, import_code_core19.evaluatePermission)(permRules, "read_file", { path: abs }, workDir) === "deny"
173352
174028
  });
173353
174029
  for (const a of mentions.attachments) {
173354
174030
  console.log(paint.dim(` + ${a.kind === "dir" ? a.rel + "/" : a.rel} (${a.lines} ${a.kind === "dir" ? "entries" : "lines"}${a.truncated ? ", truncated" : ""})`));
@@ -173389,7 +174065,7 @@ ${text}
173389
174065
  const notes = cliBackgroundAgents.takeNotifications();
173390
174066
  console.log(paint.dim(` \u2699 background agent${notes.length > 1 ? "s" : ""} ${notes.map((n) => `"${n.description}"`).join(", ")} finished \u2014 continuing`));
173391
174067
  notifier.notify(bgAgentNote(notes.map((n) => n.description)));
173392
- rl.emit("line", (0, import_code_core16.formatBackgroundNotifications)(notes));
174068
+ rl.emit("line", (0, import_code_core17.formatBackgroundNotifications)(notes));
173393
174069
  }, 400);
173394
174070
  }
173395
174071
  cliBackgroundAgents.onChange(() => {
@@ -173400,6 +174076,7 @@ ${text}
173400
174076
  deliverBackgroundWhenIdle();
173401
174077
  });
173402
174078
  rl.on("close", () => {
174079
+ fileWatcher?.stop();
173403
174080
  updateTitle();
173404
174081
  if (messages.length)
173405
174082
  saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
@@ -173410,7 +174087,7 @@ ${text}
173410
174087
 
173411
174088
  // src/commands/plugin.ts
173412
174089
  var import_prompts = __toESM(require_prompts3(), 1);
173413
- var import_code_core19 = __toESM(require_dist3(), 1);
174090
+ var import_code_core20 = __toESM(require_dist3(), 1);
173414
174091
  function describeInspection(i2, name, ctx) {
173415
174092
  console.log();
173416
174093
  console.log(paint.bold(` Plugin: ${name}`) + (i2.meta.version ? paint.dim(` v${i2.meta.version}`) : ""));
@@ -173486,14 +174163,14 @@ async function pluginInstallCommand(spec, opts) {
173486
174163
  let registryName;
173487
174164
  const isShortName = /^[\w.-]+$/.test(spec.trim()) && !spec.includes("/");
173488
174165
  if (isShortName) {
173489
- const alias = (0, import_code_core19.resolveSourceAlias)(spec.trim(), process.cwd(), { includeProject: true });
174166
+ const alias = (0, import_code_core20.resolveSourceAlias)(spec.trim(), process.cwd(), { includeProject: true });
173490
174167
  if (alias) {
173491
174168
  source2 = alias.spec;
173492
174169
  console.log(paint.dim(` Source: ${alias.name} (${alias.tier}) \u2190 ${alias.spec}`));
173493
174170
  }
173494
174171
  }
173495
174172
  if (isShortName && source2 === spec) {
173496
- const entry = await (0, import_code_core19.getRegistryPlugin)(spec.trim()).catch(() => null);
174173
+ const entry = await (0, import_code_core20.getRegistryPlugin)(spec.trim()).catch(() => null);
173497
174174
  if (entry) {
173498
174175
  source2 = entry.source;
173499
174176
  registryName = entry.name;
@@ -173508,7 +174185,7 @@ async function pluginInstallCommand(spec, opts) {
173508
174185
  return;
173509
174186
  }
173510
174187
  }
173511
- const res = await (0, import_code_core19.installPlugin)(source2, {
174188
+ const res = await (0, import_code_core20.installPlugin)(source2, {
173512
174189
  scope,
173513
174190
  workDir: process.cwd(),
173514
174191
  name: opts.name ?? registryName,
@@ -173516,7 +174193,7 @@ async function pluginInstallCommand(spec, opts) {
173516
174193
  confirm: (i2, name, ctx) => confirmInstall(i2, name, opts.yes === true, ctx)
173517
174194
  });
173518
174195
  if (registryName)
173519
- (0, import_code_core19.reportInstall)(registryName);
174196
+ (0, import_code_core20.reportInstall)(registryName);
173520
174197
  console.log();
173521
174198
  console.log(paint.green(` \u2713 Installed "${res.name}" (${res.scope}) \u2192 ${res.dir}`) + (res.sha ? paint.dim(` @ ${res.sha.slice(0, 7)}`) : ""));
173522
174199
  if (res.inspection.commands.length) {
@@ -173530,7 +174207,7 @@ async function pluginInstallCommand(spec, opts) {
173530
174207
  }
173531
174208
  function pluginRemoveCommand(name, opts) {
173532
174209
  try {
173533
- const dir = (0, import_code_core19.removePlugin)(name, {
174210
+ const dir = (0, import_code_core20.removePlugin)(name, {
173534
174211
  scope: opts.project ? "project" : void 0,
173535
174212
  // no flag → search both scopes
173536
174213
  workDir: process.cwd()
@@ -173543,14 +174220,14 @@ function pluginRemoveCommand(name, opts) {
173543
174220
  }
173544
174221
  async function pluginUpdateCommand(name, opts) {
173545
174222
  const workDir = process.cwd();
173546
- const targets = name ? [name] : (0, import_code_core19.loadPlugins)(workDir).filter((p) => (0, import_code_core19.readReceipt)(p.dir)?.kind === "github").map((p) => p.name);
174223
+ const targets = name ? [name] : (0, import_code_core20.loadPlugins)(workDir).filter((p) => (0, import_code_core20.readReceipt)(p.dir)?.kind === "github").map((p) => p.name);
173547
174224
  if (!targets.length) {
173548
174225
  console.log(paint.dim(" No updatable plugins (only GitHub-installed plugins with receipts can update)."));
173549
174226
  return;
173550
174227
  }
173551
174228
  for (const t2 of targets) {
173552
174229
  try {
173553
- const res = await (0, import_code_core19.updatePlugin)(t2, {
174230
+ const res = await (0, import_code_core20.updatePlugin)(t2, {
173554
174231
  workDir,
173555
174232
  confirm: (i2, n, ctx) => confirmInstall(i2, n, opts.yes === true, ctx)
173556
174233
  });
@@ -173567,7 +174244,7 @@ function badgeFor(p) {
173567
174244
  }
173568
174245
  async function pluginSearchCommand(query) {
173569
174246
  try {
173570
- const results = await (0, import_code_core19.searchRegistry)(query);
174247
+ const results = await (0, import_code_core20.searchRegistry)(query);
173571
174248
  if (!results.length) {
173572
174249
  console.log(paint.dim(query ? ` No plugins matching "${query}".` : " Registry is empty."));
173573
174250
  return;
@@ -173590,7 +174267,7 @@ async function pluginSearchCommand(query) {
173590
174267
  }
173591
174268
  async function pluginInfoCommand(name) {
173592
174269
  try {
173593
- const p = await (0, import_code_core19.getRegistryPlugin)(name);
174270
+ const p = await (0, import_code_core20.getRegistryPlugin)(name);
173594
174271
  if (!p) {
173595
174272
  console.error(paint.red(` \u2717 "${name}" not found in the registry.`));
173596
174273
  process.exitCode = 1;
@@ -173626,7 +174303,7 @@ async function pluginInfoCommand(name) {
173626
174303
  }
173627
174304
  }
173628
174305
  function pluginListCommand() {
173629
- const plugins = (0, import_code_core19.loadPlugins)(process.cwd());
174306
+ const plugins = (0, import_code_core20.loadPlugins)(process.cwd());
173630
174307
  if (!plugins.length) {
173631
174308
  console.log(paint.dim(" No plugins installed."));
173632
174309
  console.log(paint.dim(" Install one: nex plugin install owner/repo"));
@@ -173635,10 +174312,10 @@ function pluginListCommand() {
173635
174312
  console.log();
173636
174313
  console.log(paint.bold(" Installed plugins:"));
173637
174314
  for (const p of plugins) {
173638
- const receipt = (0, import_code_core19.readReceipt)(p.dir);
174315
+ const receipt = (0, import_code_core20.readReceipt)(p.dir);
173639
174316
  const src = receipt ? paint.dim(` \u2190 ${receipt.source}`) : paint.dim(" (manual)");
173640
174317
  const at = receipt?.sha ? paint.dim(` @ ${receipt.sha.slice(0, 7)}`) : "";
173641
- const insp = (0, import_code_core19.inspectPluginDir)(p.dir);
174318
+ const insp = (0, import_code_core20.inspectPluginDir)(p.dir);
173642
174319
  const runs2 = insp.hasHooks || insp.hasMcp ? paint.yellow(` \u26A0 ${[insp.hasHooks && "hooks", insp.hasMcp && "mcp"].filter(Boolean).join("+")}`) : "";
173643
174320
  console.log(
173644
174321
  " " + paint.cyan(p.name.padEnd(24)) + paint.dim(`${(p.version ?? "").padEnd(10)}${p.scope.padEnd(9)}`) + src + at + runs2
@@ -173648,8 +174325,8 @@ function pluginListCommand() {
173648
174325
  }
173649
174326
  function pluginSourceListCommand() {
173650
174327
  const workDir = process.cwd();
173651
- const sources = (0, import_code_core19.loadDeclaredSources)(workDir, { includeProject: true });
173652
- const patterns = (0, import_code_core19.allowedSourcePatterns)();
174328
+ const sources = (0, import_code_core20.loadDeclaredSources)(workDir, { includeProject: true });
174329
+ const patterns = (0, import_code_core20.allowedSourcePatterns)();
173653
174330
  if (patterns) {
173654
174331
  console.log();
173655
174332
  console.log(paint.bold(" Organisation policy: ") + paint.dim(`installs restricted to ${patterns.join(", ")}`));
@@ -173664,7 +174341,7 @@ function pluginSourceListCommand() {
173664
174341
  console.log(paint.bold(" Declared plugin sources:"));
173665
174342
  for (const s2 of sources) {
173666
174343
  const tier2 = s2.tier === "project" ? paint.yellow(s2.tier.padEnd(9)) : paint.dim(s2.tier.padEnd(9));
173667
- const blocked = !(0, import_code_core19.isSourceAllowed)(s2.spec, patterns) ? paint.red(" \u2717 blocked by policy") : "";
174344
+ const blocked = !(0, import_code_core20.isSourceAllowed)(s2.spec, patterns) ? paint.red(" \u2717 blocked by policy") : "";
173668
174345
  console.log(" " + paint.cyan(s2.name.padEnd(20)) + tier2 + paint.white(s2.spec) + blocked);
173669
174346
  }
173670
174347
  console.log();
@@ -173674,17 +174351,17 @@ function pluginSourceListCommand() {
173674
174351
  }
173675
174352
  function pluginSourceAddCommand(name, spec, opts) {
173676
174353
  try {
173677
- (0, import_code_core19.parsePluginSource)(spec);
173678
- const patterns = (0, import_code_core19.allowedSourcePatterns)();
173679
- if (patterns && !(0, import_code_core19.isSourceAllowed)(spec, patterns)) {
174354
+ (0, import_code_core20.parsePluginSource)(spec);
174355
+ const patterns = (0, import_code_core20.allowedSourcePatterns)();
174356
+ if (patterns && !(0, import_code_core20.isSourceAllowed)(spec, patterns)) {
173680
174357
  console.error(paint.red(` \u2717 "${spec}" is not permitted by your organisation's policy.`));
173681
174358
  console.error(paint.dim(` Allowed: ${patterns.join(", ")}`));
173682
174359
  process.exitCode = 1;
173683
174360
  return;
173684
174361
  }
173685
174362
  const scope = opts.project ? "project" : "user";
173686
- const file = (0, import_code_core19.sourcesFileFor)(scope, process.cwd());
173687
- (0, import_code_core19.writeDeclaredSource)(file, name, spec);
174363
+ const file = (0, import_code_core20.sourcesFileFor)(scope, process.cwd());
174364
+ (0, import_code_core20.writeDeclaredSource)(file, name, spec);
173688
174365
  console.log();
173689
174366
  console.log(paint.green(` \u2713 Declared "${name}" \u2192 ${spec}`) + paint.dim(` (${scope})`));
173690
174367
  console.log(paint.dim(` ${file}`));
@@ -173698,13 +174375,13 @@ function pluginSourceAddCommand(name, spec, opts) {
173698
174375
  function pluginSourceRemoveCommand(name, opts) {
173699
174376
  const workDir = process.cwd();
173700
174377
  const scope = opts.project ? "project" : "user";
173701
- const file = (0, import_code_core19.sourcesFileFor)(scope, workDir);
173702
- if ((0, import_code_core19.removeDeclaredSource)(file, name)) {
174378
+ const file = (0, import_code_core20.sourcesFileFor)(scope, workDir);
174379
+ if ((0, import_code_core20.removeDeclaredSource)(file, name)) {
173703
174380
  console.log(paint.green(` \u2713 Removed source "${name}" (${scope})`));
173704
174381
  console.log(paint.dim(" Plugins already installed from it are untouched \u2014 remove them with nex plugin remove <name>"));
173705
174382
  return;
173706
174383
  }
173707
- const found = (0, import_code_core19.resolveSourceAlias)(name, workDir, { includeProject: true });
174384
+ const found = (0, import_code_core20.resolveSourceAlias)(name, workDir, { includeProject: true });
173708
174385
  if (found) {
173709
174386
  console.error(paint.red(` \u2717 "${name}" is declared in the ${found.tier} tier, not ${scope}.`));
173710
174387
  console.error(paint.dim(` ${found.file}`));
@@ -173719,7 +174396,7 @@ function pluginSourceRemoveCommand(name, opts) {
173719
174396
  }
173720
174397
 
173721
174398
  // src/index.ts
173722
- var import_code_core21 = __toESM(require_dist3(), 1);
174399
+ var import_code_core22 = __toESM(require_dist3(), 1);
173723
174400
 
173724
174401
  // src/commands/bgJobs.ts
173725
174402
  import * as fs26 from "node:fs";
@@ -173948,7 +174625,7 @@ var program2 = new Command();
173948
174625
  program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version(NEX_VERSION).enablePositionalOptions();
173949
174626
  var auth = program2.command("auth").description("Login to your Nexrall account").action(authCommand);
173950
174627
  auth.command("status").description("Show whether you are logged in (exit 0 = yes, 1 = no). --json for scripts; the token is never printed").option("--json", "Machine-readable output").action((opts) => {
173951
- const r2 = authStatusReport((0, import_code_core21.loadAuth)(), process.env);
174628
+ const r2 = authStatusReport((0, import_code_core22.loadAuth)(), process.env);
173952
174629
  if (opts.json)
173953
174630
  console.log(JSON.stringify(r2));
173954
174631
  else if (!r2.loggedIn)
@@ -174194,8 +174871,8 @@ program2.command("sessions").description("List, export, import, or delete saved
174194
174871
  console.log(paint.dim(" nex sessions --events <id> [--json] to follow its event stream"));
174195
174872
  console.log();
174196
174873
  });
174197
- program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5.5 (fast)").option("-u, --ultra", "Use Claude Fable 5.1 (most powerful)").option("-y, --yolo", "Auto-approve all tool calls (no permission prompts)").option("-d, --dir <path>", "Set working directory (default: current directory)").option("-m, --model <name>", "Explicit model, e.g. claude-opus-5-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-c, --continue", "Continue the latest conversation in this folder").option("--plan", "Start in plan mode (read-only; /mode or Shift+Tab can leave it)").option("--readonly", "Read-only for the whole session: plan mode that cannot be switched off").option("--auto", "Start in auto mode (the default \u2014 edits and commands run without asking)").option("--mode <mode>", "Starting permission mode: ask | edit | auto | plan").option("--fullscreen", "Draw the whole session on the terminal's alternate screen: the transcript re-wraps on resize and PgUp/PgDn page through it (no terminal scrollback while it is on; /fullscreen toggles it)").option("--bg", "Run the prompt in the background (headless); returns a job id. See: nex jobs | logs | attach | kill").option("--debug", "Write request timing, retries and errors to a debug log file").option("--no-color", "Disable colored output (also honours NO_COLOR)").option("-w, --worktree [name]", "Isolate this session in its own worktree \u2014 a separate directory (and, in a git repo, its own branch) so it can never overwrite files another session (or you) are touching in the main checkout. Omit a name for an auto-generated one; pass an existing name to resume that same worktree.").option("-e, --effort <level>", "Thinking effort \u2014 real levels depend on the model (e.g. DeepSeek: low|high|max, Qwen: low|high); run `nex --model <id>` then `/effort` with no argument to see a model's scale").option("--no-banner", "Skip the ASCII banner (useful in scripts/pipes)").option("--output-format <fmt>", "One-shot output format: text | json | stream-json (implies auto-approve)").option("--audit [path]", "Log every tool call (and refusal) to a JSONL trail (default: .nexrall/audit/<session>.jsonl)").option("--permission-mode <mode>", "Permission mode: ask | edit | auto | plan (also Claude Code names: default | acceptEdits | bypassPermissions). With --output-format, anything that would prompt is DENIED instead of auto-approved").option("--allowedTools, --allowed-tools <tools...>", 'Tools/rules to allow without asking, e.g. "Bash(git *)" Edit (comma or space separated; put the prompt BEFORE this flag)').option("--disallowedTools, --disallowed-tools <tools...>", 'Tools/rules to deny, e.g. "Bash(rm *)" (deny always wins)').option("--append-system-prompt <text>", "Extra instructions appended to the project brief for this run").option("--max-budget-usd <amount>", "Stop a one-shot run once it has spent this many dollars (needs --output-format)").option("--mcp-config <configs...>", 'Load MCP servers from JSON files or inline JSON ({"mcpServers":{\u2026}}) for this run').option("--strict-mcp-config", "With --mcp-config: use ONLY those servers, ignoring ~/.nexrall/mcp.json and the project one").option("--add-dir <dirs...>", "Extra directories the agent may read and edit besides the project folder").option("--input-format <format>", "text (default) | stream-json \u2014 read the prompt as Claude-Code-style NDJSON user messages on stdin").option("--json-schema <schema>", 'JSON Schema (inline or file) the final answer must satisfy; needs --output-format. Result gains "structured_output"; exit 3 if it does not validate').option("--no-session-persistence", "Do not save this run to disk (needs --output-format; it cannot be resumed)").option("--max-turns <n>", "Stop a headless run after N agent rounds (needs --output-format; exit 4 when it hits the limit)").option("--append-system-prompt-file <path>", "Like --append-system-prompt, read from a file").option("--system-prompt <text>", "REPLACE the project brief (nexrall.md + memory + global instructions) with this text for this run").option("--system-prompt-file <path>", "Like --system-prompt, read from a file").option("--verbose", "Print progress detail (tool calls, timings, usage) to stderr while a one-shot run works").option("--include-partial-messages", "Stream model deltas as they arrive (needs --output-format stream-json)").option("--replay-user-messages", "Echo each stdin message back on stdout as it is consumed (needs --input-format stream-json --output-format stream-json)").option("--agents <json-or-file>", 'Define extra sub-agents for this run: a JSON object { "name": { "description", "prompt", "tools", "model" } } or @file.json').option("--agent <name>", "Run this session AS that agent (its prompt, tools and model), from .nexrall/agents, a plugin, --agents or a builtin").option("--bare", "Start without discovery: no nexrall.md/memory, skills, plugins, hooks or MCP config files").option("--safe-mode", "Same as --bare (Claude Code's troubleshooting name)").option("--tools <list>", 'Exactly these built-in tools ("Bash,Read,Edit", "default", or "" for none); the rest are removed, not just unapproved').option("--session-id <id>", "Use this id for a NEW conversation (letters, digits, - _), so a script can find it again").option("-n, --name <name>", "Name the conversation; `nex -r <name>` resumes it by that name").option("--fork-session", "With --resume/--continue: branch into a new session instead of continuing the original").option("--fallback-model <name>", "If the main model is overloaded or unreachable, retry the turn once on this model (needs --output-format)").option("--settings <file|json>", "Settings (permissions, hooks\u2026) for this run only: a JSON file or an inline JSON object; merged over the user/project settings").option("--debug-file <path>", "Write the --debug diagnostics log to this file (implies --debug)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
174198
- if (!(0, import_code_core20.isAuthenticated)()) {
174874
+ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, --turbo", "Use Claude Sonnet 5.5 (fast)").option("-u, --ultra", "Use Claude Fable 5.1 (most powerful)").option("-y, --yolo", "Auto-approve all tool calls (no permission prompts)").option("-d, --dir <path>", "Set working directory (default: current directory)").option("-m, --model <name>", "Explicit model, e.g. claude-opus-5-5 | gpt-5.4 | gpt-4.1").option("-r, --resume [id]", "Resume last session, or a specific session id").option("-c, --continue", "Continue the latest conversation in this folder").option("--plan", "Start in plan mode (read-only; /mode or Shift+Tab can leave it)").option("--readonly", "Read-only for the whole session: plan mode that cannot be switched off").option("--auto", "Start in auto mode (the default \u2014 edits and commands run without asking)").option("--mode <mode>", "Starting permission mode: ask | edit | auto | plan").option("--fullscreen", "Draw the whole session on the terminal's alternate screen: the transcript re-wraps on resize and PgUp/PgDn page through it (no terminal scrollback while it is on; /fullscreen toggles it)").option("--bg", "Run the prompt in the background (headless); returns a job id. See: nex jobs | logs | attach | kill").option("--debug", "Write request timing, retries and errors to a debug log file").option("--no-color", "Disable colored output (also honours NO_COLOR)").option("-w, --worktree [name]", "Isolate this session in its own worktree \u2014 a separate directory (and, in a git repo, its own branch) so it can never overwrite files another session (or you) are touching in the main checkout. Omit a name for an auto-generated one; pass an existing name to resume that same worktree.").option("-e, --effort <level>", "Thinking effort \u2014 real levels depend on the model (e.g. DeepSeek: low|high|max, Qwen: low|high); run `nex --model <id>` then `/effort` with no argument to see a model's scale").option("--no-banner", "Skip the ASCII banner (useful in scripts/pipes)").option("--output-format <fmt>", "One-shot output format: text | json | stream-json (implies auto-approve)").option("--audit [path]", "Log every tool call (and refusal) to a JSONL trail (default: .nexrall/audit/<session>.jsonl)").option("--permission-mode <mode>", "Permission mode: ask | edit | auto | plan (also Claude Code names: default | acceptEdits | bypassPermissions). With --output-format, anything that would prompt is DENIED instead of auto-approved").option("--allowedTools, --allowed-tools <tools...>", 'Tools/rules to allow without asking, e.g. "Bash(git *)" Edit (comma or space separated; put the prompt BEFORE this flag)').option("--disallowedTools, --disallowed-tools <tools...>", 'Tools/rules to deny, e.g. "Bash(rm *)" (deny always wins)').option("--append-system-prompt <text>", "Extra instructions appended to the project brief for this run").option("--max-budget-usd <amount>", "Stop a one-shot run once it has spent this many dollars (needs --output-format)").option("--mcp-config <configs...>", 'Load MCP servers from JSON files or inline JSON ({"mcpServers":{\u2026}}) for this run').option("--strict-mcp-config", "With --mcp-config: use ONLY those servers, ignoring ~/.nexrall/mcp.json and the project one").option("--add-dir <dirs...>", "Extra directories the agent may read and edit besides the project folder").option("--input-format <format>", "text (default) | stream-json \u2014 read the prompt as Claude-Code-style NDJSON user messages on stdin").option("--json-schema <schema>", 'JSON Schema (inline or file) the final answer must satisfy; needs --output-format. Result gains "structured_output"; exit 3 if it does not validate').option("--no-session-persistence", "Do not save this run to disk (needs --output-format; it cannot be resumed)").option("--max-turns <n>", "Stop a headless run after N agent rounds (needs --output-format; exit 4 when it hits the limit)").option("--permission-prompt-tool <t>", "MCP tool (mcp__server__tool) that answers permission prompts in non-interactive runs \u2014 instead of nex denying them").option("--append-system-prompt-file <path>", "Like --append-system-prompt, read from a file").option("--system-prompt <text>", "REPLACE the project brief (nexrall.md + memory + global instructions) with this text for this run").option("--system-prompt-file <path>", "Like --system-prompt, read from a file").option("--verbose", "Print progress detail (tool calls, timings, usage) to stderr while a one-shot run works").option("--include-partial-messages", "Stream model deltas as they arrive (needs --output-format stream-json)").option("--replay-user-messages", "Echo each stdin message back on stdout as it is consumed (needs --input-format stream-json --output-format stream-json)").option("--agents <json-or-file>", 'Define extra sub-agents for this run: a JSON object { "name": { "description", "prompt", "tools", "model" } } or @file.json').option("--agent <name>", "Run this session AS that agent (its prompt, tools and model), from .nexrall/agents, a plugin, --agents or a builtin").option("--bare", "Start without discovery: no nexrall.md/memory, skills, plugins, hooks or MCP config files").option("--safe-mode", "Same as --bare (Claude Code's troubleshooting name)").option("--tools <list>", 'Exactly these built-in tools ("Bash,Read,Edit", "default", or "" for none); the rest are removed, not just unapproved').option("--session-id <id>", "Use this id for a NEW conversation (letters, digits, - _), so a script can find it again").option("-n, --name <name>", "Name the conversation; `nex -r <name>` resumes it by that name").option("--fork-session", "With --resume/--continue: branch into a new session instead of continuing the original").option("--fallback-model <name>", "If the main model is overloaded or unreachable, retry the turn once on this model (needs --output-format)").option("--settings <file|json>", "Settings (permissions, hooks\u2026) for this run only: a JSON file or an inline JSON object; merged over the user/project settings").option("--debug-file <path>", "Write the --debug diagnostics log to this file (implies --debug)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
174875
+ if (!(0, import_code_core21.isAuthenticated)()) {
174199
174876
  console.error("Not logged in. Run: nex auth");
174200
174877
  process.exit(1);
174201
174878
  }
@@ -174261,11 +174938,11 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174261
174938
  console.error(st.error);
174262
174939
  process.exit(1);
174263
174940
  }
174264
- (0, import_code_core21.setSessionSettings)(st.value);
174941
+ (0, import_code_core22.setSessionSettings)(st.value);
174265
174942
  }
174266
174943
  const allowRules = parseToolList(options.allowedTools);
174267
174944
  const denyRules = parseToolList(options.disallowedTools);
174268
- (0, import_code_core21.setSessionRules)({ allow: allowRules, deny: denyRules });
174945
+ (0, import_code_core22.setSessionRules)({ allow: allowRules, deny: denyRules });
174269
174946
  let budgetUsd;
174270
174947
  if (options.maxBudgetUsd !== void 0) {
174271
174948
  const b = parseBudget(options.maxBudgetUsd);
@@ -174286,7 +174963,16 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174286
174963
  }
174287
174964
  maxTurns = t2.value;
174288
174965
  }
174289
- const headlessErr = checkHeadlessOnly({ outputFormat: options.outputFormat, budget: budgetUsd, noSessionPersistence: options.sessionPersistence === false, maxTurns });
174966
+ let permissionPromptTool;
174967
+ if (options.permissionPromptTool !== void 0) {
174968
+ const t2 = normalizePermissionPromptTool(options.permissionPromptTool);
174969
+ if (!t2.ok) {
174970
+ console.error(t2.error);
174971
+ process.exit(1);
174972
+ }
174973
+ permissionPromptTool = t2.value;
174974
+ }
174975
+ const headlessErr = checkHeadlessOnly({ outputFormat: options.outputFormat, budget: budgetUsd, noSessionPersistence: options.sessionPersistence === false, maxTurns, permissionPromptTool });
174290
174976
  if (headlessErr) {
174291
174977
  console.error(headlessErr);
174292
174978
  process.exit(1);
@@ -174310,8 +174996,8 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174310
174996
  }
174311
174997
  let selectedAgent;
174312
174998
  if (options.agent) {
174313
- const catalogue = (0, import_code_core21.loadAgentTypesWithWarnings)(process.cwd(), extraTypes).types;
174314
- selectedAgent = (0, import_code_core21.findAgentType)(catalogue, String(options.agent));
174999
+ const catalogue = (0, import_code_core22.loadAgentTypesWithWarnings)(process.cwd(), extraTypes).types;
175000
+ selectedAgent = (0, import_code_core22.findAgentType)(catalogue, String(options.agent));
174315
175001
  if (!selectedAgent) {
174316
175002
  console.error(`Unknown agent "${options.agent}". Available: ${catalogue.map((t2) => t2.name).join(", ")}`);
174317
175003
  process.exit(1);
@@ -174333,7 +175019,7 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174333
175019
  }
174334
175020
  let onlyTools;
174335
175021
  if (options.tools !== void 0) {
174336
- const t2 = parseToolsFlag(options.tools === true ? "" : options.tools, (0, import_code_core21.knownToolNames)());
175022
+ const t2 = parseToolsFlag(options.tools === true ? "" : options.tools, (0, import_code_core22.knownToolNames)());
174337
175023
  if (!t2.ok) {
174338
175024
  console.error(t2.error);
174339
175025
  process.exit(1);
@@ -174475,7 +175161,7 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174475
175161
  }
174476
175162
  effort = lvl;
174477
175163
  }
174478
- await startChatSession({ model, workDir, prompt: prompt2, resume, continueLast: !!options.continue, initialMode: modeRes.mode, lockMode: modeRes.locked, debug: !!options.debug || debugFile !== void 0, debugFile, fallbackModel, maxTurns, onlyTools, newSessionId, sessionName, forkSession: !!options.forkSession, stdinText, stdinFollowUps, showBanner, outputFormat, effort, audit: options.audit, worktree, fullscreen: !!options.fullscreen, strictPermissions: options.permissionMode !== void 0, budgetUsd, appendSystemPrompt, mcpOverride, strictMcp: !!options.strictMcpConfig, systemPrompt: sysPrompt.value, verbose: !!options.verbose, includePartialMessages: !!options.includePartialMessages, agents: agentsParsed.value, selectedAgent: selectedAgent ? { name: selectedAgent.name, prompt: selectedAgent.prompt, tools: selectedAgent.tools, model: selectedAgent.model } : void 0, replayUserMessages: !!options.replayUserMessages, inputFormatWasStreamJson: options.inputFormat !== void 0 && String(options.inputFormat).toLowerCase() === "stream-json", bare: !!(options.bare || options.safeMode), noSessionPersistence: options.sessionPersistence === false, addDirs: addDirs.value, jsonSchema });
175164
+ await startChatSession({ model, workDir, prompt: prompt2, resume, continueLast: !!options.continue, initialMode: modeRes.mode, lockMode: modeRes.locked, debug: !!options.debug || debugFile !== void 0, debugFile, fallbackModel, maxTurns, onlyTools, newSessionId, sessionName, forkSession: !!options.forkSession, stdinText, stdinFollowUps, showBanner, outputFormat, effort, audit: options.audit, worktree, fullscreen: !!options.fullscreen, strictPermissions: options.permissionMode !== void 0, budgetUsd, appendSystemPrompt, mcpOverride, strictMcp: !!options.strictMcpConfig, systemPrompt: sysPrompt.value, verbose: !!options.verbose, includePartialMessages: !!options.includePartialMessages, agents: agentsParsed.value, selectedAgent: selectedAgent ? { name: selectedAgent.name, prompt: selectedAgent.prompt, tools: selectedAgent.tools, model: selectedAgent.model } : void 0, replayUserMessages: !!options.replayUserMessages, inputFormatWasStreamJson: options.inputFormat !== void 0 && String(options.inputFormat).toLowerCase() === "stream-json", bare: !!(options.bare || options.safeMode), noSessionPersistence: options.sessionPersistence === false, addDirs: addDirs.value, jsonSchema, permissionPromptTool });
174479
175165
  });
174480
175166
  program2.parse();
174481
175167
  /*! Bundled license information: