nexrall-code 0.5.168 → 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 +425 -208
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -106914,7 +106914,7 @@ var require_loop = __commonJS({
106914
106914
  exports2.parsePromptHookReply = parsePromptHookReply;
106915
106915
  exports2.classifyStopFailure = classifyStopFailure;
106916
106916
  exports2.runLifecycleHooks = runLifecycleHooks;
106917
- exports2.runPermissionRequestHooks = runPermissionRequestHooks2;
106917
+ exports2.runPermissionRequestHooks = runPermissionRequestHooks3;
106918
106918
  exports2.fireNotificationHooks = fireNotificationHooks2;
106919
106919
  exports2.fireManualPostCompactHook = fireManualPostCompactHook2;
106920
106920
  exports2.fireManualPreCompactHook = fireManualPreCompactHook2;
@@ -107030,8 +107030,42 @@ var require_loop = __commonJS({
107030
107030
  ...extra
107031
107031
  ];
107032
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
+ }
107033
107039
  return merged;
107034
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
+ }
107035
107069
  function spawnHook(command, opts) {
107036
107070
  return new Promise((resolve11) => {
107037
107071
  let stdout = "";
@@ -107447,7 +107481,7 @@ ${out.output}` : `Error: ${out.error}` : out.output ?? "",
107447
107481
  }
107448
107482
  return outcome;
107449
107483
  }
107450
- async function runPermissionRequestHooks2(workDir, tool, input, sessionId = "") {
107484
+ async function runPermissionRequestHooks3(workDir, tool, input, sessionId = "") {
107451
107485
  try {
107452
107486
  const items = loadHooks(workDir).PermissionRequest;
107453
107487
  if (!items?.length)
@@ -109562,7 +109596,8 @@ ${ctx}
109562
109596
  }
109563
109597
  const prefetchSafe = prefetchOn && (0, toolPrefetch_1.isPrefetchSafeMessage)(toolUseBlocks.map((b) => b.name));
109564
109598
  const toolResults = await Promise.all(toolUseBlocks.map(async (block) => {
109565
- const { id, name, input } = block;
109599
+ const { id, name } = block;
109600
+ let input = block.input;
109566
109601
  const toolMeta = { id };
109567
109602
  options.onToolUse(name, input, void 0, toolMeta);
109568
109603
  let result;
@@ -109593,7 +109628,31 @@ ${ctx}
109593
109628
  let permitted;
109594
109629
  let deniedReason = null;
109595
109630
  try {
109596
- 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
+ }
109597
109656
  } catch (err) {
109598
109657
  permitted = false;
109599
109658
  if (err instanceof ToolNotAllowedError)
@@ -109891,6 +109950,7 @@ var require_lifecycleHost = __commonJS({
109891
109950
  Object.defineProperty(exports2, "__esModule", { value: true });
109892
109951
  exports2.fireInstructionsLoadedHooks = fireInstructionsLoadedHooks2;
109893
109952
  exports2.startFileChangedWatcher = startFileChangedWatcher2;
109953
+ exports2.filesChangedSinceSnapshot = filesChangedSinceSnapshot;
109894
109954
  var fs28 = __importStar(__require("fs"));
109895
109955
  var path28 = __importStar(__require("path"));
109896
109956
  var loop_1 = require_loop();
@@ -109953,8 +110013,63 @@ var require_lifecycleHost = __commonJS({
109953
110013
  }
109954
110014
  };
109955
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);
109956
110030
  const pending = /* @__PURE__ */ new Map();
109957
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
+ };
109958
110073
  let watcher = null;
109959
110074
  try {
109960
110075
  watcher = fs28.watch(workDir, { persistent: false }, (_eventType, filename) => {
@@ -109963,52 +110078,25 @@ var require_lifecycleHost = __commonJS({
109963
110078
  const name = filename ? path28.basename(String(filename)) : "";
109964
110079
  if (!name || !watched().has(name))
109965
110080
  return;
109966
- const prev = pending.get(name);
109967
- if (prev)
109968
- clearTimeout(prev);
109969
- pending.set(name, setTimeout(() => {
109970
- pending.delete(name);
109971
- let exists = true;
109972
- try {
109973
- exists = fs28.existsSync(path28.join(workDir, name));
109974
- } catch {
109975
- exists = false;
109976
- }
109977
- const event = !exists ? "unlink" : known.has(name) ? "change" : "add";
109978
- if (exists)
109979
- known.add(name);
109980
- else
109981
- known.delete(name);
109982
- void fireChanged(workDir, sessionId, name, event, items, dynamicNames.has(name)).then((o) => {
109983
- if (!o)
109984
- return;
109985
- if (o.systemMessage && opts?.onSystemMessage) {
109986
- try {
109987
- opts.onSystemMessage(o.systemMessage);
109988
- } catch {
109989
- }
109990
- }
109991
- if (o.watchPaths) {
109992
- dynamicNames.clear();
109993
- for (const p of o.watchPaths) {
109994
- const n = isWatchableName(path28.basename(p)) ? path28.basename(p) : "";
109995
- if (n)
109996
- dynamicNames.add(n);
109997
- }
109998
- seedKnown(dynamicNames);
109999
- }
110000
- }).catch(() => {
110001
- });
110002
- }, 250));
110081
+ scheduleFire(name);
110003
110082
  });
110004
110083
  watcher.on("error", () => {
110005
110084
  });
110006
110085
  } catch {
110007
110086
  return null;
110008
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;
110009
110095
  return {
110010
110096
  stop() {
110011
110097
  closed = true;
110098
+ if (reconcileTimer)
110099
+ clearTimeout(reconcileTimer);
110012
110100
  for (const t2 of pending.values())
110013
110101
  clearTimeout(t2);
110014
110102
  pending.clear();
@@ -110026,12 +110114,20 @@ var require_lifecycleHost = __commonJS({
110026
110114
  dynamicNames.add(n);
110027
110115
  }
110028
110116
  seedKnown(dynamicNames);
110117
+ snapshot(dynamicNames);
110029
110118
  },
110030
110119
  watchedFiles() {
110031
110120
  return [...watched()].sort();
110032
110121
  }
110033
110122
  };
110034
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
+ }
110035
110131
  async function fireChanged(workDir, sessionId, name, event, items, isDynamic) {
110036
110132
  const abs = path28.join(workDir, name);
110037
110133
  try {
@@ -146132,7 +146228,7 @@ var {
146132
146228
  } = import_index.default;
146133
146229
 
146134
146230
  // src/index.ts
146135
- var import_code_core20 = __toESM(require_dist3(), 1);
146231
+ var import_code_core21 = __toESM(require_dist3(), 1);
146136
146232
  import * as path27 from "path";
146137
146233
  import * as fs27 from "fs";
146138
146234
  import { createRequire as createRequire2 } from "module";
@@ -152041,7 +152137,7 @@ function confirmMatches(env4, typed) {
152041
152137
  }
152042
152138
 
152043
152139
  // src/commands/chat.ts
152044
- var import_code_core16 = __toESM(require_dist3(), 1);
152140
+ var import_code_core17 = __toESM(require_dist3(), 1);
152045
152141
  import * as os15 from "os";
152046
152142
  import * as crypto7 from "crypto";
152047
152143
  import { createRequire } from "module";
@@ -152951,8 +153047,82 @@ async function askFlow(req) {
152951
153047
  return await choose({ title: `Allow ${tool}?`, risk: riskOf(tool, input), alwaysLabel: null }) !== "n";
152952
153048
  }
152953
153049
 
152954
- // 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
152955
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);
152956
153126
  import * as path7 from "path";
152957
153127
  var _state;
152958
153128
  var _sinks = /* @__PURE__ */ new Map();
@@ -152972,7 +153142,7 @@ function currentAudit() {
152972
153142
  const target = resolveAuditPath(_state);
152973
153143
  let sink = _sinks.get(target);
152974
153144
  if (!sink) {
152975
- sink = (0, import_code_core7.createJsonlAuditSink)(target);
153145
+ sink = (0, import_code_core8.createJsonlAuditSink)(target);
152976
153146
  _sinks.set(target, sink);
152977
153147
  }
152978
153148
  const cfg = {
@@ -152985,14 +153155,14 @@ function currentAudit() {
152985
153155
  // request that caused it. Same trap as @nexrall/agent's and Desktop's.
152986
153156
  sessionId: _state.getSessionId()
152987
153157
  };
152988
- const email = (0, import_code_core7.loadAuth)()?.email;
153158
+ const email = (0, import_code_core8.loadAuth)()?.email;
152989
153159
  if (email)
152990
153160
  cfg.userId = email;
152991
153161
  return cfg;
152992
153162
  }
152993
153163
 
152994
153164
  // src/commands/chat.ts
152995
- var import_code_core17 = __toESM(require_dist3(), 1);
153165
+ var import_code_core18 = __toESM(require_dist3(), 1);
152996
153166
 
152997
153167
  // src/headlessFlags.ts
152998
153168
  import * as fs7 from "fs";
@@ -153138,6 +153308,8 @@ function checkHeadlessOnly(f3) {
153138
153308
  return "--no-session-persistence only works with --output-format (a one-shot, non-interactive run).";
153139
153309
  if (f3.maxTurns !== void 0)
153140
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).";
153141
153313
  return null;
153142
153314
  }
153143
153315
  function headlessVerdict(i2) {
@@ -153326,6 +153498,15 @@ function readPromptFile(file, cwd2, flag = "--append-system-prompt-file") {
153326
153498
  return { ok: false, error: `${flag}: "${file}" is empty` };
153327
153499
  return { ok: true, value: text.trim() };
153328
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
+ }
153329
153510
  var CLAUDE_TOOL_ALIASES = {
153330
153511
  Read: ["read_file", "list_directory", "search_files", "glob", "notebook_read"],
153331
153512
  Edit: ["edit_file", "multi_edit", "notebook_edit"],
@@ -153532,7 +153713,7 @@ function parseAgentsArg(raw, cwd2) {
153532
153713
  }
153533
153714
 
153534
153715
  // src/commands/chat.ts
153535
- var import_code_core18 = __toESM(require_dist3(), 1);
153716
+ var import_code_core19 = __toESM(require_dist3(), 1);
153536
153717
 
153537
153718
  // src/debugLog.ts
153538
153719
  import fs8 from "fs";
@@ -153579,14 +153760,14 @@ function debugLog(event, fields) {
153579
153760
  }
153580
153761
 
153581
153762
  // src/commands/doctor.ts
153582
- var import_code_core9 = __toESM(require_dist3(), 1);
153763
+ var import_code_core10 = __toESM(require_dist3(), 1);
153583
153764
  import fs10 from "fs";
153584
153765
  import os4 from "os";
153585
153766
  import path11 from "path";
153586
153767
  import { spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
153587
153768
 
153588
153769
  // src/commands/sessionTools.ts
153589
- var import_code_core8 = __toESM(require_dist3(), 1);
153770
+ var import_code_core9 = __toESM(require_dist3(), 1);
153590
153771
  import { spawnSync, execFileSync } from "child_process";
153591
153772
  import fs9 from "fs";
153592
153773
  import path10 from "path";
@@ -153635,7 +153816,7 @@ function nextMode(current) {
153635
153816
  return MODE_CYCLE[(i2 + 1) % MODE_CYCLE.length];
153636
153817
  }
153637
153818
  function textOf(m2) {
153638
- 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();
153639
153820
  }
153640
153821
  function lastAssistantText(messages) {
153641
153822
  for (let i2 = messages.length - 1; i2 >= 0; i2--) {
@@ -153661,7 +153842,7 @@ function exportMarkdown(messages, meta) {
153661
153842
  for (const m2 of messages) {
153662
153843
  const parts = [];
153663
153844
  for (const b of m2.content) {
153664
- if ((0, import_code_core8.isRuntimeContextBlock)(b))
153845
+ if ((0, import_code_core9.isRuntimeContextBlock)(b))
153665
153846
  continue;
153666
153847
  if (b.type === "text") {
153667
153848
  if (b.text.trim())
@@ -154019,17 +154200,17 @@ function realProbes(c) {
154019
154200
  return { inRepo: false };
154020
154201
  }
154021
154202
  },
154022
- hasToken: () => !!(0, import_code_core9.getToken)(),
154203
+ hasToken: () => !!(0, import_code_core10.getToken)(),
154023
154204
  verifyAuth: async () => {
154024
154205
  try {
154025
- await (0, import_code_core9.getBalance)();
154206
+ await (0, import_code_core10.getBalance)();
154026
154207
  return { ok: true, detail: "signed in, token accepted" };
154027
154208
  } catch (e2) {
154028
154209
  return { ok: false, detail: `token rejected \u2014 ${e2.message.slice(0, 120)}` };
154029
154210
  }
154030
154211
  },
154031
- reach: () => reachHost(import_code_core9.API_BASE),
154032
- apiBase: import_code_core9.API_BASE,
154212
+ reach: () => reachHost(import_code_core10.API_BASE),
154213
+ apiBase: import_code_core10.API_BASE,
154033
154214
  isTTY: !!process.stdout.isTTY,
154034
154215
  columns: process.stdout.columns,
154035
154216
  rows: process.stdout.rows,
@@ -154037,7 +154218,7 @@ function realProbes(c) {
154037
154218
  mcp: () => {
154038
154219
  let configured = 0;
154039
154220
  try {
154040
- 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;
154041
154222
  } catch {
154042
154223
  }
154043
154224
  return { configured, rows: c.mcpRows ?? [] };
@@ -163895,7 +164076,7 @@ var useBoxMetrics = (ref) => {
163895
164076
  var use_box_metrics_default = useBoxMetrics;
163896
164077
 
163897
164078
  // src/ui/themeSettings.ts
163898
- var import_code_core10 = __toESM(require_dist3(), 1);
164079
+ var import_code_core11 = __toESM(require_dist3(), 1);
163899
164080
  import * as fs17 from "fs";
163900
164081
  import * as os9 from "os";
163901
164082
  import * as path17 from "path";
@@ -163929,13 +164110,13 @@ function applyThemeConfig(cfg, opts = {}) {
163929
164110
  return { resolved, notes };
163930
164111
  }
163931
164112
  function applyTrustedTheme(env4 = process.env) {
163932
- const settings = (0, import_code_core10.loadSettings)(os9.homedir());
164113
+ const settings = (0, import_code_core11.loadSettings)(os9.homedir());
163933
164114
  const cfg = readThemeConfig(settings.raw);
163934
164115
  const { resolved, notes } = applyThemeConfig(cfg, { env: env4 });
163935
164116
  return { ...cfg, resolved, notes };
163936
164117
  }
163937
164118
  function loadAndApplyTheme(workDir, env4 = process.env) {
163938
- const settings = (0, import_code_core10.loadSettings)(workDir);
164119
+ const settings = (0, import_code_core11.loadSettings)(workDir);
163939
164120
  const cfg = readThemeConfig(settings.raw);
163940
164121
  const { resolved, notes } = applyThemeConfig(cfg, { env: env4 });
163941
164122
  return { ...cfg, resolved, notes };
@@ -167651,7 +167832,7 @@ function orderByRecents(entries, recents) {
167651
167832
  }
167652
167833
 
167653
167834
  // src/ui/memoryNote.ts
167654
- var import_code_core11 = __toESM(require_dist3(), 1);
167835
+ var import_code_core12 = __toESM(require_dist3(), 1);
167655
167836
  function stripEscape(line) {
167656
167837
  return line.startsWith("\\#") ? line.slice(1) : line;
167657
167838
  }
@@ -167668,7 +167849,7 @@ function parseMemoryNote(line) {
167668
167849
  return text ? { scope: "project", text } : null;
167669
167850
  }
167670
167851
  async function saveNote(note, workDir) {
167671
- 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" });
167672
167853
  return { ok: r2.ok, already: r2.already, superseded: r2.superseded === true, scope: r2.scope, file: r2.file, text: note.text };
167673
167854
  }
167674
167855
  function parseMemoryAdd(arg) {
@@ -167690,7 +167871,7 @@ function interpretPrompt(line) {
167690
167871
  }
167691
167872
 
167692
167873
  // src/ui/mcpPanel.ts
167693
- var import_code_core12 = __toESM(require_dist3(), 1);
167874
+ var import_code_core13 = __toESM(require_dist3(), 1);
167694
167875
  function parseMcpArgs(arg) {
167695
167876
  const parts = arg.trim().split(/\s+/).filter(Boolean);
167696
167877
  if (parts.length === 0)
@@ -167791,7 +167972,7 @@ function formatMcpDetail(input) {
167791
167972
  const desc = (t2.description ?? "").split("\n")[0].trim();
167792
167973
  const head = ` ${t2.name.padEnd(nameWidth)}`;
167793
167974
  const maxSuffix = Math.max(0, budget - head.length);
167794
- let suffix = (0, import_code_core12.describeStats)(byTool.get(t2.name));
167975
+ let suffix = (0, import_code_core13.describeStats)(byTool.get(t2.name));
167795
167976
  if (suffix) {
167796
167977
  suffix = ` ${suffix}`;
167797
167978
  if (suffix.length > maxSuffix) {
@@ -168005,9 +168186,9 @@ function planModelSwitch(next, effort, clamp) {
168005
168186
  }
168006
168187
 
168007
168188
  // src/ui/keybindingsStore.ts
168008
- var import_code_core13 = __toESM(require_dist3(), 1);
168189
+ var import_code_core14 = __toESM(require_dist3(), 1);
168009
168190
  function loadAndApplyKeybindings(workDir) {
168010
- const settings = (0, import_code_core13.loadSettings)(workDir);
168191
+ const settings = (0, import_code_core14.loadSettings)(workDir);
168011
168192
  const raw = settings.raw.keybindings;
168012
168193
  const result = parseKeybindings(raw);
168013
168194
  setBindings(result);
@@ -168057,7 +168238,7 @@ function resetKeybindings(workDir, scope = "user") {
168057
168238
  return result;
168058
168239
  }
168059
168240
  function readRawBlock(workDir) {
168060
- const settings = (0, import_code_core13.loadSettings)(workDir);
168241
+ const settings = (0, import_code_core14.loadSettings)(workDir);
168061
168242
  const raw = settings.raw.keybindings;
168062
168243
  return raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {};
168063
168244
  }
@@ -168257,7 +168438,7 @@ function formatTaskGraph(input) {
168257
168438
  }
168258
168439
 
168259
168440
  // src/ui/contextPanel.ts
168260
- var import_code_core14 = __toESM(require_dist3(), 1);
168441
+ var import_code_core15 = __toESM(require_dist3(), 1);
168261
168442
  var KIND_LABEL2 = {
168262
168443
  tool_result: "tool results",
168263
168444
  reply: "model replies",
@@ -168332,8 +168513,8 @@ function textHead(block) {
168332
168513
  var TOP_DEFAULT = 3;
168333
168514
  var TOP_ALL = 10;
168334
168515
  function measureContext(messages, topN = TOP_DEFAULT) {
168335
- const bytes = (0, import_code_core14.estimateBodyBytes)(messages);
168336
- 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);
168337
168518
  const toolNames = /* @__PURE__ */ new Map();
168338
168519
  const buckets = /* @__PURE__ */ new Map();
168339
168520
  const all = [];
@@ -168513,7 +168694,7 @@ function formatContextPanel(input) {
168513
168694
  }
168514
168695
 
168515
168696
  // src/ui/permDashboard.ts
168516
- var import_code_core15 = __toESM(require_dist3(), 1);
168697
+ var import_code_core16 = __toESM(require_dist3(), 1);
168517
168698
  var DASHBOARD_ACTIONS = [
168518
168699
  { group: "Filesystem", label: "read a project file", tool: "read_file", input: { path: "src/app.ts" } },
168519
168700
  { group: "Filesystem", label: "edit a project file", tool: "edit_file", input: { path: "src/app.ts", old_string: "a", new_string: "b" } },
@@ -168544,15 +168725,15 @@ var NOT_A_READ = /* @__PURE__ */ new Set(["todo_write", "use_skill"]);
168544
168725
  function capOf(tool) {
168545
168726
  if (tool === "task")
168546
168727
  return "delegate";
168547
- if (import_code_core15.BASH_TOOLS.has(tool))
168728
+ if (import_code_core16.BASH_TOOLS.has(tool))
168548
168729
  return "shell";
168549
- 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))
168550
168731
  return "write";
168551
168732
  if (NETWORK_TOOLS.has(tool))
168552
168733
  return "network";
168553
168734
  if (tool === "bash_output" || tool === "kill_shell")
168554
168735
  return "shell";
168555
- 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))
168556
168737
  return "read";
168557
168738
  return null;
168558
168739
  }
@@ -168580,19 +168761,19 @@ function capPhrase(caps) {
168580
168761
  return parts.length ? parts.join(", ") : "nothing";
168581
168762
  }
168582
168763
  function explainAction(rules, mode, workDir, action, opts = {}) {
168583
- 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);
168584
168765
  if (rule === "deny")
168585
168766
  return { mark: "\u2717", reason: "denied by a permission rule" };
168586
- 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) {
168587
168768
  return { mark: "\u26A0", reason: 'irreversible \u2014 always asks (type "yes")' };
168588
168769
  }
168589
168770
  if (rule === "allow")
168590
168771
  return { mark: "\u2713", reason: "allowed by a permission rule" };
168591
- 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 });
168592
168773
  if (verdict === "allow") {
168593
168774
  if (capOf(action.tool) === "read")
168594
168775
  return { mark: "\u2713", reason: "read-only \u2014 allowed in every mode" };
168595
- 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 ?? ""))) {
168596
168777
  return { mark: "\u2713", reason: "read-only command \u2014 allowed in every mode" };
168597
168778
  }
168598
168779
  if (action.tool === "fetch_url" || action.tool === "web_search") {
@@ -169050,7 +169231,7 @@ function agentTypeModel(type, workDir) {
169050
169231
  if (!agentTypeCache || agentTypeCache.workDir !== workDir || now - agentTypeCache.at > 1e3) {
169051
169232
  const models = /* @__PURE__ */ new Map();
169052
169233
  try {
169053
- for (const t2 of (0, import_code_core16.loadAgentTypesWithWarnings)(workDir).types)
169234
+ for (const t2 of (0, import_code_core17.loadAgentTypesWithWarnings)(workDir).types)
169054
169235
  if (t2.model)
169055
169236
  models.set(t2.name, t2.model);
169056
169237
  } catch {
@@ -169115,13 +169296,13 @@ function agentPanelContext(v, cols, now) {
169115
169296
  }
169116
169297
  let maxConcurrent;
169117
169298
  try {
169118
- 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);
169119
169300
  } catch {
169120
169301
  maxConcurrent = void 0;
169121
169302
  }
169122
169303
  const types3 = (() => {
169123
169304
  try {
169124
- return (0, import_code_core16.loadAgentTypesWithWarnings)(v.workDir).types.map((t2) => ({
169305
+ return (0, import_code_core17.loadAgentTypesWithWarnings)(v.workDir).types.map((t2) => ({
169125
169306
  name: t2.name,
169126
169307
  description: t2.description,
169127
169308
  ...t2.model ? { model: t2.model } : {},
@@ -169221,7 +169402,7 @@ function printMergeReview(dir, cols) {
169221
169402
  function printTaskGraph(workDir, cols) {
169222
169403
  let tasks = [];
169223
169404
  try {
169224
- tasks = (0, import_code_core18.listSharedTasks)(workDir);
169405
+ tasks = (0, import_code_core19.listSharedTasks)(workDir);
169225
169406
  } catch (e2) {
169226
169407
  console.log(paint.red(" Could not read the shared task list: " + e2.message));
169227
169408
  return;
@@ -169336,13 +169517,13 @@ var NO_PDF_MODELS = /* @__PURE__ */ new Set([
169336
169517
  ...NO_VISION_MODELS
169337
169518
  ]);
169338
169519
  function needsVisionSidecar(model) {
169339
- 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));
169340
169521
  }
169341
169522
  function needsPdfSidecar(model) {
169342
- 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));
169343
169524
  }
169344
169525
  function selectableModelIds() {
169345
- return (0, import_code_core16.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
169526
+ return (0, import_code_core17.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
169346
169527
  }
169347
169528
  function normaliseModelId(model) {
169348
169529
  const raw = (model ?? "").trim();
@@ -169403,9 +169584,9 @@ var _BACKEND_STYLE_TO_LOCAL = {
169403
169584
  };
169404
169585
  function effortStyleFor(modelId) {
169405
169586
  const id = normaliseModelId(modelId);
169406
- if ((0, import_code_core16.liveVendorFor)(id) === "Anthropic")
169587
+ if ((0, import_code_core17.liveVendorFor)(id) === "Anthropic")
169407
169588
  return "anthropic";
169408
- const liveStyle = (0, import_code_core16.liveReasoningEffortStyle)(id);
169589
+ const liveStyle = (0, import_code_core17.liveReasoningEffortStyle)(id);
169409
169590
  if (liveStyle && _BACKEND_STYLE_TO_LOCAL[liveStyle])
169410
169591
  return _BACKEND_STYLE_TO_LOCAL[liveStyle];
169411
169592
  return MODEL_EFFORT_STYLE[id] ?? "anthropic";
@@ -169425,7 +169606,7 @@ function defaultEffortForModel(modelId) {
169425
169606
  }
169426
169607
  function resolveModelLabel(alias) {
169427
169608
  const id = normaliseModelId(alias);
169428
- return (0, import_code_core16.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
169609
+ return (0, import_code_core17.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
169429
169610
  }
169430
169611
  function tryExec(cmd, cwd2) {
169431
169612
  try {
@@ -169515,12 +169696,12 @@ function readNexrallMd(workDir) {
169515
169696
  } catch {
169516
169697
  }
169517
169698
  };
169518
- const memContent = (0, import_code_core16.readAllMemory)(workDir);
169699
+ const memContent = (0, import_code_core17.readAllMemory)(workDir);
169519
169700
  if (memContent) {
169520
169701
  parts.push(memContent);
169521
- noteLoaded((0, import_code_core16.memoryFilePath)("global"), "User");
169702
+ noteLoaded((0, import_code_core17.memoryFilePath)("global"), "User");
169522
169703
  if (workDir)
169523
- noteLoaded((0, import_code_core16.memoryFilePath)("project", workDir), "Project");
169704
+ noteLoaded((0, import_code_core17.memoryFilePath)("project", workDir), "Project");
169524
169705
  }
169525
169706
  const globalNexrall = path25.join(os15.homedir(), ".nexrall", "nexrall.md");
169526
169707
  const globalContent = tryRead(globalNexrall);
@@ -169757,7 +169938,7 @@ var Spinner = class {
169757
169938
  return this.interval !== null;
169758
169939
  }
169759
169940
  };
169760
- var cliBackgroundAgents = new import_code_core16.BackgroundAgentHub();
169941
+ var cliBackgroundAgents = new import_code_core17.BackgroundAgentHub();
169761
169942
  function nextBackgroundChange() {
169762
169943
  return new Promise((resolve11) => {
169763
169944
  const off = cliBackgroundAgents.onChange(() => {
@@ -169775,7 +169956,7 @@ async function initMcp(workDir, override, strict) {
169775
169956
  _mcpManager = null;
169776
169957
  return;
169777
169958
  }
169778
- _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core16.McpManager.loadConfig(workDir);
169959
+ _mcpConfig = strict && override ? { mcpServers: {} } : import_code_core17.McpManager.loadConfig(workDir);
169779
169960
  if (override)
169780
169961
  Object.assign(_mcpConfig.mcpServers, override.mcpServers);
169781
169962
  if (Object.keys(_mcpConfig.mcpServers).length === 0) {
@@ -169786,12 +169967,12 @@ async function initMcp(workDir, override, strict) {
169786
169967
  await injectMcpTokens(_mcpConfig);
169787
169968
  } catch {
169788
169969
  }
169789
- const manager = new import_code_core16.McpManager();
169970
+ const manager = new import_code_core17.McpManager();
169790
169971
  await manager.connectAll(_mcpConfig);
169791
169972
  _mcpManager = manager;
169792
169973
  }
169793
169974
  function formatAgentsList(workDir) {
169794
- const { types: types3, warnings } = (0, import_code_core16.loadAgentTypesWithWarnings)(workDir);
169975
+ const { types: types3, warnings } = (0, import_code_core17.loadAgentTypesWithWarnings)(workDir);
169795
169976
  if (!types3.length)
169796
169977
  return paint.dim(" No agent types found (this should not happen \u2014 builtins always register).");
169797
169978
  const lines = types3.map((t2) => {
@@ -169976,7 +170157,7 @@ function startSessionLog(id, resumed, extra = {}) {
169976
170157
  }
169977
170158
  var failureLedger = new FailureLedger();
169978
170159
  var networkLedger = new NetworkLedger();
169979
- 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;
169980
170161
  var notifier = new Notifier({ env: process.env, isTTY: Boolean(process.stdout.isTTY) });
169981
170162
  var userActiveThisTurn = false;
169982
170163
  var failureBannerShown = "";
@@ -170183,7 +170364,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
170183
170364
  setMode(mode);
170184
170365
  console.log();
170185
170366
  spinner.start("thinking\u2026", { hint: false, verbs: true });
170186
- const result = await (0, import_code_core16.runAgentLoop)(messages, {
170367
+ const result = await (0, import_code_core17.runAgentLoop)(messages, {
170187
170368
  workDir,
170188
170369
  sessionId: activeSessionIdGetter(),
170189
170370
  model: modelAlias,
@@ -170527,12 +170708,12 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
170527
170708
  sessionId: activeSessionIdGetter(),
170528
170709
  cwd: workDir,
170529
170710
  model: modelAlias,
170530
- tools: _headlessPolicy.onlyTools ? [..._headlessPolicy.onlyTools].sort() : (0, import_code_core16.knownToolNames)(),
170711
+ tools: _headlessPolicy.onlyTools ? [..._headlessPolicy.onlyTools].sort() : (0, import_code_core17.knownToolNames)(),
170531
170712
  permissionMode: _headlessPolicy.permissionMode
170532
170713
  }));
170533
170714
  emit({ type: "start", model: modelAlias, workDir });
170534
170715
  verbose(`[verbose] model=${modelAlias} cwd=${workDir} mode=${mode} session=${activeSessionIdGetter() ?? "-"}`);
170535
- const result = await (0, import_code_core16.runAgentLoop)(messages, {
170716
+ const result = await (0, import_code_core17.runAgentLoop)(messages, {
170536
170717
  workDir,
170537
170718
  sessionId: activeSessionIdGetter(),
170538
170719
  model: modelAlias,
@@ -170673,16 +170854,43 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
170673
170854
  emit({ type: "permission_denied", tool, reason: "tools" });
170674
170855
  return false;
170675
170856
  }
170676
- 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 });
170677
170859
  const v = headlessVerdict({
170678
- 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),
170679
170861
  destructive: !!d,
170680
170862
  allowDestructive: process.env.NEXRALL_ALLOW_DESTRUCTIVE === "1",
170681
170863
  strict: _headlessPolicy.strict,
170682
- policy: (0, import_code_core17.decide)({ tool, input, mode: (0, import_code_core18.parseMode)(mode), bypass: isYoloMode(), destructive: !!d })
170864
+ policy
170683
170865
  });
170684
170866
  if (v.verdict === "allow")
170685
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
+ }
170686
170894
  if (v.reason === "destructive" && d) {
170687
170895
  emit({ type: "error", error: `Destructive command blocked in headless mode (${d.category}): ${d.reason}. Set NEXRALL_ALLOW_DESTRUCTIVE=1 to permit.` });
170688
170896
  } else {
@@ -170920,7 +171128,7 @@ async function startChatSession(options) {
170920
171128
  if (options.bare)
170921
171129
  setNoDiscovery(true);
170922
171130
  _cliAgentTypes = (options.agents ?? []).map((a) => ({ ...a, source: "programmatic" }));
170923
- (0, import_code_core16.enableAgentTranscriptPersistence)();
171131
+ (0, import_code_core17.enableAgentTranscriptPersistence)();
170924
171132
  const headless = options.outputFormat === "json" || options.outputFormat === "stream-json";
170925
171133
  const requestedWorkDir = options.workDir;
170926
171134
  let workDir = requestedWorkDir;
@@ -170929,7 +171137,7 @@ async function startChatSession(options) {
170929
171137
  let peerInbox;
170930
171138
  const peerInboxQueue = [];
170931
171139
  let modelAlias = options.model;
170932
- void (0, import_code_core16.getModelCatalogue)().catch(() => {
171140
+ void (0, import_code_core17.getModelCatalogue)().catch(() => {
170933
171141
  });
170934
171142
  const canPrompt = !headless && !options.prompt && !options.stdinText && process.stdin.isTTY === true && process.stdout.isTTY === true;
170935
171143
  if (!trustGrantedByEnv()) {
@@ -170957,21 +171165,21 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
170957
171165
  }
170958
171166
  const themeConfig = loadAndApplyTheme(workDir);
170959
171167
  const keyConfig = loadAndApplyKeybindings(workDir);
170960
- liveDiffEnabled = readLiveDiffSetting((0, import_code_core18.loadSettings)(workDir).raw);
170961
- mouseEnabled = readMouseSetting((0, import_code_core18.loadSettings)(workDir).raw);
170962
- 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));
170963
171171
  setAskNotifier((what) => {
170964
171172
  notifier.notify(needsYouNote(what));
170965
171173
  emitEvent("permission.requested", { what });
170966
- (0, import_code_core17.fireNotificationHooks)(workDir, `Nexrall needs your permission: ${what}`);
171174
+ (0, import_code_core18.fireNotificationHooks)(workDir, `Nexrall needs your permission: ${what}`);
170967
171175
  });
170968
171176
  setPermissionObserver((tool, granted) => emitEvent(granted ? "permission.granted" : "permission.denied", { tool }));
170969
171177
  let activeThemeName = themeConfig.theme;
170970
171178
  if (options.worktree !== void 0) {
170971
171179
  const requestedName = typeof options.worktree === "string" ? options.worktree : void 0;
170972
- const existing = requestedName ? (0, import_code_core16.findWorktreeByName)(requestedWorkDir, requestedName) : null;
171180
+ const existing = requestedName ? (0, import_code_core17.findWorktreeByName)(requestedWorkDir, requestedName) : null;
170973
171181
  if (existing) {
170974
- const entered = (0, import_code_core16.enterWorktree)(existing);
171182
+ const entered = (0, import_code_core17.enterWorktree)(existing);
170975
171183
  if (!entered.ok) {
170976
171184
  process.stderr.write(`Failed to resume worktree "${requestedName}": ${entered.error}
170977
171185
  `);
@@ -170981,7 +171189,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
170981
171189
  if (!headless)
170982
171190
  console.log(paint.dim(` Resumed worktree: ${paint.bold(existing.name)} (${existing.worktreePath})`));
170983
171191
  } else {
170984
- const created = (0, import_code_core16.createWorktree)(requestedWorkDir, requestedName);
171192
+ const created = (0, import_code_core17.createWorktree)(requestedWorkDir, requestedName);
170985
171193
  if (!created.ok || !created.state) {
170986
171194
  process.stderr.write(`Failed to create worktree: ${created.error ?? "unknown error"}
170987
171195
  `);
@@ -171000,7 +171208,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
171000
171208
  process.on("exit", () => {
171001
171209
  if (!worktreeState)
171002
171210
  return;
171003
- const hadWork = (0, import_code_core16.worktreeHasWork)(worktreeState);
171211
+ const hadWork = (0, import_code_core17.worktreeHasWork)(worktreeState);
171004
171212
  if (hadWork) {
171005
171213
  process.stderr.write(
171006
171214
  `
@@ -171011,7 +171219,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171011
171219
  );
171012
171220
  return;
171013
171221
  }
171014
- (0, import_code_core16.removeWorktree)(worktreeState);
171222
+ (0, import_code_core17.removeWorktree)(worktreeState);
171015
171223
  });
171016
171224
  }
171017
171225
  const env4 = collectEnv(workDir);
@@ -171020,7 +171228,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171020
171228
  const nexrallMd = [agentPrefix, briefBase, options.appendSystemPrompt?.trim(), addDirNote(options.addDirs ?? []), options.jsonSchema ? schemaInstruction(options.jsonSchema) : ""].filter(Boolean).join("\n\n") || void 0;
171021
171229
  const agentTools = options.selectedAgent?.tools;
171022
171230
  const toolAllow = agentTools && options.onlyTools ? options.onlyTools.filter((t2) => agentTools.includes(t2)) : options.onlyTools ?? agentTools;
171023
- _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 };
171024
171232
  _headlessPolicy.verbose = !!options.verbose;
171025
171233
  _headlessPolicy.includePartial = !!options.includePartialMessages;
171026
171234
  _headlessPolicy.permissionMode = String(options.initialMode ?? "");
@@ -171029,8 +171237,8 @@ Resume it with: nex --worktree ${worktreeState.name}
171029
171237
  const interactive = !headless && !options.prompt && !options.stdinText;
171030
171238
  let peerHeartbeatTimer;
171031
171239
  if (interactive) {
171032
- const handle2 = (0, import_code_core16.registerPeer)({ clientType: "cli", workDir: requestedWorkDir });
171033
- 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) });
171034
171242
  handle2.record.socketPath = inbox.socketPath;
171035
171243
  handle2.heartbeat();
171036
171244
  peerHandle = handle2;
@@ -171102,7 +171310,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171102
171310
  let sessionId = options.newSessionId ?? crypto7.randomBytes(8).toString("hex");
171103
171311
  activeSessionIdGetter = () => sessionId;
171104
171312
  let sessionTitle = options.sessionName ?? "";
171105
- const fileWatcher = noDiscovery ? null : (0, import_code_core16.startFileChangedWatcher)(workDir, sessionId, {
171313
+ const fileWatcher = noDiscovery ? null : (0, import_code_core17.startFileChangedWatcher)(workDir, sessionId, {
171106
171314
  // A hook's {"systemMessage":"…"} is the event's one user-visible field.
171107
171315
  onSystemMessage: (m2) => console.log(paint.dim(` ${m2}`))
171108
171316
  });
@@ -171113,7 +171321,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171113
171321
  if (options.verbose)
171114
171322
  process.stderr.write("[verbose] FileChanged hook: " + watching.join(", ") + "\n");
171115
171323
  }
171116
- void (0, import_code_core16.fireInstructionsLoadedHooks)(workDir, sessionId, lastLoadedInstructions);
171324
+ void (0, import_code_core17.fireInstructionsLoadedHooks)(workDir, sessionId, lastLoadedInstructions);
171117
171325
  const modeLocked = options.lockMode === true;
171118
171326
  let agentMode = modeLocked ? "plan" : options.initialMode ?? "auto";
171119
171327
  const refuseIfLocked = (what) => {
@@ -171135,7 +171343,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171135
171343
  for (const n of themeConfig.notes)
171136
171344
  console.log(paint.dim(" theme: " + n));
171137
171345
  }
171138
- let skills = noDiscovery ? [] : (0, import_code_core16.loadSkills)(workDir);
171346
+ let skills = noDiscovery ? [] : (0, import_code_core17.loadSkills)(workDir);
171139
171347
  if (skills.length && !headless)
171140
171348
  console.log(paint.dim(` ${skills.length} skill(s) loaded`));
171141
171349
  let contextSet = loadContextSet(workDir);
@@ -171200,7 +171408,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171200
171408
  }
171201
171409
  startSessionLog(sessionId, true, options.forkSession ? { workDir, forkOf: stored.id } : { workDir });
171202
171410
  try {
171203
- const compacted = await (0, import_code_core16.compactMessagesForResume)(messages, {
171411
+ const compacted = await (0, import_code_core17.compactMessagesForResume)(messages, {
171204
171412
  workDir,
171205
171413
  model: modelAlias,
171206
171414
  clientType: "cli"
@@ -171216,7 +171424,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171216
171424
  }
171217
171425
  if (!(options.resume || options.continueLast))
171218
171426
  startSessionLog(sessionId, false, { workDir });
171219
- const checkpoints = new import_code_core16.CheckpointManager(workDir, sessionId);
171427
+ const checkpoints = new import_code_core17.CheckpointManager(workDir, sessionId);
171220
171428
  const commitCheckpoint = () => {
171221
171429
  checkpoints.commitTurn();
171222
171430
  emitEvent("checkpoint.created", { turns: checkpoints.list().length });
@@ -171238,7 +171446,7 @@ Resume it with: nex --worktree ${worktreeState.name}
171238
171446
  sessionTitle = raw.length > 60 ? raw.slice(0, 60) + "\u2026" : raw;
171239
171447
  };
171240
171448
  const recoverTurn = (err, current) => {
171241
- const salvaged = (0, import_code_core16.salvageHistory)(err);
171449
+ const salvaged = (0, import_code_core17.salvageHistory)(err);
171242
171450
  printErrorReport(err, abortSignal.aborted);
171243
171451
  printTurnSummary(abortSignal.aborted ? "interrupted" : "failed");
171244
171452
  if (!salvaged)
@@ -171296,7 +171504,7 @@ ${text}` : "");
171296
171504
  throw e2;
171297
171505
  const from = modelAlias;
171298
171506
  modelAlias = options.fallbackModel;
171299
- const salvagedMsgs = (0, import_code_core16.salvageHistory)(e2);
171507
+ const salvagedMsgs = (0, import_code_core17.salvageHistory)(e2);
171300
171508
  if (salvagedMsgs)
171301
171509
  messages = salvagedMsgs;
171302
171510
  const note = `Model ${from} failed (${String(e2.message).slice(0, 120)}); retrying on ${modelAlias}`;
@@ -171315,7 +171523,7 @@ ${text}` : "");
171315
171523
  continue;
171316
171524
  }
171317
171525
  const notes = cliBackgroundAgents.takeNotifications();
171318
- 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) }] });
171319
171527
  result = await runOnce();
171320
171528
  messages = result.messages;
171321
171529
  }
@@ -171333,7 +171541,7 @@ ${text}` : "");
171333
171541
  await nextBackgroundChange();
171334
171542
  continue;
171335
171543
  }
171336
- 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()) }] });
171337
171545
  result = await runOnce();
171338
171546
  messages = result.messages;
171339
171547
  }
@@ -171346,7 +171554,7 @@ ${text}` : "");
171346
171554
  await new Promise((r2) => setImmediate(r2));
171347
171555
  process.exit(process.exitCode ?? 0);
171348
171556
  } catch (err) {
171349
- const salvaged = (0, import_code_core16.salvageHistory)(err);
171557
+ const salvaged = (0, import_code_core17.salvageHistory)(err);
171350
171558
  if (salvaged) {
171351
171559
  messages = salvaged;
171352
171560
  try {
@@ -171379,12 +171587,12 @@ ${text}` : "");
171379
171587
  console.log(paint.dim(" Type a message, /help for commands, or Ctrl+C to exit."));
171380
171588
  console.log();
171381
171589
  const footerExtras = () => {
171382
- const win = (0, import_code_core16.contextWindowFor)(modelAlias);
171590
+ const win = (0, import_code_core17.contextWindowFor)(modelAlias);
171383
171591
  return {
171384
171592
  nav: transcriptNav ? navLabel(transcriptNav) : void 0,
171385
171593
  model: resolveModelLabel(modelAlias),
171386
171594
  branch: gitBranch(workDir) ?? void 0,
171387
- 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,
171388
171596
  costUsd: sessionUsage.costReported ? sessionUsage.cost : void 0,
171389
171597
  // Live sub-agents (spec §37). Recomputed per footer render, which is also what
171390
171598
  // retires a background agent's row the moment the hub drops it.
@@ -171454,7 +171662,7 @@ ${text}` : "");
171454
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)") : ""));
171455
171663
  }
171456
171664
  };
171457
- 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))];
171458
171666
  let slashRecents = loadRecents();
171459
171667
  const syncSlashMenu = () => {
171460
171668
  if (interactive)
@@ -171496,7 +171704,7 @@ ${text}` : "");
171496
171704
  };
171497
171705
  const modelPickerNote = (id) => {
171498
171706
  const parts = [];
171499
- 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]);
171500
171708
  if (mult !== void 0 && mult !== 1)
171501
171709
  parts.push(`${mult}x`);
171502
171710
  if (needsVisionSidecar(id))
@@ -171691,7 +171899,7 @@ ${text}` : "");
171691
171899
  return;
171692
171900
  }
171693
171901
  const row2 = matches[0];
171694
- const scope = import_code_core16.McpManager.serverScope(workDir, row2.name);
171902
+ const scope = import_code_core17.McpManager.serverScope(workDir, row2.name);
171695
171903
  if (action.kind === "detail") {
171696
171904
  const st = _mcpManager?.getStatus().find((x2) => x2.name === row2.name);
171697
171905
  const tools = (st?.tools ?? []).map((t2) => ({ name: t2.name, description: t2.description }));
@@ -171715,7 +171923,7 @@ ${text}` : "");
171715
171923
  let signedIn = false;
171716
171924
  let attempted = false;
171717
171925
  if (!plan.refuse && plan.persist) {
171718
- 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);
171719
171927
  noteDisabledInConfig(row2.name, plan.persist.disabled);
171720
171928
  }
171721
171929
  if (!plan.refuse) {
@@ -171900,12 +172108,12 @@ ${text}` : "");
171900
172108
  rl.prompt();
171901
172109
  return;
171902
172110
  }
171903
- 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") {
171904
172112
  console.log(paint.red(" \u2716 Denied by permission rule: bash"));
171905
172113
  rl.prompt();
171906
172114
  return;
171907
172115
  }
171908
- if (modeLocked && !(0, import_code_core18.isReadOnlyBash)(cmd)) {
172116
+ if (modeLocked && !(0, import_code_core19.isReadOnlyBash)(cmd)) {
171909
172117
  console.log(paint.yellow(" \u2298 Read-only session (--readonly): only read-only commands can run with !."));
171910
172118
  rl.prompt();
171911
172119
  return;
@@ -172060,7 +172268,7 @@ ${text}` : "");
172060
172268
  emitEvent("session.completed", { id: liveSessionId, reason: "clear" });
172061
172269
  sessionId = crypto7.randomBytes(8).toString("hex");
172062
172270
  sessionTitle = "";
172063
- (0, import_code_core18.resetSessionSubAgentBudget)();
172271
+ (0, import_code_core19.resetSessionSubAgentBudget)();
172064
172272
  sessionUsage.reset();
172065
172273
  sessionStartedAt = Date.now();
172066
172274
  sessionToolCalls = 0;
@@ -172156,7 +172364,7 @@ ${text}` : "");
172156
172364
  startSessionLog(sessionId, true, { workDir });
172157
172365
  const beforeCompact = messages.length;
172158
172366
  try {
172159
- await (0, import_code_core16.compactMessagesForResume)(messages, {
172367
+ await (0, import_code_core17.compactMessagesForResume)(messages, {
172160
172368
  workDir,
172161
172369
  model: modelAlias,
172162
172370
  clientType: "cli"
@@ -172210,7 +172418,7 @@ ${text}` : "");
172210
172418
  }
172211
172419
  rl.pause();
172212
172420
  console.log(paint.dim(" Compacting conversation\u2026"));
172213
- 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));
172214
172422
  const KEEP = 4;
172215
172423
  const toSummarize = messages.slice(0, -KEEP);
172216
172424
  const kept = messages.slice(-KEEP);
@@ -172256,7 +172464,7 @@ ${summaryText}` }] },
172256
172464
  saveSession2(sessionId, sessionTitle || "Untitled", workDir, messages);
172257
172465
  console.log(paint.green(` Compacted ${toSummarize.length} messages \u2192 ${messages.length} messages.`));
172258
172466
  emitEvent("context.compacted", { where: "manual", before: toSummarize.length + KEEP, after: messages.length });
172259
- await (0, import_code_core17.fireManualPostCompactHook)(workDir, sessionId, toSummarize.length);
172467
+ await (0, import_code_core18.fireManualPostCompactHook)(workDir, sessionId, toSummarize.length);
172260
172468
  if (pin.attachments.length)
172261
172469
  console.log(paint.dim(` ${pin.attachments.length} pinned file(s) re-attached.`));
172262
172470
  for (const n of pin.notes)
@@ -172332,13 +172540,13 @@ ${dirList}`;
172332
172540
  const sub = arg.trim().toLowerCase();
172333
172541
  console.log();
172334
172542
  if (sub === "list") {
172335
- const all = (0, import_code_core16.listWorktrees)(requestedWorkDir);
172543
+ const all = (0, import_code_core17.listWorktrees)(requestedWorkDir);
172336
172544
  if (!all.length) {
172337
172545
  console.log(paint.dim(" No worktrees under this project."));
172338
172546
  } else {
172339
172547
  console.log(paint.bold(` Worktrees (${all.length})`));
172340
172548
  for (const w of all) {
172341
- 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");
172342
172550
  console.log(` ${paint.cyan(w.name)} ${paint.dim(w.worktreePath)} [${dirty}]`);
172343
172551
  }
172344
172552
  console.log();
@@ -172376,7 +172584,7 @@ ${dirList}`;
172376
172584
  if (!targetName || !text) {
172377
172585
  console.log(paint.dim(" Usage: /peers send <name> <message>"));
172378
172586
  } else {
172379
- 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);
172380
172588
  if (!target) {
172381
172589
  console.log(paint.red(` No live session named "${targetName}". Use /peers to list who's reachable.`));
172382
172590
  } else if (!target.socketPath) {
@@ -172393,7 +172601,7 @@ ${dirList}`;
172393
172601
  console.log(paint.dim(" Usage: /peers broadcast <message>"));
172394
172602
  } else {
172395
172603
  const { broadcastPeerMessage } = await Promise.resolve().then(() => __toESM(require_dist3(), 1));
172396
- 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 }));
172397
172605
  if (!targets.length) {
172398
172606
  console.log(paint.dim(" No other reachable Nexrall Code sessions to broadcast to."));
172399
172607
  } else {
@@ -172405,7 +172613,7 @@ ${dirList}`;
172405
172613
  }
172406
172614
  }
172407
172615
  } else {
172408
- const peers = (0, import_code_core16.listPeers)(peerHandle.record.id);
172616
+ const peers = (0, import_code_core17.listPeers)(peerHandle.record.id);
172409
172617
  if (peers.length <= 1) {
172410
172618
  console.log(paint.dim(" No other Nexrall Code sessions are currently discoverable on this machine."));
172411
172619
  } else {
@@ -172447,7 +172655,7 @@ ${dirList}`;
172447
172655
  const label = memScope === "global" ? "GLOBAL" : "this PROJECT's";
172448
172656
  const answer = await new Promise((resolve11) => rl.question(paint.yellow(` Clear ${label} memory? This cannot be undone. [y/n] `), (a) => resolve11(a.trim())));
172449
172657
  if (answer === "y" || answer === "yes") {
172450
- (0, import_code_core16.clearMemory)(memScope, workDir);
172658
+ (0, import_code_core17.clearMemory)(memScope, workDir);
172451
172659
  console.log(paint.green(` ${memScope === "global" ? "Global" : "Project"} memory cleared.`));
172452
172660
  } else {
172453
172661
  console.log(paint.dim(" Cancelled."));
@@ -172458,20 +172666,20 @@ ${dirList}`;
172458
172666
  }
172459
172667
  console.log();
172460
172668
  if (wantsGlobalOnly) {
172461
- const stats = (0, import_code_core16.memoryStats)("global");
172462
- 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 });
172463
172671
  const archivedNote = stats.archived > 0 ? `, ${stats.archived} archived` : "";
172464
172672
  console.log(paint.bold(" Global memory ") + paint.dim(`(${stats.entries} entries${archivedNote}, ${(stats.bytes / 1024).toFixed(1)}KB) \u2014 ${stats.file}`));
172465
172673
  console.log();
172466
172674
  console.log(content || paint.dim(" No global memories saved yet."));
172467
172675
  } else {
172468
- const projStats = (0, import_code_core16.memoryStats)("project", workDir);
172469
- 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");
172470
172678
  const projArchived = projStats.archived > 0 ? `+${projStats.archived} archived ` : "";
172471
172679
  const globalArchived = globalStats.archived > 0 ? `+${globalStats.archived} archived ` : "";
172472
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)`));
172473
172681
  console.log();
172474
- const merged = (0, import_code_core16.readAllMemory)(workDir, { includeArchived: wantsArchived });
172682
+ const merged = (0, import_code_core17.readAllMemory)(workDir, { includeArchived: wantsArchived });
172475
172683
  console.log(merged || paint.dim(" No memories saved yet."));
172476
172684
  }
172477
172685
  console.log();
@@ -172506,12 +172714,12 @@ ${dirList}`;
172506
172714
  console.log(paint.dim(` Current: ${paint.cyan(agentMode)}`));
172507
172715
  for (const m2 of ["ask", "edit", "auto", "plan"]) {
172508
172716
  const marker = m2 === agentMode ? paint.cyan("\u276F") : " ";
172509
- 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 `, ""))}`);
172510
172718
  }
172511
172719
  } else if (["ask", "edit", "plan", "auto"].includes(modeArg)) {
172512
172720
  agentMode = modeArg;
172513
172721
  updateFooter();
172514
- 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 `, "")}`));
172515
172723
  } else {
172516
172724
  console.log(paint.red(` Unknown: ${modeArg}`));
172517
172725
  }
@@ -172552,22 +172760,22 @@ ${dirList}`;
172552
172760
  if (target === void 0) {
172553
172761
  console.log(paint.red(" Usage: /team use <team id|personal> (/team lists the ids)"));
172554
172762
  } else {
172555
- await (0, import_code_core16.setBillingTarget)(target);
172763
+ await (0, import_code_core17.setBillingTarget)(target);
172556
172764
  console.log(paint.green(` \u2713 Usage will now be billed to ${target === null ? "your personal wallet" : `team ${target}`}.`));
172557
172765
  if (target !== null)
172558
172766
  console.log(paint.dim(" This applies to every product and client on this account, not just the CLI."));
172559
172767
  }
172560
172768
  } else {
172561
172769
  try {
172562
- const orgs = await (0, import_code_core16.getOrgs)();
172563
- const pin = (0, import_code_core16.billingOverrideFields)();
172770
+ const orgs = await (0, import_code_core17.getOrgs)();
172771
+ const pin = (0, import_code_core17.billingOverrideFields)();
172564
172772
  for (const l2 of billingLines(orgs, "teamId" in pin ? pin.teamId : void 0)) {
172565
172773
  console.log(l2.startsWith("!") ? paint.yellow(" " + l2) : " " + l2);
172566
172774
  }
172567
172775
  } catch (orgErr) {
172568
172776
  if (cmd === "/team")
172569
172777
  throw orgErr;
172570
- const bal = await (0, import_code_core16.getBalance)();
172778
+ const bal = await (0, import_code_core17.getBalance)();
172571
172779
  console.log(paint.dim(" Balance: ") + paint.cyan(`$${bal.toFixed(4)}`));
172572
172780
  }
172573
172781
  }
@@ -172961,7 +173169,7 @@ ${dirList}`;
172961
173169
  }
172962
173170
  agentMode = "plan";
172963
173171
  updateFooter();
172964
- 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)`));
172965
173173
  rl.prompt();
172966
173174
  return;
172967
173175
  }
@@ -172969,7 +173177,7 @@ ${dirList}`;
172969
173177
  const parts2 = arg.trim().split(/\s+/).filter(Boolean);
172970
173178
  const scope = parts2.includes("--project") ? "project" : "user";
172971
173179
  const want = (parts2.find((x2) => x2 !== "--project") ?? "").toLowerCase();
172972
- const customColorsNow = () => readThemeConfig((0, import_code_core18.loadSettings)(workDir).raw).custom;
173180
+ const customColorsNow = () => readThemeConfig((0, import_code_core19.loadSettings)(workDir).raw).custom;
172973
173181
  const switchTheme = (name, persist) => {
172974
173182
  const out = applyThemeConfig({ theme: name, custom: name === "custom" ? customColorsNow() : {}, errors: [] });
172975
173183
  activeThemeName = name;
@@ -172987,7 +173195,7 @@ ${dirList}`;
172987
173195
  const w = clearThemeKeys(workDir, scope);
172988
173196
  if (!w.ok)
172989
173197
  console.log(paint.yellow(` Could not write ${w.path}: ${w.error}`));
172990
- const after = readThemeConfig((0, import_code_core18.loadSettings)(workDir).raw);
173198
+ const after = readThemeConfig((0, import_code_core19.loadSettings)(workDir).raw);
172991
173199
  const out = applyThemeConfig(after);
172992
173200
  activeThemeName = after.theme;
172993
173201
  console.log(" " + paint.green(`Theme reset: ${out.resolved}`) + paint.dim(` \xB7 removed theme/themeColors from ${settingsPath(workDir, scope)}`));
@@ -173078,7 +173286,7 @@ ${dirList}`;
173078
173286
  }
173079
173287
  const types3 = (() => {
173080
173288
  try {
173081
- return (0, import_code_core16.loadAgentTypesWithWarnings)(workDir).types.map((t2) => ({
173289
+ return (0, import_code_core17.loadAgentTypesWithWarnings)(workDir).types.map((t2) => ({
173082
173290
  name: t2.name,
173083
173291
  description: t2.description,
173084
173292
  ...t2.tools ? { tools: t2.tools } : {},
@@ -173091,7 +173299,7 @@ ${dirList}`;
173091
173299
  for (const l2 of formatPermissions({
173092
173300
  rules: permRules,
173093
173301
  workDir,
173094
- mode: (0, import_code_core18.parseMode)(agentMode),
173302
+ mode: (0, import_code_core19.parseMode)(agentMode),
173095
173303
  yolo: isYoloMode(),
173096
173304
  agents: types3,
173097
173305
  // The guard the dashboard describes is the one the handler actually runs, so the
@@ -173296,7 +173504,7 @@ ${caption}` : `[Attached: ${rel}]`;
173296
173504
  if (needsSidecar) {
173297
173505
  console.log(paint.dim(` ${resolveModelLabel(modelAlias)} can't read ${isPdf ? "PDFs" : "images"} directly \u2014 describing ${rel} with Claude Sonnet 5.5 first\u2026`));
173298
173506
  try {
173299
- 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);
173300
173508
  content.push({ type: "text", text: `<attachment_description>
173301
173509
  ${text}
173302
173510
  </attachment_description>` });
@@ -173340,7 +173548,7 @@ ${text}
173340
173548
  process.exit(0);
173341
173549
  }
173342
173550
  case "/plugins": {
173343
- const plugins = (0, import_code_core16.loadPlugins)(workDir);
173551
+ const plugins = (0, import_code_core17.loadPlugins)(workDir);
173344
173552
  if (!plugins.length) {
173345
173553
  console.log(paint.dim(" No plugins installed. Install one: nex plugin install owner/repo"));
173346
173554
  } else {
@@ -173353,7 +173561,7 @@ ${text}
173353
173561
  }
173354
173562
  console.log();
173355
173563
  }
173356
- skills = (0, import_code_core16.loadSkills)(workDir);
173564
+ skills = (0, import_code_core17.loadSkills)(workDir);
173357
173565
  syncSlashMenu();
173358
173566
  rl.prompt();
173359
173567
  return;
@@ -173544,7 +173752,7 @@ ${text}
173544
173752
  return;
173545
173753
  }
173546
173754
  case "/handoff": {
173547
- 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);
173548
173756
  const parsed = parseHandoffArgs(arg, types3);
173549
173757
  if ("error" in parsed) {
173550
173758
  console.log(paint.red(" " + parsed.error));
@@ -173577,8 +173785,8 @@ ${text}
173577
173785
  return;
173578
173786
  }
173579
173787
  const measure = measureContext(messages, parsed.all ? TOP_ALL : TOP_DEFAULT);
173580
- const window2 = (0, import_code_core16.contextWindowFor)(modelAlias);
173581
- 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);
173582
173790
  for (const line of formatContextPanel({
173583
173791
  measure,
173584
173792
  window: window2,
@@ -173595,9 +173803,9 @@ ${text}
173595
173803
  }
173596
173804
  case "/skills":
173597
173805
  case "/commands": {
173598
- skills = (0, import_code_core16.loadSkills)(workDir);
173806
+ skills = (0, import_code_core17.loadSkills)(workDir);
173599
173807
  syncSlashMenu();
173600
- const invocable = (0, import_code_core16.userInvokableSkills)(skills);
173808
+ const invocable = (0, import_code_core17.userInvokableSkills)(skills);
173601
173809
  if (!invocable.length) {
173602
173810
  console.log(paint.dim(" No skills. Add .nexrall/skills/<name>/SKILL.md (or .nexrall/commands/<name>.md) to create one."));
173603
173811
  } else {
@@ -173773,15 +173981,15 @@ ${text}
173773
173981
  return;
173774
173982
  }
173775
173983
  default: {
173776
- const custom = (0, import_code_core16.findSkill)(skills, cmd);
173984
+ const custom = (0, import_code_core17.findSkill)(skills, cmd);
173777
173985
  if (!custom) {
173778
173986
  console.log(paint.red(` Unknown command: ${cmd}`));
173779
173987
  console.log(paint.dim(" Type /help for built-ins or /skills for custom ones."));
173780
173988
  rl.prompt();
173781
173989
  return;
173782
173990
  }
173783
- const expanded = (0, import_code_core16.expandSkill)(custom, arg, workDir);
173784
- 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;
173785
173993
  const turnMode = modeLocked ? "plan" : custom.mode ?? agentMode;
173786
173994
  console.log(paint.dim(` Running /${custom.name}\u2026`));
173787
173995
  checkpoints.beginTurn(`/${custom.name} ${arg}`.trim(), messages.length);
@@ -173816,7 +174024,7 @@ ${text}
173816
174024
  }
173817
174025
  checkpoints.beginTurn(userInput, messages.length);
173818
174026
  const mentions = expandMentions(userInput, workDir, {
173819
- 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"
173820
174028
  });
173821
174029
  for (const a of mentions.attachments) {
173822
174030
  console.log(paint.dim(` + ${a.kind === "dir" ? a.rel + "/" : a.rel} (${a.lines} ${a.kind === "dir" ? "entries" : "lines"}${a.truncated ? ", truncated" : ""})`));
@@ -173857,7 +174065,7 @@ ${text}
173857
174065
  const notes = cliBackgroundAgents.takeNotifications();
173858
174066
  console.log(paint.dim(` \u2699 background agent${notes.length > 1 ? "s" : ""} ${notes.map((n) => `"${n.description}"`).join(", ")} finished \u2014 continuing`));
173859
174067
  notifier.notify(bgAgentNote(notes.map((n) => n.description)));
173860
- rl.emit("line", (0, import_code_core16.formatBackgroundNotifications)(notes));
174068
+ rl.emit("line", (0, import_code_core17.formatBackgroundNotifications)(notes));
173861
174069
  }, 400);
173862
174070
  }
173863
174071
  cliBackgroundAgents.onChange(() => {
@@ -173879,7 +174087,7 @@ ${text}
173879
174087
 
173880
174088
  // src/commands/plugin.ts
173881
174089
  var import_prompts = __toESM(require_prompts3(), 1);
173882
- var import_code_core19 = __toESM(require_dist3(), 1);
174090
+ var import_code_core20 = __toESM(require_dist3(), 1);
173883
174091
  function describeInspection(i2, name, ctx) {
173884
174092
  console.log();
173885
174093
  console.log(paint.bold(` Plugin: ${name}`) + (i2.meta.version ? paint.dim(` v${i2.meta.version}`) : ""));
@@ -173955,14 +174163,14 @@ async function pluginInstallCommand(spec, opts) {
173955
174163
  let registryName;
173956
174164
  const isShortName = /^[\w.-]+$/.test(spec.trim()) && !spec.includes("/");
173957
174165
  if (isShortName) {
173958
- 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 });
173959
174167
  if (alias) {
173960
174168
  source2 = alias.spec;
173961
174169
  console.log(paint.dim(` Source: ${alias.name} (${alias.tier}) \u2190 ${alias.spec}`));
173962
174170
  }
173963
174171
  }
173964
174172
  if (isShortName && source2 === spec) {
173965
- 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);
173966
174174
  if (entry) {
173967
174175
  source2 = entry.source;
173968
174176
  registryName = entry.name;
@@ -173977,7 +174185,7 @@ async function pluginInstallCommand(spec, opts) {
173977
174185
  return;
173978
174186
  }
173979
174187
  }
173980
- const res = await (0, import_code_core19.installPlugin)(source2, {
174188
+ const res = await (0, import_code_core20.installPlugin)(source2, {
173981
174189
  scope,
173982
174190
  workDir: process.cwd(),
173983
174191
  name: opts.name ?? registryName,
@@ -173985,7 +174193,7 @@ async function pluginInstallCommand(spec, opts) {
173985
174193
  confirm: (i2, name, ctx) => confirmInstall(i2, name, opts.yes === true, ctx)
173986
174194
  });
173987
174195
  if (registryName)
173988
- (0, import_code_core19.reportInstall)(registryName);
174196
+ (0, import_code_core20.reportInstall)(registryName);
173989
174197
  console.log();
173990
174198
  console.log(paint.green(` \u2713 Installed "${res.name}" (${res.scope}) \u2192 ${res.dir}`) + (res.sha ? paint.dim(` @ ${res.sha.slice(0, 7)}`) : ""));
173991
174199
  if (res.inspection.commands.length) {
@@ -173999,7 +174207,7 @@ async function pluginInstallCommand(spec, opts) {
173999
174207
  }
174000
174208
  function pluginRemoveCommand(name, opts) {
174001
174209
  try {
174002
- const dir = (0, import_code_core19.removePlugin)(name, {
174210
+ const dir = (0, import_code_core20.removePlugin)(name, {
174003
174211
  scope: opts.project ? "project" : void 0,
174004
174212
  // no flag → search both scopes
174005
174213
  workDir: process.cwd()
@@ -174012,14 +174220,14 @@ function pluginRemoveCommand(name, opts) {
174012
174220
  }
174013
174221
  async function pluginUpdateCommand(name, opts) {
174014
174222
  const workDir = process.cwd();
174015
- 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);
174016
174224
  if (!targets.length) {
174017
174225
  console.log(paint.dim(" No updatable plugins (only GitHub-installed plugins with receipts can update)."));
174018
174226
  return;
174019
174227
  }
174020
174228
  for (const t2 of targets) {
174021
174229
  try {
174022
- const res = await (0, import_code_core19.updatePlugin)(t2, {
174230
+ const res = await (0, import_code_core20.updatePlugin)(t2, {
174023
174231
  workDir,
174024
174232
  confirm: (i2, n, ctx) => confirmInstall(i2, n, opts.yes === true, ctx)
174025
174233
  });
@@ -174036,7 +174244,7 @@ function badgeFor(p) {
174036
174244
  }
174037
174245
  async function pluginSearchCommand(query) {
174038
174246
  try {
174039
- const results = await (0, import_code_core19.searchRegistry)(query);
174247
+ const results = await (0, import_code_core20.searchRegistry)(query);
174040
174248
  if (!results.length) {
174041
174249
  console.log(paint.dim(query ? ` No plugins matching "${query}".` : " Registry is empty."));
174042
174250
  return;
@@ -174059,7 +174267,7 @@ async function pluginSearchCommand(query) {
174059
174267
  }
174060
174268
  async function pluginInfoCommand(name) {
174061
174269
  try {
174062
- const p = await (0, import_code_core19.getRegistryPlugin)(name);
174270
+ const p = await (0, import_code_core20.getRegistryPlugin)(name);
174063
174271
  if (!p) {
174064
174272
  console.error(paint.red(` \u2717 "${name}" not found in the registry.`));
174065
174273
  process.exitCode = 1;
@@ -174095,7 +174303,7 @@ async function pluginInfoCommand(name) {
174095
174303
  }
174096
174304
  }
174097
174305
  function pluginListCommand() {
174098
- const plugins = (0, import_code_core19.loadPlugins)(process.cwd());
174306
+ const plugins = (0, import_code_core20.loadPlugins)(process.cwd());
174099
174307
  if (!plugins.length) {
174100
174308
  console.log(paint.dim(" No plugins installed."));
174101
174309
  console.log(paint.dim(" Install one: nex plugin install owner/repo"));
@@ -174104,10 +174312,10 @@ function pluginListCommand() {
174104
174312
  console.log();
174105
174313
  console.log(paint.bold(" Installed plugins:"));
174106
174314
  for (const p of plugins) {
174107
- const receipt = (0, import_code_core19.readReceipt)(p.dir);
174315
+ const receipt = (0, import_code_core20.readReceipt)(p.dir);
174108
174316
  const src = receipt ? paint.dim(` \u2190 ${receipt.source}`) : paint.dim(" (manual)");
174109
174317
  const at = receipt?.sha ? paint.dim(` @ ${receipt.sha.slice(0, 7)}`) : "";
174110
- const insp = (0, import_code_core19.inspectPluginDir)(p.dir);
174318
+ const insp = (0, import_code_core20.inspectPluginDir)(p.dir);
174111
174319
  const runs2 = insp.hasHooks || insp.hasMcp ? paint.yellow(` \u26A0 ${[insp.hasHooks && "hooks", insp.hasMcp && "mcp"].filter(Boolean).join("+")}`) : "";
174112
174320
  console.log(
174113
174321
  " " + paint.cyan(p.name.padEnd(24)) + paint.dim(`${(p.version ?? "").padEnd(10)}${p.scope.padEnd(9)}`) + src + at + runs2
@@ -174117,8 +174325,8 @@ function pluginListCommand() {
174117
174325
  }
174118
174326
  function pluginSourceListCommand() {
174119
174327
  const workDir = process.cwd();
174120
- const sources = (0, import_code_core19.loadDeclaredSources)(workDir, { includeProject: true });
174121
- 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)();
174122
174330
  if (patterns) {
174123
174331
  console.log();
174124
174332
  console.log(paint.bold(" Organisation policy: ") + paint.dim(`installs restricted to ${patterns.join(", ")}`));
@@ -174133,7 +174341,7 @@ function pluginSourceListCommand() {
174133
174341
  console.log(paint.bold(" Declared plugin sources:"));
174134
174342
  for (const s2 of sources) {
174135
174343
  const tier2 = s2.tier === "project" ? paint.yellow(s2.tier.padEnd(9)) : paint.dim(s2.tier.padEnd(9));
174136
- 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") : "";
174137
174345
  console.log(" " + paint.cyan(s2.name.padEnd(20)) + tier2 + paint.white(s2.spec) + blocked);
174138
174346
  }
174139
174347
  console.log();
@@ -174143,17 +174351,17 @@ function pluginSourceListCommand() {
174143
174351
  }
174144
174352
  function pluginSourceAddCommand(name, spec, opts) {
174145
174353
  try {
174146
- (0, import_code_core19.parsePluginSource)(spec);
174147
- const patterns = (0, import_code_core19.allowedSourcePatterns)();
174148
- 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)) {
174149
174357
  console.error(paint.red(` \u2717 "${spec}" is not permitted by your organisation's policy.`));
174150
174358
  console.error(paint.dim(` Allowed: ${patterns.join(", ")}`));
174151
174359
  process.exitCode = 1;
174152
174360
  return;
174153
174361
  }
174154
174362
  const scope = opts.project ? "project" : "user";
174155
- const file = (0, import_code_core19.sourcesFileFor)(scope, process.cwd());
174156
- (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);
174157
174365
  console.log();
174158
174366
  console.log(paint.green(` \u2713 Declared "${name}" \u2192 ${spec}`) + paint.dim(` (${scope})`));
174159
174367
  console.log(paint.dim(` ${file}`));
@@ -174167,13 +174375,13 @@ function pluginSourceAddCommand(name, spec, opts) {
174167
174375
  function pluginSourceRemoveCommand(name, opts) {
174168
174376
  const workDir = process.cwd();
174169
174377
  const scope = opts.project ? "project" : "user";
174170
- const file = (0, import_code_core19.sourcesFileFor)(scope, workDir);
174171
- 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)) {
174172
174380
  console.log(paint.green(` \u2713 Removed source "${name}" (${scope})`));
174173
174381
  console.log(paint.dim(" Plugins already installed from it are untouched \u2014 remove them with nex plugin remove <name>"));
174174
174382
  return;
174175
174383
  }
174176
- const found = (0, import_code_core19.resolveSourceAlias)(name, workDir, { includeProject: true });
174384
+ const found = (0, import_code_core20.resolveSourceAlias)(name, workDir, { includeProject: true });
174177
174385
  if (found) {
174178
174386
  console.error(paint.red(` \u2717 "${name}" is declared in the ${found.tier} tier, not ${scope}.`));
174179
174387
  console.error(paint.dim(` ${found.file}`));
@@ -174188,7 +174396,7 @@ function pluginSourceRemoveCommand(name, opts) {
174188
174396
  }
174189
174397
 
174190
174398
  // src/index.ts
174191
- var import_code_core21 = __toESM(require_dist3(), 1);
174399
+ var import_code_core22 = __toESM(require_dist3(), 1);
174192
174400
 
174193
174401
  // src/commands/bgJobs.ts
174194
174402
  import * as fs26 from "node:fs";
@@ -174417,7 +174625,7 @@ var program2 = new Command();
174417
174625
  program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version(NEX_VERSION).enablePositionalOptions();
174418
174626
  var auth = program2.command("auth").description("Login to your Nexrall account").action(authCommand);
174419
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) => {
174420
- const r2 = authStatusReport((0, import_code_core21.loadAuth)(), process.env);
174628
+ const r2 = authStatusReport((0, import_code_core22.loadAuth)(), process.env);
174421
174629
  if (opts.json)
174422
174630
  console.log(JSON.stringify(r2));
174423
174631
  else if (!r2.loggedIn)
@@ -174663,8 +174871,8 @@ program2.command("sessions").description("List, export, import, or delete saved
174663
174871
  console.log(paint.dim(" nex sessions --events <id> [--json] to follow its event stream"));
174664
174872
  console.log();
174665
174873
  });
174666
- 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) => {
174667
- 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)()) {
174668
174876
  console.error("Not logged in. Run: nex auth");
174669
174877
  process.exit(1);
174670
174878
  }
@@ -174730,11 +174938,11 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174730
174938
  console.error(st.error);
174731
174939
  process.exit(1);
174732
174940
  }
174733
- (0, import_code_core21.setSessionSettings)(st.value);
174941
+ (0, import_code_core22.setSessionSettings)(st.value);
174734
174942
  }
174735
174943
  const allowRules = parseToolList(options.allowedTools);
174736
174944
  const denyRules = parseToolList(options.disallowedTools);
174737
- (0, import_code_core21.setSessionRules)({ allow: allowRules, deny: denyRules });
174945
+ (0, import_code_core22.setSessionRules)({ allow: allowRules, deny: denyRules });
174738
174946
  let budgetUsd;
174739
174947
  if (options.maxBudgetUsd !== void 0) {
174740
174948
  const b = parseBudget(options.maxBudgetUsd);
@@ -174755,7 +174963,16 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174755
174963
  }
174756
174964
  maxTurns = t2.value;
174757
174965
  }
174758
- 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 });
174759
174976
  if (headlessErr) {
174760
174977
  console.error(headlessErr);
174761
174978
  process.exit(1);
@@ -174779,8 +174996,8 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174779
174996
  }
174780
174997
  let selectedAgent;
174781
174998
  if (options.agent) {
174782
- const catalogue = (0, import_code_core21.loadAgentTypesWithWarnings)(process.cwd(), extraTypes).types;
174783
- 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));
174784
175001
  if (!selectedAgent) {
174785
175002
  console.error(`Unknown agent "${options.agent}". Available: ${catalogue.map((t2) => t2.name).join(", ")}`);
174786
175003
  process.exit(1);
@@ -174802,7 +175019,7 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174802
175019
  }
174803
175020
  let onlyTools;
174804
175021
  if (options.tools !== void 0) {
174805
- 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)());
174806
175023
  if (!t2.ok) {
174807
175024
  console.error(t2.error);
174808
175025
  process.exit(1);
@@ -174944,7 +175161,7 @@ program2.option("-p, --pro", "Use Claude Opus 5.5 (most capable)").option("-t, -
174944
175161
  }
174945
175162
  effort = lvl;
174946
175163
  }
174947
- 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 });
174948
175165
  });
174949
175166
  program2.parse();
174950
175167
  /*! Bundled license information: