nexrall-code 0.5.123 → 0.5.124

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 +508 -416
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -104964,60 +104964,361 @@ var require_rules = __commonJS({
104964
104964
  }
104965
104965
  });
104966
104966
 
104967
- // ../core/dist/permissions/bashClassify.js
104968
- var require_bashClassify = __commonJS({
104969
- "../core/dist/permissions/bashClassify.js"(exports2) {
104967
+ // ../core/dist/agent/planMode.js
104968
+ var require_planMode = __commonJS({
104969
+ "../core/dist/agent/planMode.js"(exports2) {
104970
104970
  "use strict";
104971
104971
  Object.defineProperty(exports2, "__esModule", { value: true });
104972
- exports2.bashNeedsRepoLock = bashNeedsRepoLock;
104973
- var MUTATING_BASH_VERBS = /* @__PURE__ */ new Set([
104974
- "git",
104975
- "npm",
104976
- "pnpm",
104977
- "yarn",
104978
- "bun",
104979
- "cargo",
104980
- "go",
104981
- "pip",
104982
- "pip3",
104983
- "poetry",
104984
- "uv",
104985
- "bundle",
104986
- "composer",
104987
- "gradle",
104988
- "mvn",
104989
- "make",
104990
- "terraform",
104991
- "helm",
104992
- "docker",
104993
- "vsce",
104994
- "tsc",
104995
- "pytest",
104996
- "dotnet",
104997
- "swift",
104998
- "gem"
104972
+ exports2.PLAN_MODE_INSTRUCTIONS = void 0;
104973
+ exports2.isReadOnlyCommand = isReadOnlyCommand;
104974
+ exports2.isReadOnlyPipeline = isReadOnlyPipeline;
104975
+ exports2.checkPlanMode = checkPlanMode;
104976
+ var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
104977
+ "read_file",
104978
+ "search_files",
104979
+ "glob",
104980
+ "list_directory",
104981
+ "notebook_read",
104982
+ // Planning bookkeeping. todo_write persists only to the session's own todo
104983
+ // list, not the repo, so it stays available — a plan mode that cannot draft
104984
+ // a checklist is missing the point of plan mode.
104985
+ "todo_write",
104986
+ "todo_read",
104987
+ // Reading memory is fine; memory_write is NOT here on purpose. Memory is
104988
+ // durable state that survives the session, so writing it is a real mutation
104989
+ // even though no file in the repo changes.
104990
+ "memory_read",
104991
+ "use_skill",
104992
+ "fetch_url",
104993
+ "web_search",
104994
+ // Delegating research is ALLOWED, and this is safe rather than a hole:
104995
+ // runSubTask passes planMode down, so the sub-agent is under the same lock and
104996
+ // its own write tools are refused by this very function one level deeper.
104997
+ //
104998
+ // Worth allowing rather than blanket-refusing, because plan mode exists to
104999
+ // support deep research and the explorer agent is the cheapest way to do bulk
105000
+ // searching without flooding the main context. Blocking `task` would have made
105001
+ // the mode's headline use case worse, for no security gain.
105002
+ "task",
105003
+ // VS Code language server — all pure queries.
105004
+ "get_symbols",
105005
+ "get_workspace_symbols",
105006
+ "find_references",
105007
+ "go_to_definition",
105008
+ "get_hover",
105009
+ "get_diagnostics",
105010
+ // Reading output from an ALREADY-running background shell. Starting one is
105011
+ // gated with the rest of bash below; draining a buffer is not a new effect.
105012
+ "bash_output"
104999
105013
  ]);
105000
- function bashNeedsRepoLock(command) {
105001
- if (!command)
105002
- return false;
105003
- const segments = command.split(/\|\||&&|[;|&\n()]|\$\(/);
105004
- for (const seg of segments) {
105005
- for (const tokenRaw of seg.trim().split(/\s+/)) {
105006
- const token = tokenRaw.replace(/^["']|["']$/g, "");
105007
- if (!token)
105008
- continue;
105009
- if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
105010
- continue;
105011
- if (token === "sudo" || token === "time" || token === "nice" || token === "env")
105012
- continue;
105013
- const verb = token.split("/").pop() ?? token;
105014
- if (MUTATING_BASH_VERBS.has(verb))
105014
+ var READ_ONLY_BASH = /* @__PURE__ */ new Set([
105015
+ "ls",
105016
+ "cat",
105017
+ "head",
105018
+ "tail",
105019
+ "wc",
105020
+ "file",
105021
+ "stat",
105022
+ "du",
105023
+ "df",
105024
+ "pwd",
105025
+ "which",
105026
+ "type",
105027
+ "echo",
105028
+ "printf",
105029
+ "basename",
105030
+ "dirname",
105031
+ "realpath",
105032
+ "readlink",
105033
+ "grep",
105034
+ "egrep",
105035
+ "fgrep",
105036
+ "rg",
105037
+ "ag",
105038
+ "ack",
105039
+ "find",
105040
+ "fd",
105041
+ "locate",
105042
+ "diff",
105043
+ "comm",
105044
+ "cmp",
105045
+ "sort",
105046
+ "uniq",
105047
+ "cut",
105048
+ "tr",
105049
+ "column",
105050
+ "jq",
105051
+ "yq",
105052
+ "date",
105053
+ "whoami",
105054
+ "hostname",
105055
+ "uname",
105056
+ "env",
105057
+ "printenv",
105058
+ "id",
105059
+ "groups",
105060
+ "ps",
105061
+ "uptime",
105062
+ "free",
105063
+ "node",
105064
+ "python",
105065
+ "python3",
105066
+ // `less`/`more`/`top` are deliberately NOT here: pagers run `!cmd` from inside and
105067
+ // `top` never exits. `env` is listed but only bare — see toolFlagsOk.
105068
+ "tree",
105069
+ "nl",
105070
+ "tac",
105071
+ "strings",
105072
+ "md5sum",
105073
+ "sha256sum",
105074
+ "true",
105075
+ "false",
105076
+ "test",
105077
+ "sleep",
105078
+ "seq",
105079
+ "expr",
105080
+ // Changes this shell's cwd only. Needed so `cd x && ls` is not refused.
105081
+ "cd"
105082
+ ]);
105083
+ var SYSTEM_BIN_DIRS = /* @__PURE__ */ new Set([
105084
+ "/bin",
105085
+ "/usr/bin",
105086
+ "/usr/local/bin",
105087
+ "/sbin",
105088
+ "/usr/sbin",
105089
+ "/opt/homebrew/bin"
105090
+ ]);
105091
+ var UNSAFE_ENV = /^(PATH|HOME|SHELL|IFS|ENV|BASH_ENV|PAGER|MANPAGER|EDITOR|VISUAL|BROWSER|LD_\w*|DYLD_\w*|GIT_\w*|NODE_\w*|NPM_\w*|npm_\w*|PYTHON\w*|BASH\w*|RUBY\w*|PERL\w*|LESS\w*|XDG_\w*|\w*OPTS?)=/i;
105092
+ var GH_NOUNS = /* @__PURE__ */ new Set(["pr", "issue", "run", "repo", "release", "workflow", "gist", "label"]);
105093
+ var READ_ONLY_SUBCOMMANDS = {
105094
+ git: /* @__PURE__ */ new Set([
105095
+ "log",
105096
+ "show",
105097
+ "diff",
105098
+ "status",
105099
+ "blame",
105100
+ "branch",
105101
+ "tag",
105102
+ "remote",
105103
+ "describe",
105104
+ "rev-parse",
105105
+ "rev-list",
105106
+ "ls-files",
105107
+ "ls-tree",
105108
+ "ls-remote",
105109
+ "cat-file",
105110
+ "shortlog",
105111
+ "reflog",
105112
+ "whatchanged",
105113
+ "grep",
105114
+ "config",
105115
+ "check-ignore",
105116
+ "merge-base",
105117
+ "name-rev",
105118
+ "count-objects",
105119
+ "verify-commit"
105120
+ ]),
105121
+ // Package managers: only their query verbs.
105122
+ npm: /* @__PURE__ */ new Set(["ls", "list", "view", "info", "outdated", "why", "config", "ping", "search"]),
105123
+ pnpm: /* @__PURE__ */ new Set(["ls", "list", "view", "info", "outdated", "why", "config"]),
105124
+ yarn: /* @__PURE__ */ new Set(["list", "info", "why", "config"]),
105125
+ cargo: /* @__PURE__ */ new Set(["tree", "metadata", "search"]),
105126
+ go: /* @__PURE__ */ new Set(["list", "version", "env", "vet", "doc"]),
105127
+ docker: /* @__PURE__ */ new Set(["ps", "images", "logs", "inspect", "version", "info", "top", "port", "diff", "stats"]),
105128
+ kubectl: /* @__PURE__ */ new Set(["get", "describe", "logs", "explain", "top", "version", "api-resources", "api-versions"]),
105129
+ // gh: read verbs only. `gh run rerun`, `gh pr merge` etc. are excluded, and
105130
+ // the nested-verb check in isReadOnlyCommand handles `gh pr view` vs `gh pr merge`.
105131
+ gh: /* @__PURE__ */ new Set(["browse"])
105132
+ };
105133
+ var READ_ONLY_NESTED = {
105134
+ gh: /* @__PURE__ */ new Set(["view", "list", "status", "diff", "checks"])
105135
+ };
105136
+ var SHELL_CONTROL = /[\n\r;&|`]|\$\(|>>|>|<\(/;
105137
+ var isShort = (f3) => f3.startsWith("-") && !f3.startsWith("--");
105138
+ var flagName = (f3) => f3.split("=")[0];
105139
+ function toolFlagsOk(verb, args) {
105140
+ const flags = args.filter((a) => a.startsWith("-"));
105141
+ const pos = args.filter((a) => !a.startsWith("-"));
105142
+ switch (verb) {
105143
+ case "env":
105144
+ return args.length === 0;
105145
+ case "sort":
105146
+ return !flags.some((f3) => isShort(f3) && /^-[A-Za-z]*o/.test(f3) || f3.startsWith("--output") || f3.startsWith("--compress-program"));
105147
+ case "uniq":
105148
+ return pos.length <= 1;
105149
+ case "find":
105150
+ return !args.some((a) => ["-exec", "-execdir", "-delete", "-ok", "-okdir", "-fprint", "-fprint0", "-fprintf", "-fls"].includes(a));
105151
+ case "fd":
105152
+ return !flags.some((f3) => f3.startsWith("--exec") || isShort(f3) && /^-[A-Za-z]*[xX]/.test(f3));
105153
+ case "rg":
105154
+ return !flags.some((f3) => flagName(f3) === "--pre" || f3.startsWith("--hostname-bin"));
105155
+ case "ag":
105156
+ case "ack":
105157
+ return !flags.some((f3) => f3.startsWith("--pager"));
105158
+ case "yq":
105159
+ return !flags.some((f3) => isShort(f3) && /^-[A-Za-z]*i/.test(f3) || f3.startsWith("--inplace"));
105160
+ case "tree":
105161
+ return !flags.some((f3) => isShort(f3) && /^-[A-Za-z]*o/.test(f3));
105162
+ case "date":
105163
+ return !flags.some((f3) => isShort(f3) && /^-[A-Za-z]*s/.test(f3) || f3.startsWith("--set"));
105164
+ case "hostname":
105165
+ return pos.length === 0;
105166
+ case "file":
105167
+ return !flags.some((f3) => isShort(f3) && /^-[A-Za-z]*C/.test(f3) || f3.startsWith("--compile"));
105168
+ default:
105169
+ return true;
105170
+ }
105171
+ }
105172
+ function flagsWithin(flags, shorts, longs) {
105173
+ return flags.every((f3) => {
105174
+ if (isShort(f3))
105175
+ return [...f3.slice(1)].every((c) => shorts.includes(c) || /\d/.test(c));
105176
+ return longs.includes(flagName(f3));
105177
+ });
105178
+ }
105179
+ var LISTISH = ["-l", "--list", "--contains", "--no-contains", "--merged", "--no-merged", "--points-at"];
105180
+ function subcommandFlagsOk(verb, sub, args) {
105181
+ const flags = args.filter((a) => a.startsWith("-"));
105182
+ const pos = args.filter((a) => !a.startsWith("-"));
105183
+ const listish = flags.some((f3) => LISTISH.includes(flagName(f3)) || isShort(f3) && f3.includes("l"));
105184
+ if (verb === "git") {
105185
+ if (flags.some((f3) => f3.startsWith("--output") || f3 === "--ext-diff" || f3 === "-O" || f3.startsWith("--open-files-in-pager")))
105186
+ return false;
105187
+ switch (sub) {
105188
+ case "branch":
105189
+ return flagsWithin(flags, "arvlq", ["--all", "--remotes", "--verbose", "--list", "--show-current", "--contains", "--no-contains", "--merged", "--no-merged", "--points-at", "--sort", "--format", "--column", "--no-column", "--color", "--no-color", "--abbrev", "--no-abbrev", "--ignore-case", "--omit-empty"]) && (pos.length <= 1 || listish);
105190
+ case "tag":
105191
+ return flagsWithin(flags, "lnv", ["--list", "--contains", "--no-contains", "--merged", "--no-merged", "--points-at", "--sort", "--format", "--column", "--no-column", "--color", "--no-color", "--verify", "--ignore-case", "--omit-empty"]) && (pos.length <= 1 || listish || flags.some((f3) => f3 === "-v" || f3 === "--verify"));
105192
+ case "remote":
105193
+ return flagsWithin(flags, "vn", ["--verbose"]) && (pos[1] === void 0 || pos[1] === "show" || pos[1] === "get-url");
105194
+ case "config":
105195
+ return flagsWithin(flags, "lz", ["--get", "--get-all", "--get-regexp", "--list", "--show-origin", "--show-scope", "--global", "--local", "--system", "--worktree", "--name-only", "--null", "--includes", "--no-includes", "--type"]) && pos.length <= 2;
105196
+ case "reflog":
105197
+ return pos[1] !== "expire" && pos[1] !== "delete";
105198
+ default:
105015
105199
  return true;
105016
- break;
105017
105200
  }
105018
105201
  }
105019
- return false;
105202
+ if (sub === "config" && (verb === "npm" || verb === "pnpm" || verb === "yarn")) {
105203
+ return pos[1] === void 0 || pos[1] === "get" || pos[1] === "list" || pos[1] === "ls";
105204
+ }
105205
+ if (verb === "go") {
105206
+ return !flags.some((f3) => {
105207
+ const n = f3.replace(/^--?/, "-");
105208
+ return n === "-w" || n === "-u" || n.startsWith("-vettool") || n.startsWith("-toolexec") || n === "-exec";
105209
+ });
105210
+ }
105211
+ return true;
105212
+ }
105213
+ function firstWords(command) {
105214
+ return command.trim().split(/\s+/).filter(Boolean);
105215
+ }
105216
+ function isReadOnlyCommand(command) {
105217
+ const cmd = command.trim();
105218
+ if (!cmd)
105219
+ return false;
105220
+ if (SHELL_CONTROL.test(cmd))
105221
+ return false;
105222
+ const words = firstWords(cmd);
105223
+ if (!words.length)
105224
+ return false;
105225
+ let i2 = 0;
105226
+ while (i2 < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[i2])) {
105227
+ if (UNSAFE_ENV.test(words[i2]))
105228
+ return false;
105229
+ i2++;
105230
+ }
105231
+ if (i2 >= words.length)
105232
+ return false;
105233
+ let verb = words[i2];
105234
+ const slash = verb.lastIndexOf("/");
105235
+ if (slash >= 0) {
105236
+ if (!SYSTEM_BIN_DIRS.has(verb.slice(0, slash)))
105237
+ return false;
105238
+ verb = verb.slice(slash + 1);
105239
+ }
105240
+ if (verb === "sudo" || verb === "doas" || verb === "su")
105241
+ return false;
105242
+ const rest = words.slice(i2 + 1).filter((w) => !w.startsWith("-"));
105243
+ const subs = READ_ONLY_SUBCOMMANDS[verb];
105244
+ if (subs) {
105245
+ const sub = rest[0];
105246
+ if (!sub) {
105247
+ return true;
105248
+ }
105249
+ if (subs.has(sub))
105250
+ return subcommandFlagsOk(verb, sub, words.slice(i2 + 1));
105251
+ const nested = READ_ONLY_NESTED[verb];
105252
+ if (nested && rest[1] && nested.has(rest[1]) && (verb !== "gh" || GH_NOUNS.has(sub)))
105253
+ return true;
105254
+ return false;
105255
+ }
105256
+ if (!READ_ONLY_TOOLS.has(verb) && !READ_ONLY_BASH.has(verb))
105257
+ return false;
105258
+ if (verb === "node" || verb === "python" || verb === "python3") {
105259
+ return words.slice(i2 + 1).every((w) => w === "--version" || w === "-V" || w === "-v");
105260
+ }
105261
+ if (!toolFlagsOk(verb, words.slice(i2 + 1)))
105262
+ return false;
105263
+ return true;
105264
+ }
105265
+ var HARMLESS_REDIRECT = /\s*\d?>\s*&\d\b|\s*\d?>>?\s*\/dev\/null(?=\s|$)/g;
105266
+ function isReadOnlyPipeline(command) {
105267
+ const cmd = command.replace(HARMLESS_REDIRECT, " ").trim();
105268
+ if (!cmd)
105269
+ return false;
105270
+ if (/[>`]|\$\(|<\(/.test(cmd))
105271
+ return false;
105272
+ const segs = cmd.split(/\|\||&&|[;|\n\r]/).map((x2) => x2.trim()).filter(Boolean);
105273
+ if (!segs.length)
105274
+ return false;
105275
+ return segs.every((seg) => !seg.includes("&") && isReadOnlyCommand(seg));
105020
105276
  }
105277
+ function checkPlanMode(tool, input) {
105278
+ if (tool === "bash") {
105279
+ const command = typeof input.command === "string" ? input.command : "";
105280
+ if (input.run_in_background === true) {
105281
+ return {
105282
+ reason: "bash-not-read-only",
105283
+ message: "Plan mode is ON: background processes cannot be started. Finish researching and propose your plan; the user will leave plan mode if they want it carried out."
105284
+ };
105285
+ }
105286
+ if (isReadOnlyCommand(command))
105287
+ return null;
105288
+ return {
105289
+ reason: "bash-not-read-only",
105290
+ message: `Plan mode is ON, so \`bash\` is limited to provably read-only commands and this one was not recognised as such (chained commands, redirection and command substitution are always refused). Do NOT ask the user to approve it and do NOT try a variation to get around this \u2014 it is a session-wide lock, not a per-call prompt. Use read-only inspection instead, and put the command in your plan as a step to run once plan mode is off.`
105291
+ };
105292
+ }
105293
+ if (READ_ONLY_TOOLS.has(tool))
105294
+ return null;
105295
+ return {
105296
+ reason: "mutating-tool",
105297
+ message: `Plan mode is ON, so \`${tool}\` is unavailable \u2014 this session may not modify anything yet. Do NOT ask for approval: no answer the user gives at this prompt can unlock it, because it is a session-wide lock they control directly. Research with read-only tools and finish by proposing a concrete plan (files to change, in order, and how to verify). The user will exit plan mode to let you carry it out.`
105298
+ };
105299
+ }
105300
+ exports2.PLAN_MODE_INSTRUCTIONS = [
105301
+ "# PLAN MODE IS ACTIVE",
105302
+ "",
105303
+ "This session is READ-ONLY. You cannot edit files, write files, run mutating commands, or start",
105304
+ "background processes. This is enforced outside your control \u2014 it is not a permission prompt, and",
105305
+ "the user cannot approve an exception from inside this turn.",
105306
+ "",
105307
+ "Therefore:",
105308
+ '- NEVER ask "shall I go ahead and make this change?" \u2014 you cannot, whatever the answer.',
105309
+ '- NEVER attempt a mutating tool "just to see" \u2014 it will be refused and wastes the turn.',
105310
+ "- NEVER try to reach a blocked command another way (a different flag, a script, an interpreter).",
105311
+ "",
105312
+ "Your job is to research thoroughly, then deliver a plan:",
105313
+ "1. What you found \u2014 the specific files, functions and line numbers that matter.",
105314
+ "2. What you propose to change \u2014 file by file, in dependency order.",
105315
+ "3. Risks and unknowns \u2014 what could break, what you could not verify, what you assumed.",
105316
+ "4. How it will be verified \u2014 the exact build/test/lint command that proves it works.",
105317
+ "",
105318
+ 'Be concrete. "Update the auth logic" is not a plan; "add a `refreshToken` field to AuthConfig in',
105319
+ 'auth/index.ts:42, then thread it through client.ts:118" is. The user will review the plan and',
105320
+ "leave plan mode to have it carried out."
105321
+ ].join("\n");
105021
105322
  }
105022
105323
  });
105023
105324
 
@@ -105027,11 +105328,11 @@ var require_modePolicy = __commonJS({
105027
105328
  "use strict";
105028
105329
  Object.defineProperty(exports2, "__esModule", { value: true });
105029
105330
  exports2.BASH_TOOLS = exports2.FILE_MUTATE_TOOLS = exports2.FILE_WRITE_TOOLS = exports2.READ_ONLY_TOOLS = void 0;
105030
- exports2.isReadOnlyBash = isReadOnlyBash;
105331
+ exports2.isReadOnlyBash = isReadOnlyBash2;
105031
105332
  exports2.decide = decide;
105032
105333
  exports2.describeMode = describeMode2;
105033
105334
  exports2.parseMode = parseMode2;
105034
- var bashClassify_1 = require_bashClassify();
105335
+ var planMode_1 = require_planMode();
105035
105336
  exports2.READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
105036
105337
  "read_file",
105037
105338
  "list_directory",
@@ -105071,15 +105372,15 @@ var require_modePolicy = __commonJS({
105071
105372
  "copy_file"
105072
105373
  ]);
105073
105374
  exports2.BASH_TOOLS = /* @__PURE__ */ new Set(["bash", "run_command", "execute_command"]);
105074
- function isReadOnlyBash(command) {
105075
- return !(0, bashClassify_1.bashNeedsRepoLock)(command);
105375
+ function isReadOnlyBash2(command) {
105376
+ return (0, planMode_1.isReadOnlyPipeline)(command);
105076
105377
  }
105077
105378
  function decide(req) {
105078
105379
  const { tool, mode } = req;
105079
105380
  const command = String(req.input.command ?? req.input.cmd ?? "");
105080
105381
  if (exports2.READ_ONLY_TOOLS.has(tool))
105081
105382
  return "allow";
105082
- if (exports2.BASH_TOOLS.has(tool) && isReadOnlyBash(command) && !req.destructive)
105383
+ if (exports2.BASH_TOOLS.has(tool) && isReadOnlyBash2(command) && !req.destructive)
105083
105384
  return "allow";
105084
105385
  if (req.destructive)
105085
105386
  return "confirm";
@@ -105310,261 +105611,60 @@ var require_destructive = __commonJS({
105310
105611
  }
105311
105612
  });
105312
105613
 
105313
- // ../core/dist/agent/planMode.js
105314
- var require_planMode = __commonJS({
105315
- "../core/dist/agent/planMode.js"(exports2) {
105614
+ // ../core/dist/permissions/bashClassify.js
105615
+ var require_bashClassify = __commonJS({
105616
+ "../core/dist/permissions/bashClassify.js"(exports2) {
105316
105617
  "use strict";
105317
105618
  Object.defineProperty(exports2, "__esModule", { value: true });
105318
- exports2.PLAN_MODE_INSTRUCTIONS = void 0;
105319
- exports2.isReadOnlyCommand = isReadOnlyCommand;
105320
- exports2.checkPlanMode = checkPlanMode;
105321
- var READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
105322
- "read_file",
105323
- "search_files",
105324
- "glob",
105325
- "list_directory",
105326
- "notebook_read",
105327
- // Planning bookkeeping. todo_write persists only to the session's own todo
105328
- // list, not the repo, so it stays available — a plan mode that cannot draft
105329
- // a checklist is missing the point of plan mode.
105330
- "todo_write",
105331
- "todo_read",
105332
- // Reading memory is fine; memory_write is NOT here on purpose. Memory is
105333
- // durable state that survives the session, so writing it is a real mutation
105334
- // even though no file in the repo changes.
105335
- "memory_read",
105336
- "use_skill",
105337
- "fetch_url",
105338
- "web_search",
105339
- // Delegating research is ALLOWED, and this is safe rather than a hole:
105340
- // runSubTask passes planMode down, so the sub-agent is under the same lock and
105341
- // its own write tools are refused by this very function one level deeper.
105342
- //
105343
- // Worth allowing rather than blanket-refusing, because plan mode exists to
105344
- // support deep research and the explorer agent is the cheapest way to do bulk
105345
- // searching without flooding the main context. Blocking `task` would have made
105346
- // the mode's headline use case worse, for no security gain.
105347
- "task",
105348
- // VS Code language server — all pure queries.
105349
- "get_symbols",
105350
- "get_workspace_symbols",
105351
- "find_references",
105352
- "go_to_definition",
105353
- "get_hover",
105354
- "get_diagnostics",
105355
- // Reading output from an ALREADY-running background shell. Starting one is
105356
- // gated with the rest of bash below; draining a buffer is not a new effect.
105357
- "bash_output"
105358
- ]);
105359
- var READ_ONLY_BASH = /* @__PURE__ */ new Set([
105360
- "ls",
105361
- "cat",
105362
- "head",
105363
- "tail",
105364
- "wc",
105365
- "file",
105366
- "stat",
105367
- "du",
105368
- "df",
105369
- "pwd",
105370
- "which",
105371
- "type",
105372
- "echo",
105373
- "printf",
105374
- "basename",
105375
- "dirname",
105376
- "realpath",
105377
- "readlink",
105378
- "grep",
105379
- "egrep",
105380
- "fgrep",
105381
- "rg",
105382
- "ag",
105383
- "ack",
105384
- "find",
105385
- "fd",
105386
- "locate",
105387
- "diff",
105388
- "comm",
105389
- "cmp",
105390
- "sort",
105391
- "uniq",
105392
- "cut",
105393
- "tr",
105394
- "column",
105395
- "jq",
105396
- "yq",
105397
- "date",
105398
- "whoami",
105399
- "hostname",
105400
- "uname",
105401
- "env",
105402
- "printenv",
105403
- "id",
105404
- "groups",
105405
- "ps",
105406
- "top",
105407
- "uptime",
105408
- "free",
105409
- "node",
105410
- "python",
105411
- "python3",
105412
- "tree",
105413
- "less",
105414
- "more",
105415
- "nl",
105416
- "tac",
105417
- "strings",
105418
- "md5sum",
105419
- "sha256sum",
105420
- "true",
105421
- "false",
105422
- "test",
105423
- "sleep",
105424
- "seq",
105425
- "expr"
105619
+ exports2.bashNeedsRepoLock = bashNeedsRepoLock;
105620
+ var MUTATING_BASH_VERBS = /* @__PURE__ */ new Set([
105621
+ "git",
105622
+ "npm",
105623
+ "pnpm",
105624
+ "yarn",
105625
+ "bun",
105626
+ "cargo",
105627
+ "go",
105628
+ "pip",
105629
+ "pip3",
105630
+ "poetry",
105631
+ "uv",
105632
+ "bundle",
105633
+ "composer",
105634
+ "gradle",
105635
+ "mvn",
105636
+ "make",
105637
+ "terraform",
105638
+ "helm",
105639
+ "docker",
105640
+ "vsce",
105641
+ "tsc",
105642
+ "pytest",
105643
+ "dotnet",
105644
+ "swift",
105645
+ "gem"
105426
105646
  ]);
105427
- var READ_ONLY_SUBCOMMANDS = {
105428
- git: /* @__PURE__ */ new Set([
105429
- "log",
105430
- "show",
105431
- "diff",
105432
- "status",
105433
- "blame",
105434
- "branch",
105435
- "tag",
105436
- "remote",
105437
- "describe",
105438
- "rev-parse",
105439
- "rev-list",
105440
- "ls-files",
105441
- "ls-tree",
105442
- "ls-remote",
105443
- "cat-file",
105444
- "shortlog",
105445
- "reflog",
105446
- "whatchanged",
105447
- "grep",
105448
- "config",
105449
- "check-ignore",
105450
- "merge-base",
105451
- "name-rev",
105452
- "count-objects",
105453
- "verify-commit"
105454
- ]),
105455
- // Package managers: only their query verbs.
105456
- npm: /* @__PURE__ */ new Set(["ls", "list", "view", "info", "outdated", "why", "config", "ping", "search"]),
105457
- pnpm: /* @__PURE__ */ new Set(["ls", "list", "view", "info", "outdated", "why", "config"]),
105458
- yarn: /* @__PURE__ */ new Set(["list", "info", "why", "config"]),
105459
- cargo: /* @__PURE__ */ new Set(["tree", "metadata", "search"]),
105460
- go: /* @__PURE__ */ new Set(["list", "version", "env", "vet", "doc"]),
105461
- docker: /* @__PURE__ */ new Set(["ps", "images", "logs", "inspect", "version", "info", "top", "port", "diff", "stats"]),
105462
- kubectl: /* @__PURE__ */ new Set(["get", "describe", "logs", "explain", "top", "version", "api-resources", "api-versions"]),
105463
- // gh: read verbs only. `gh run rerun`, `gh pr merge` etc. are excluded, and
105464
- // the nested-verb check in isReadOnlyCommand handles `gh pr view` vs `gh pr merge`.
105465
- gh: /* @__PURE__ */ new Set(["browse"])
105466
- };
105467
- var READ_ONLY_NESTED = {
105468
- gh: /* @__PURE__ */ new Set(["view", "list", "status", "diff", "checks"])
105469
- };
105470
- var SHELL_CONTROL = /[\n\r;&|`]|\$\(|>>|>|<\(/;
105471
- function firstWords(command) {
105472
- return command.trim().split(/\s+/).filter(Boolean);
105473
- }
105474
- function isReadOnlyCommand(command) {
105475
- const cmd = command.trim();
105476
- if (!cmd)
105477
- return false;
105478
- if (SHELL_CONTROL.test(cmd))
105479
- return false;
105480
- const words = firstWords(cmd);
105481
- if (!words.length)
105482
- return false;
105483
- let i2 = 0;
105484
- while (i2 < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[i2]))
105485
- i2++;
105486
- if (i2 >= words.length)
105487
- return false;
105488
- let verb = words[i2];
105489
- const slash = verb.lastIndexOf("/");
105490
- if (slash >= 0)
105491
- verb = verb.slice(slash + 1);
105492
- if (verb === "sudo" || verb === "doas" || verb === "su")
105493
- return false;
105494
- const rest = words.slice(i2 + 1).filter((w) => !w.startsWith("-"));
105495
- const subs = READ_ONLY_SUBCOMMANDS[verb];
105496
- if (subs) {
105497
- const sub = rest[0];
105498
- if (!sub) {
105499
- return true;
105500
- }
105501
- if (subs.has(sub)) {
105502
- if (verb === "git" && sub === "config") {
105503
- const args = words.slice(i2 + 2).filter((w) => !w.startsWith("-"));
105504
- return args.length <= 1;
105505
- }
105506
- return true;
105507
- }
105508
- const nested = READ_ONLY_NESTED[verb];
105509
- if (nested && rest[1] && nested.has(rest[1]))
105510
- return true;
105511
- return false;
105512
- }
105513
- if (!READ_ONLY_TOOLS.has(verb) && !READ_ONLY_BASH.has(verb))
105514
- return false;
105515
- if (verb === "node" || verb === "python" || verb === "python3") {
105516
- return words.slice(i2 + 1).every((w) => w === "--version" || w === "-V" || w === "-v");
105517
- }
105518
- if (verb === "find" && words.some((w) => w === "-exec" || w === "-execdir" || w === "-delete")) {
105647
+ function bashNeedsRepoLock(command) {
105648
+ if (!command)
105519
105649
  return false;
105520
- }
105521
- return true;
105522
- }
105523
- function checkPlanMode(tool, input) {
105524
- if (tool === "bash") {
105525
- const command = typeof input.command === "string" ? input.command : "";
105526
- if (input.run_in_background === true) {
105527
- return {
105528
- reason: "bash-not-read-only",
105529
- message: "Plan mode is ON: background processes cannot be started. Finish researching and propose your plan; the user will leave plan mode if they want it carried out."
105530
- };
105650
+ const segments = command.split(/\|\||&&|[;|&\n()]|\$\(/);
105651
+ for (const seg of segments) {
105652
+ for (const tokenRaw of seg.trim().split(/\s+/)) {
105653
+ const token = tokenRaw.replace(/^["']|["']$/g, "");
105654
+ if (!token)
105655
+ continue;
105656
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
105657
+ continue;
105658
+ if (token === "sudo" || token === "time" || token === "nice" || token === "env")
105659
+ continue;
105660
+ const verb = token.split("/").pop() ?? token;
105661
+ if (MUTATING_BASH_VERBS.has(verb))
105662
+ return true;
105663
+ break;
105531
105664
  }
105532
- if (isReadOnlyCommand(command))
105533
- return null;
105534
- return {
105535
- reason: "bash-not-read-only",
105536
- message: `Plan mode is ON, so \`bash\` is limited to provably read-only commands and this one was not recognised as such (chained commands, redirection and command substitution are always refused). Do NOT ask the user to approve it and do NOT try a variation to get around this \u2014 it is a session-wide lock, not a per-call prompt. Use read-only inspection instead, and put the command in your plan as a step to run once plan mode is off.`
105537
- };
105538
105665
  }
105539
- if (READ_ONLY_TOOLS.has(tool))
105540
- return null;
105541
- return {
105542
- reason: "mutating-tool",
105543
- message: `Plan mode is ON, so \`${tool}\` is unavailable \u2014 this session may not modify anything yet. Do NOT ask for approval: no answer the user gives at this prompt can unlock it, because it is a session-wide lock they control directly. Research with read-only tools and finish by proposing a concrete plan (files to change, in order, and how to verify). The user will exit plan mode to let you carry it out.`
105544
- };
105666
+ return false;
105545
105667
  }
105546
- exports2.PLAN_MODE_INSTRUCTIONS = [
105547
- "# PLAN MODE IS ACTIVE",
105548
- "",
105549
- "This session is READ-ONLY. You cannot edit files, write files, run mutating commands, or start",
105550
- "background processes. This is enforced outside your control \u2014 it is not a permission prompt, and",
105551
- "the user cannot approve an exception from inside this turn.",
105552
- "",
105553
- "Therefore:",
105554
- '- NEVER ask "shall I go ahead and make this change?" \u2014 you cannot, whatever the answer.',
105555
- '- NEVER attempt a mutating tool "just to see" \u2014 it will be refused and wastes the turn.',
105556
- "- NEVER try to reach a blocked command another way (a different flag, a script, an interpreter).",
105557
- "",
105558
- "Your job is to research thoroughly, then deliver a plan:",
105559
- "1. What you found \u2014 the specific files, functions and line numbers that matter.",
105560
- "2. What you propose to change \u2014 file by file, in dependency order.",
105561
- "3. Risks and unknowns \u2014 what could break, what you could not verify, what you assumed.",
105562
- "4. How it will be verified \u2014 the exact build/test/lint command that proves it works.",
105563
- "",
105564
- 'Be concrete. "Update the auth logic" is not a plan; "add a `refreshToken` field to AuthConfig in',
105565
- 'auth/index.ts:42, then thread it through client.ts:118" is. The user will review the plan and',
105566
- "leave plan mode to have it carried out."
105567
- ].join("\n");
105568
105668
  }
105569
105669
  });
105570
105670
 
@@ -145175,7 +145275,7 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
145175
145275
  var source_default = chalk;
145176
145276
 
145177
145277
  // src/index.ts
145178
- var import_code_core9 = __toESM(require_dist3(), 1);
145278
+ var import_code_core10 = __toESM(require_dist3(), 1);
145179
145279
 
145180
145280
  // src/commands/auth.ts
145181
145281
  import * as readline from "readline";
@@ -145392,7 +145492,7 @@ import * as crypto4 from "crypto";
145392
145492
  import { createRequire } from "module";
145393
145493
  import { fileURLToPath as fileURLToPath2 } from "url";
145394
145494
  import { execSync, execFileSync as execFileSync3 } from "child_process";
145395
- var import_code_core6 = __toESM(require_dist3(), 1);
145495
+ var import_code_core7 = __toESM(require_dist3(), 1);
145396
145496
 
145397
145497
  // ../../node_modules/.pnpm/ansi-regex@6.2.2/node_modules/ansi-regex/index.js
145398
145498
  function ansiRegex({ onlyFirst = false } = {}) {
@@ -146350,17 +146450,9 @@ function showDiff(filePath, oldContent, newContent) {
146350
146450
  }
146351
146451
 
146352
146452
  // src/permissions/risk.ts
146453
+ var import_code_core3 = __toESM(require_dist3(), 1);
146353
146454
  import * as path from "path";
146354
146455
  var HIGH_BASH = /\b(sudo|rm|rmdir|chmod|chown|dd|mkfs|kill|killall|pkill|shutdown|reboot)\b|\b(curl|wget)\b[^|]*\|\s*(sudo\s+)?(ba|z)?sh\b|\b(ssh|scp|rsync)\b|\bgit\s+(push|reset\s+--hard|clean)\b/;
146355
- var READ_VERBS = /* @__PURE__ */ new Set(["ls", "cat", "head", "tail", "wc", "pwd", "echo", "which", "grep", "rg", "find", "stat", "file", "du", "df", "tree", "diff", "sort", "uniq", "date", "whoami"]);
146356
- function looksReadOnly(cmd) {
146357
- if (/[>`]|\$\(|\bfind\b[^|;&]*\s-(delete|exec|execdir)\b/.test(cmd))
146358
- return false;
146359
- return cmd.split(/\|\||&&|[;|&\n]/).every((seg) => {
146360
- const verb = seg.trim().split(/\s+/)[0] ?? "";
146361
- return verb === "" || READ_VERBS.has(verb.split("/").pop());
146362
- });
146363
- }
146364
146456
  function outsideProject(p, workDir) {
146365
146457
  if (!p)
146366
146458
  return false;
@@ -146376,7 +146468,7 @@ function assessRisk(tool, input, workDir, fileExists) {
146376
146468
  const cmd = String(input.command ?? input.cmd ?? "");
146377
146469
  if (HIGH_BASH.test(cmd))
146378
146470
  return { risk: "high", detail: `can delete, overwrite or reach the network${out}` };
146379
- if (looksReadOnly(cmd))
146471
+ if ((0, import_code_core3.isReadOnlyBash)(cmd))
146380
146472
  return { risk: "low", detail: "read-only command" };
146381
146473
  return { risk: "medium", detail: "runs code that may change files" };
146382
146474
  }
@@ -146396,18 +146488,18 @@ function assessRisk(tool, input, workDir, fileExists) {
146396
146488
  }
146397
146489
 
146398
146490
  // src/permissions/handler.ts
146399
- var import_code_core3 = __toESM(require_dist3(), 1);
146491
+ var import_code_core4 = __toESM(require_dist3(), 1);
146400
146492
  var autoApproved = /* @__PURE__ */ new Set();
146401
146493
  var _rules = { allow: [], ask: [], deny: [] };
146402
146494
  var _workDir = process.cwd();
146403
146495
  var _mode = "ask";
146404
146496
  function initPermissions(workDir) {
146405
146497
  _workDir = workDir;
146406
- _rules = (0, import_code_core3.loadSettings)(workDir).permissions;
146498
+ _rules = (0, import_code_core4.loadSettings)(workDir).permissions;
146407
146499
  return _rules;
146408
146500
  }
146409
146501
  function setMode(mode) {
146410
- _mode = (0, import_code_core3.parseMode)(mode);
146502
+ _mode = (0, import_code_core4.parseMode)(mode);
146411
146503
  }
146412
146504
  function setAutoApprove(category) {
146413
146505
  autoApproved.add(category);
@@ -146485,12 +146577,12 @@ function announceAgent(req) {
146485
146577
  }
146486
146578
  async function requestPermission(req) {
146487
146579
  const { tool, input } = req;
146488
- const decision = (0, import_code_core3.evaluatePermission)(_rules, tool, input, _workDir);
146580
+ const decision = (0, import_code_core4.evaluatePermission)(_rules, tool, input, _workDir);
146489
146581
  if (decision === "deny") {
146490
146582
  console.error(source_default.red(` \u2716 Denied by permission rule: ${tool}`));
146491
146583
  return false;
146492
146584
  }
146493
- const destructive = (0, import_code_core3.isDestructiveBash)(tool, input);
146585
+ const destructive = (0, import_code_core4.isDestructiveBash)(tool, input);
146494
146586
  if (destructive && process.env.NEXRALL_ALLOW_DESTRUCTIVE !== "1") {
146495
146587
  return serialisePrompt(async () => {
146496
146588
  announceAgent(req);
@@ -146511,7 +146603,7 @@ async function requestPermission(req) {
146511
146603
  }
146512
146604
  if (decision === "allow")
146513
146605
  return true;
146514
- const verdict = (0, import_code_core3.decide)({
146606
+ const verdict = (0, import_code_core4.decide)({
146515
146607
  tool,
146516
146608
  input,
146517
146609
  mode: _mode,
@@ -146671,7 +146763,7 @@ async function askFlow(req) {
146671
146763
  }
146672
146764
 
146673
146765
  // src/audit.ts
146674
- var import_code_core4 = __toESM(require_dist3(), 1);
146766
+ var import_code_core5 = __toESM(require_dist3(), 1);
146675
146767
  import * as path2 from "path";
146676
146768
  var _state;
146677
146769
  var _sinks = /* @__PURE__ */ new Map();
@@ -146691,7 +146783,7 @@ function currentAudit() {
146691
146783
  const target = resolveAuditPath(_state);
146692
146784
  let sink = _sinks.get(target);
146693
146785
  if (!sink) {
146694
- sink = (0, import_code_core4.createJsonlAuditSink)(target);
146786
+ sink = (0, import_code_core5.createJsonlAuditSink)(target);
146695
146787
  _sinks.set(target, sink);
146696
146788
  }
146697
146789
  const cfg = {
@@ -146704,14 +146796,14 @@ function currentAudit() {
146704
146796
  // request that caused it. Same trap as @nexrall/agent's and Desktop's.
146705
146797
  sessionId: _state.getSessionId()
146706
146798
  };
146707
- const email = (0, import_code_core4.loadAuth)()?.email;
146799
+ const email = (0, import_code_core5.loadAuth)()?.email;
146708
146800
  if (email)
146709
146801
  cfg.userId = email;
146710
146802
  return cfg;
146711
146803
  }
146712
146804
 
146713
146805
  // src/commands/chat.ts
146714
- var import_code_core7 = __toESM(require_dist3(), 1);
146806
+ var import_code_core8 = __toESM(require_dist3(), 1);
146715
146807
 
146716
146808
  // src/commands/sessions.ts
146717
146809
  import * as fs3 from "fs";
@@ -147145,7 +147237,7 @@ async function updateCommand(opts) {
147145
147237
  }
147146
147238
 
147147
147239
  // src/commands/sessionTools.ts
147148
- var import_code_core5 = __toESM(require_dist3(), 1);
147240
+ var import_code_core6 = __toESM(require_dist3(), 1);
147149
147241
  import { spawnSync, execFileSync } from "child_process";
147150
147242
  import fs5 from "fs";
147151
147243
  import path6 from "path";
@@ -147205,7 +147297,7 @@ function nextMode(current) {
147205
147297
  return MODE_CYCLE[(i2 + 1) % MODE_CYCLE.length];
147206
147298
  }
147207
147299
  function textOf(m2) {
147208
- return m2.content.filter((b) => b.type === "text" && !(0, import_code_core5.isRuntimeContextBlock)(b)).map((b) => b.text).join("\n").trim();
147300
+ return m2.content.filter((b) => b.type === "text" && !(0, import_code_core6.isRuntimeContextBlock)(b)).map((b) => b.text).join("\n").trim();
147209
147301
  }
147210
147302
  function lastAssistantText(messages) {
147211
147303
  for (let i2 = messages.length - 1; i2 >= 0; i2--) {
@@ -147231,7 +147323,7 @@ function exportMarkdown(messages, meta) {
147231
147323
  for (const m2 of messages) {
147232
147324
  const parts = [];
147233
147325
  for (const b of m2.content) {
147234
- if ((0, import_code_core5.isRuntimeContextBlock)(b))
147326
+ if ((0, import_code_core6.isRuntimeContextBlock)(b))
147235
147327
  continue;
147236
147328
  if (b.type === "text") {
147237
147329
  if (b.text.trim())
@@ -158021,7 +158113,7 @@ var MODEL_COST_MULTIPLIER = {
158021
158113
  "glm-5.3": 0.6
158022
158114
  };
158023
158115
  function modelWithCostHint(id) {
158024
- const mult = (0, import_code_core6.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
158116
+ const mult = (0, import_code_core7.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
158025
158117
  return mult === void 0 ? id : `${id} (${mult}x)`;
158026
158118
  }
158027
158119
  var NO_VISION_MODELS = /* @__PURE__ */ new Set(["qwen3.7-max", "glm-5.3"]);
@@ -158037,13 +158129,13 @@ var NO_PDF_MODELS = /* @__PURE__ */ new Set([
158037
158129
  ...NO_VISION_MODELS
158038
158130
  ]);
158039
158131
  function needsVisionSidecar(model) {
158040
- return !(0, import_code_core6.liveSupportsImageInput)(model, !NO_VISION_MODELS.has(model));
158132
+ return !(0, import_code_core7.liveSupportsImageInput)(model, !NO_VISION_MODELS.has(model));
158041
158133
  }
158042
158134
  function needsPdfSidecar(model) {
158043
- return !(0, import_code_core6.liveSupportsPdfInput)(model, !NO_PDF_MODELS.has(model));
158135
+ return !(0, import_code_core7.liveSupportsPdfInput)(model, !NO_PDF_MODELS.has(model));
158044
158136
  }
158045
158137
  function selectableModelIds() {
158046
- return (0, import_code_core6.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
158138
+ return (0, import_code_core7.liveSelectableModelIds)() ?? SELECTABLE_MODELS;
158047
158139
  }
158048
158140
  function normaliseModelId(model) {
158049
158141
  const raw = (model ?? "").trim();
@@ -158104,9 +158196,9 @@ var _BACKEND_STYLE_TO_LOCAL = {
158104
158196
  };
158105
158197
  function effortStyleFor(modelId) {
158106
158198
  const id = normaliseModelId(modelId);
158107
- if ((0, import_code_core6.liveVendorFor)(id) === "Anthropic")
158199
+ if ((0, import_code_core7.liveVendorFor)(id) === "Anthropic")
158108
158200
  return "anthropic";
158109
- const liveStyle = (0, import_code_core6.liveReasoningEffortStyle)(id);
158201
+ const liveStyle = (0, import_code_core7.liveReasoningEffortStyle)(id);
158110
158202
  if (liveStyle && _BACKEND_STYLE_TO_LOCAL[liveStyle])
158111
158203
  return _BACKEND_STYLE_TO_LOCAL[liveStyle];
158112
158204
  return MODEL_EFFORT_STYLE[id] ?? "anthropic";
@@ -158126,7 +158218,7 @@ function defaultEffortForModel(modelId) {
158126
158218
  }
158127
158219
  function resolveModelLabel(alias) {
158128
158220
  const id = normaliseModelId(alias);
158129
- return (0, import_code_core6.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
158221
+ return (0, import_code_core7.liveLabelFor)(id, MODEL_LABELS[id] ?? id);
158130
158222
  }
158131
158223
  function tryExec(cmd, cwd2) {
158132
158224
  try {
@@ -158200,7 +158292,7 @@ var tryRead = (p) => {
158200
158292
  };
158201
158293
  function readNexrallMd(workDir) {
158202
158294
  const parts = [];
158203
- const memContent = (0, import_code_core6.readAllMemory)(workDir);
158295
+ const memContent = (0, import_code_core7.readAllMemory)(workDir);
158204
158296
  if (memContent)
158205
158297
  parts.push(memContent);
158206
158298
  const globalContent = tryRead(path9.join(os6.homedir(), ".nexrall", "nexrall.md"));
@@ -158307,7 +158399,7 @@ var Spinner = class {
158307
158399
  return this.interval !== null;
158308
158400
  }
158309
158401
  };
158310
- var cliBackgroundAgents = new import_code_core6.BackgroundAgentHub();
158402
+ var cliBackgroundAgents = new import_code_core7.BackgroundAgentHub();
158311
158403
  function nextBackgroundChange() {
158312
158404
  return new Promise((resolve6) => {
158313
158405
  const off = cliBackgroundAgents.onChange(() => {
@@ -158319,12 +158411,12 @@ function nextBackgroundChange() {
158319
158411
  var _mcpManager = null;
158320
158412
  var _mcpConfig = null;
158321
158413
  async function initMcp(workDir) {
158322
- _mcpConfig = import_code_core6.McpManager.loadConfig(workDir);
158414
+ _mcpConfig = import_code_core7.McpManager.loadConfig(workDir);
158323
158415
  if (Object.keys(_mcpConfig.mcpServers).length === 0) {
158324
158416
  _mcpManager = null;
158325
158417
  return;
158326
158418
  }
158327
- const manager = new import_code_core6.McpManager();
158419
+ const manager = new import_code_core7.McpManager();
158328
158420
  await manager.connectAll(_mcpConfig);
158329
158421
  _mcpManager = manager;
158330
158422
  }
@@ -158345,10 +158437,10 @@ function formatMcpStatus() {
158345
158437
  return lines.join("\n");
158346
158438
  }
158347
158439
  function formatContextUsage(messages, modelAlias) {
158348
- const window2 = (0, import_code_core6.contextWindowFor)(modelAlias);
158349
- const tokens = (0, import_code_core6.estimateTokensRough)(messages);
158350
- const bytes = (0, import_code_core6.estimateBodyBytes)(messages);
158351
- const { prune, compact } = (0, import_code_core6.compactionThresholds)(window2);
158440
+ const window2 = (0, import_code_core7.contextWindowFor)(modelAlias);
158441
+ const tokens = (0, import_code_core7.estimateTokensRough)(messages);
158442
+ const bytes = (0, import_code_core7.estimateBodyBytes)(messages);
158443
+ const { prune, compact } = (0, import_code_core7.compactionThresholds)(window2);
158352
158444
  const pct = Math.min(100, tokens / window2 * 100);
158353
158445
  const barWidth = 30;
158354
158446
  const filled = Math.round(pct / 100 * barWidth);
@@ -158365,7 +158457,7 @@ function formatContextUsage(messages, modelAlias) {
158365
158457
  return lines.join("\n");
158366
158458
  }
158367
158459
  function formatAgentsList(workDir) {
158368
- const { types: types3, warnings } = (0, import_code_core6.loadAgentTypesWithWarnings)(workDir);
158460
+ const { types: types3, warnings } = (0, import_code_core7.loadAgentTypesWithWarnings)(workDir);
158369
158461
  if (!types3.length)
158370
158462
  return source_default.dim(" No agent types found (this should not happen \u2014 builtins always register).");
158371
158463
  const lines = types3.map((t2) => {
@@ -158414,7 +158506,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
158414
158506
  setMode(mode);
158415
158507
  console.log();
158416
158508
  spinner.start("thinking\u2026", { hint: false });
158417
- const result = await (0, import_code_core6.runAgentLoop)(messages, {
158509
+ const result = await (0, import_code_core7.runAgentLoop)(messages, {
158418
158510
  workDir,
158419
158511
  model: modelAlias,
158420
158512
  env: refreshEnvForTurn(env4, workDir),
@@ -158624,7 +158716,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
158624
158716
  process.stdout.write(JSON.stringify(obj2) + "\n");
158625
158717
  };
158626
158718
  emit({ type: "start", model: modelAlias, workDir });
158627
- const result = await (0, import_code_core6.runAgentLoop)(messages, {
158719
+ const result = await (0, import_code_core7.runAgentLoop)(messages, {
158628
158720
  workDir,
158629
158721
  model: modelAlias,
158630
158722
  env: refreshEnvForTurn(env4, workDir),
@@ -158722,7 +158814,7 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env4
158722
158814
  // no human to confirm, they are denied unless NEXRALL_ALLOW_DESTRUCTIVE=1 is
158723
158815
  // explicitly set for this CI run.
158724
158816
  requestPermission: async ({ tool, input }) => {
158725
- const d = (0, import_code_core6.isDestructiveBash)(tool, input);
158817
+ const d = (0, import_code_core7.isDestructiveBash)(tool, input);
158726
158818
  if (d && process.env.NEXRALL_ALLOW_DESTRUCTIVE !== "1") {
158727
158819
  emit({ type: "error", error: `Destructive command blocked in headless mode (${d.category}): ${d.reason}. Set NEXRALL_ALLOW_DESTRUCTIVE=1 to permit.` });
158728
158820
  return false;
@@ -158838,7 +158930,7 @@ function printSessions() {
158838
158930
  console.log();
158839
158931
  }
158840
158932
  async function startChatSession(options) {
158841
- (0, import_code_core6.enableAgentTranscriptPersistence)();
158933
+ (0, import_code_core7.enableAgentTranscriptPersistence)();
158842
158934
  const headless = options.outputFormat === "json" || options.outputFormat === "stream-json";
158843
158935
  const requestedWorkDir = options.workDir;
158844
158936
  let workDir = requestedWorkDir;
@@ -158847,7 +158939,7 @@ async function startChatSession(options) {
158847
158939
  let peerInbox;
158848
158940
  const peerInboxQueue = [];
158849
158941
  let modelAlias = options.model;
158850
- void (0, import_code_core6.getModelCatalogue)().catch(() => {
158942
+ void (0, import_code_core7.getModelCatalogue)().catch(() => {
158851
158943
  });
158852
158944
  const canPrompt = !headless && !options.prompt && !options.stdinText && process.stdin.isTTY === true && process.stdout.isTTY === true;
158853
158945
  if (!trustGrantedByEnv()) {
@@ -158875,9 +158967,9 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
158875
158967
  }
158876
158968
  if (options.worktree !== void 0) {
158877
158969
  const requestedName = typeof options.worktree === "string" ? options.worktree : void 0;
158878
- const existing = requestedName ? (0, import_code_core6.findWorktreeByName)(requestedWorkDir, requestedName) : null;
158970
+ const existing = requestedName ? (0, import_code_core7.findWorktreeByName)(requestedWorkDir, requestedName) : null;
158879
158971
  if (existing) {
158880
- const entered = (0, import_code_core6.enterWorktree)(existing);
158972
+ const entered = (0, import_code_core7.enterWorktree)(existing);
158881
158973
  if (!entered.ok) {
158882
158974
  process.stderr.write(`Failed to resume worktree "${requestedName}": ${entered.error}
158883
158975
  `);
@@ -158887,7 +158979,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
158887
158979
  if (!headless)
158888
158980
  console.log(source_default.dim(` Resumed worktree: ${source_default.bold(existing.name)} (${existing.worktreePath})`));
158889
158981
  } else {
158890
- const created = (0, import_code_core6.createWorktree)(requestedWorkDir, requestedName);
158982
+ const created = (0, import_code_core7.createWorktree)(requestedWorkDir, requestedName);
158891
158983
  if (!created.ok || !created.state) {
158892
158984
  process.stderr.write(`Failed to create worktree: ${created.error ?? "unknown error"}
158893
158985
  `);
@@ -158906,7 +158998,7 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
158906
158998
  process.on("exit", () => {
158907
158999
  if (!worktreeState)
158908
159000
  return;
158909
- const hadWork = (0, import_code_core6.worktreeHasWork)(worktreeState);
159001
+ const hadWork = (0, import_code_core7.worktreeHasWork)(worktreeState);
158910
159002
  if (hadWork) {
158911
159003
  process.stderr.write(
158912
159004
  `
@@ -158917,7 +159009,7 @@ Resume it with: nex --worktree ${worktreeState.name}
158917
159009
  );
158918
159010
  return;
158919
159011
  }
158920
- (0, import_code_core6.removeWorktree)(worktreeState);
159012
+ (0, import_code_core7.removeWorktree)(worktreeState);
158921
159013
  });
158922
159014
  }
158923
159015
  const env4 = collectEnv(workDir);
@@ -158925,8 +159017,8 @@ Resume it with: nex --worktree ${worktreeState.name}
158925
159017
  const interactive = !headless && !options.prompt && !options.stdinText;
158926
159018
  let peerHeartbeatTimer;
158927
159019
  if (interactive) {
158928
- const handle2 = (0, import_code_core6.registerPeer)({ clientType: "cli", workDir: requestedWorkDir });
158929
- const inbox = (0, import_code_core6.startPeerInbox)(handle2.record.id, { onMessage: (m2) => peerInboxQueue.push(m2) });
159020
+ const handle2 = (0, import_code_core7.registerPeer)({ clientType: "cli", workDir: requestedWorkDir });
159021
+ const inbox = (0, import_code_core7.startPeerInbox)(handle2.record.id, { onMessage: (m2) => peerInboxQueue.push(m2) });
158930
159022
  handle2.record.socketPath = inbox.socketPath;
158931
159023
  handle2.heartbeat();
158932
159024
  peerHandle = handle2;
@@ -158996,7 +159088,7 @@ Resume it with: nex --worktree ${worktreeState.name}
158996
159088
  const ruleCount = permRules.allow.length + permRules.ask.length + permRules.deny.length;
158997
159089
  if (ruleCount && !headless)
158998
159090
  console.log(source_default.dim(` ${ruleCount} permission rule(s) loaded`));
158999
- let skills = (0, import_code_core6.loadSkills)(workDir);
159091
+ let skills = (0, import_code_core7.loadSkills)(workDir);
159000
159092
  if (skills.length && !headless)
159001
159093
  console.log(source_default.dim(` ${skills.length} skill(s) loaded`));
159002
159094
  const mcpStarted = (async () => {
@@ -159028,7 +159120,7 @@ Resume it with: nex --worktree ${worktreeState.name}
159028
159120
  sessionId = stored.id;
159029
159121
  sessionTitle = stored.title;
159030
159122
  try {
159031
- const compacted = await (0, import_code_core6.compactMessagesForResume)(messages, {
159123
+ const compacted = await (0, import_code_core7.compactMessagesForResume)(messages, {
159032
159124
  workDir,
159033
159125
  model: modelAlias,
159034
159126
  clientType: "cli"
@@ -159042,7 +159134,7 @@ Resume it with: nex --worktree ${worktreeState.name}
159042
159134
  console.log();
159043
159135
  }
159044
159136
  }
159045
- const checkpoints = new import_code_core6.CheckpointManager(workDir, sessionId);
159137
+ const checkpoints = new import_code_core7.CheckpointManager(workDir, sessionId);
159046
159138
  if (options.audit) {
159047
159139
  const auditPath = enableAudit({
159048
159140
  filePath: typeof options.audit === "string" ? options.audit : void 0,
@@ -159060,7 +159152,7 @@ Resume it with: nex --worktree ${worktreeState.name}
159060
159152
  sessionTitle = raw.length > 60 ? raw.slice(0, 60) + "\u2026" : raw;
159061
159153
  };
159062
159154
  const recoverTurn = (err, current) => {
159063
- const salvaged = (0, import_code_core6.salvageHistory)(err);
159155
+ const salvaged = (0, import_code_core7.salvageHistory)(err);
159064
159156
  console.error("\n" + source_default.red("Error: ") + String(err.message));
159065
159157
  if (!salvaged)
159066
159158
  return current;
@@ -159110,7 +159202,7 @@ ${text}` : "");
159110
159202
  continue;
159111
159203
  }
159112
159204
  const notes = cliBackgroundAgents.takeNotifications();
159113
- messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core6.formatBackgroundNotifications)(notes) }] });
159205
+ messages.push({ role: "user", content: [{ type: "text", text: (0, import_code_core7.formatBackgroundNotifications)(notes) }] });
159114
159206
  result = await runOnce();
159115
159207
  messages = result.messages;
159116
159208
  }
@@ -159122,7 +159214,7 @@ ${text}` : "");
159122
159214
  await new Promise((r2) => setImmediate(r2));
159123
159215
  process.exit(0);
159124
159216
  } catch (err) {
159125
- const salvaged = (0, import_code_core6.salvageHistory)(err);
159217
+ const salvaged = (0, import_code_core7.salvageHistory)(err);
159126
159218
  if (salvaged) {
159127
159219
  messages = salvaged;
159128
159220
  try {
@@ -159153,11 +159245,11 @@ ${text}` : "");
159153
159245
  console.log(source_default.dim(" Type a message, /help for commands, or Ctrl+C to exit."));
159154
159246
  console.log();
159155
159247
  const footerExtras = () => {
159156
- const win = (0, import_code_core6.contextWindowFor)(modelAlias);
159248
+ const win = (0, import_code_core7.contextWindowFor)(modelAlias);
159157
159249
  return {
159158
159250
  model: resolveModelLabel(modelAlias),
159159
159251
  branch: gitBranch(workDir) ?? void 0,
159160
- ctxPct: messages.length ? Math.min(100, (0, import_code_core6.estimateTokensRough)(messages) / win * 100) : 0,
159252
+ ctxPct: messages.length ? Math.min(100, (0, import_code_core7.estimateTokensRough)(messages) / win * 100) : 0,
159161
159253
  costUsd: sessionUsage.costReported ? sessionUsage.cost : void 0
159162
159254
  };
159163
159255
  };
@@ -159201,7 +159293,7 @@ ${text}` : "");
159201
159293
  updateFooter();
159202
159294
  const syncSlashMenu = () => {
159203
159295
  if (interactive)
159204
- getInkTerminal().setSlashEntries([...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core6.userInvokableSkills)(skills))]);
159296
+ getInkTerminal().setSlashEntries([...BUILTIN_SLASH_COMMANDS, ...skillEntries((0, import_code_core7.userInvokableSkills)(skills))]);
159205
159297
  };
159206
159298
  syncSlashMenu();
159207
159299
  if (interactive) {
@@ -159251,7 +159343,7 @@ ${text}` : "");
159251
159343
  messages = [];
159252
159344
  sessionId = crypto4.randomBytes(8).toString("hex");
159253
159345
  sessionTitle = "";
159254
- (0, import_code_core7.resetSessionSubAgentBudget)();
159346
+ (0, import_code_core8.resetSessionSubAgentBudget)();
159255
159347
  sessionUsage.reset();
159256
159348
  cliBackgroundAgents.stopAll({ dropPending: true });
159257
159349
  inputBuffer = "";
@@ -159291,7 +159383,7 @@ ${text}` : "");
159291
159383
  sessionTitle = stored.title;
159292
159384
  sessionUsage.reset();
159293
159385
  try {
159294
- await (0, import_code_core6.compactMessagesForResume)(messages, {
159386
+ await (0, import_code_core7.compactMessagesForResume)(messages, {
159295
159387
  workDir,
159296
159388
  model: modelAlias,
159297
159389
  clientType: "cli"
@@ -159466,13 +159558,13 @@ ${dirList}`;
159466
159558
  const sub = arg.trim().toLowerCase();
159467
159559
  console.log();
159468
159560
  if (sub === "list") {
159469
- const all = (0, import_code_core6.listWorktrees)(requestedWorkDir);
159561
+ const all = (0, import_code_core7.listWorktrees)(requestedWorkDir);
159470
159562
  if (!all.length) {
159471
159563
  console.log(source_default.dim(" No worktrees under this project."));
159472
159564
  } else {
159473
159565
  console.log(source_default.bold(` Worktrees (${all.length})`));
159474
159566
  for (const w of all) {
159475
- const dirty = (0, import_code_core6.worktreeHasWork)(w) ? source_default.yellow("dirty") : source_default.dim("clean");
159567
+ const dirty = (0, import_code_core7.worktreeHasWork)(w) ? source_default.yellow("dirty") : source_default.dim("clean");
159476
159568
  console.log(` ${source_default.cyan(w.name)} ${source_default.dim(w.worktreePath)} [${dirty}]`);
159477
159569
  }
159478
159570
  console.log();
@@ -159510,7 +159602,7 @@ ${dirList}`;
159510
159602
  if (!targetName || !text) {
159511
159603
  console.log(source_default.dim(" Usage: /peers send <name> <message>"));
159512
159604
  } else {
159513
- const target = (0, import_code_core6.listPeers)().find((p) => p.name === targetName);
159605
+ const target = (0, import_code_core7.listPeers)().find((p) => p.name === targetName);
159514
159606
  if (!target) {
159515
159607
  console.log(source_default.red(` No live session named "${targetName}". Use /peers to list who's reachable.`));
159516
159608
  } else if (!target.socketPath) {
@@ -159527,7 +159619,7 @@ ${dirList}`;
159527
159619
  console.log(source_default.dim(" Usage: /peers broadcast <message>"));
159528
159620
  } else {
159529
159621
  const { broadcastPeerMessage } = await Promise.resolve().then(() => __toESM(require_dist3(), 1));
159530
- const targets = (0, import_code_core6.listPeers)(peerHandle.record.id).filter((p) => p.id !== peerHandle.record.id && p.socketPath && p.inbound !== "refuse").map((p) => ({ name: p.name, socketPath: p.socketPath }));
159622
+ const targets = (0, import_code_core7.listPeers)(peerHandle.record.id).filter((p) => p.id !== peerHandle.record.id && p.socketPath && p.inbound !== "refuse").map((p) => ({ name: p.name, socketPath: p.socketPath }));
159531
159623
  if (!targets.length) {
159532
159624
  console.log(source_default.dim(" No other reachable Nexrall Code sessions to broadcast to."));
159533
159625
  } else {
@@ -159539,7 +159631,7 @@ ${dirList}`;
159539
159631
  }
159540
159632
  }
159541
159633
  } else {
159542
- const peers = (0, import_code_core6.listPeers)(peerHandle.record.id);
159634
+ const peers = (0, import_code_core7.listPeers)(peerHandle.record.id);
159543
159635
  if (peers.length <= 1) {
159544
159636
  console.log(source_default.dim(" No other Nexrall Code sessions are currently discoverable on this machine."));
159545
159637
  } else {
@@ -159569,7 +159661,7 @@ ${dirList}`;
159569
159661
  const label = memScope === "global" ? "GLOBAL" : "this PROJECT's";
159570
159662
  const answer = await new Promise((resolve6) => rl.question(source_default.yellow(` Clear ${label} memory? This cannot be undone. [y/n] `), (a) => resolve6(a.trim())));
159571
159663
  if (answer === "y" || answer === "yes") {
159572
- (0, import_code_core6.clearMemory)(memScope, workDir);
159664
+ (0, import_code_core7.clearMemory)(memScope, workDir);
159573
159665
  console.log(source_default.green(` ${memScope === "global" ? "Global" : "Project"} memory cleared.`));
159574
159666
  } else {
159575
159667
  console.log(source_default.dim(" Cancelled."));
@@ -159580,20 +159672,20 @@ ${dirList}`;
159580
159672
  }
159581
159673
  console.log();
159582
159674
  if (wantsGlobalOnly) {
159583
- const stats = (0, import_code_core6.memoryStats)("global");
159584
- const content = (0, import_code_core6.readMemory)("global", void 0, { includeArchived: wantsArchived });
159675
+ const stats = (0, import_code_core7.memoryStats)("global");
159676
+ const content = (0, import_code_core7.readMemory)("global", void 0, { includeArchived: wantsArchived });
159585
159677
  const archivedNote = stats.archived > 0 ? `, ${stats.archived} archived` : "";
159586
159678
  console.log(source_default.bold(" Global memory ") + source_default.dim(`(${stats.entries} entries${archivedNote}, ${(stats.bytes / 1024).toFixed(1)}KB) \u2014 ${stats.file}`));
159587
159679
  console.log();
159588
159680
  console.log(content || source_default.dim(" No global memories saved yet."));
159589
159681
  } else {
159590
- const projStats = (0, import_code_core6.memoryStats)("project", workDir);
159591
- const globalStats = (0, import_code_core6.memoryStats)("global");
159682
+ const projStats = (0, import_code_core7.memoryStats)("project", workDir);
159683
+ const globalStats = (0, import_code_core7.memoryStats)("global");
159592
159684
  const projArchived = projStats.archived > 0 ? `+${projStats.archived} archived ` : "";
159593
159685
  const globalArchived = globalStats.archived > 0 ? `+${globalStats.archived} archived ` : "";
159594
159686
  console.log(source_default.bold(" Memory") + source_default.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)`));
159595
159687
  console.log();
159596
- const merged = (0, import_code_core6.readAllMemory)(workDir, { includeArchived: wantsArchived });
159688
+ const merged = (0, import_code_core7.readAllMemory)(workDir, { includeArchived: wantsArchived });
159597
159689
  console.log(merged || source_default.dim(" No memories saved yet."));
159598
159690
  }
159599
159691
  console.log();
@@ -159632,12 +159724,12 @@ ${dirList}`;
159632
159724
  console.log(source_default.dim(` Current: ${source_default.cyan(agentMode)}`));
159633
159725
  for (const m2 of ["ask", "edit", "auto", "plan"]) {
159634
159726
  const marker = m2 === agentMode ? source_default.cyan("\u276F") : " ";
159635
- console.log(` ${marker} ${source_default.bold(m2.padEnd(5))} ${source_default.dim((0, import_code_core7.describeMode)(m2).replace(`${m2} \u2014 `, ""))}`);
159727
+ console.log(` ${marker} ${source_default.bold(m2.padEnd(5))} ${source_default.dim((0, import_code_core8.describeMode)(m2).replace(`${m2} \u2014 `, ""))}`);
159636
159728
  }
159637
159729
  } else if (["ask", "edit", "plan", "auto"].includes(modeArg)) {
159638
159730
  agentMode = modeArg;
159639
159731
  updateFooter();
159640
- console.log(source_default.green(` Mode \u2192 ${source_default.bold(modeArg)}`) + source_default.dim(` ${(0, import_code_core7.describeMode)(modeArg).replace(`${modeArg} \u2014 `, "")}`));
159732
+ console.log(source_default.green(` Mode \u2192 ${source_default.bold(modeArg)}`) + source_default.dim(` ${(0, import_code_core8.describeMode)(modeArg).replace(`${modeArg} \u2014 `, "")}`));
159641
159733
  } else {
159642
159734
  console.log(source_default.red(` Unknown: ${modeArg}`));
159643
159735
  }
@@ -159675,22 +159767,22 @@ ${dirList}`;
159675
159767
  if (target === void 0) {
159676
159768
  console.log(source_default.red(" Usage: /team use <team id|personal> (/team lists the ids)"));
159677
159769
  } else {
159678
- await (0, import_code_core6.setBillingTarget)(target);
159770
+ await (0, import_code_core7.setBillingTarget)(target);
159679
159771
  console.log(source_default.green(` \u2713 Usage will now be billed to ${target === null ? "your personal wallet" : `team ${target}`}.`));
159680
159772
  if (target !== null)
159681
159773
  console.log(source_default.dim(" This applies to every product and client on this account, not just the CLI."));
159682
159774
  }
159683
159775
  } else {
159684
159776
  try {
159685
- const orgs = await (0, import_code_core6.getOrgs)();
159686
- const pin = (0, import_code_core6.billingOverrideFields)();
159777
+ const orgs = await (0, import_code_core7.getOrgs)();
159778
+ const pin = (0, import_code_core7.billingOverrideFields)();
159687
159779
  for (const l2 of billingLines(orgs, "teamId" in pin ? pin.teamId : void 0)) {
159688
159780
  console.log(l2.startsWith("!") ? source_default.yellow(" " + l2) : " " + l2);
159689
159781
  }
159690
159782
  } catch (orgErr) {
159691
159783
  if (cmd === "/team")
159692
159784
  throw orgErr;
159693
- const bal = await (0, import_code_core6.getBalance)();
159785
+ const bal = await (0, import_code_core7.getBalance)();
159694
159786
  console.log(source_default.dim(" Balance: ") + source_default.cyan(`$${bal.toFixed(4)}`));
159695
159787
  }
159696
159788
  }
@@ -159800,7 +159892,7 @@ ${dirList}`;
159800
159892
  case "/plan": {
159801
159893
  agentMode = "plan";
159802
159894
  updateFooter();
159803
- console.log(source_default.green(` Mode \u2192 ${source_default.bold("plan")}`) + source_default.dim(` ${(0, import_code_core7.describeMode)("plan").replace("plan \u2014 ", "")} (/mode auto to leave)`));
159895
+ console.log(source_default.green(` Mode \u2192 ${source_default.bold("plan")}`) + source_default.dim(` ${(0, import_code_core8.describeMode)("plan").replace("plan \u2014 ", "")} (/mode auto to leave)`));
159804
159896
  rl.prompt();
159805
159897
  return;
159806
159898
  }
@@ -159887,7 +159979,7 @@ ${caption}` : `[Attached: ${rel}]`;
159887
159979
  if (needsSidecar) {
159888
159980
  console.log(source_default.dim(` ${resolveModelLabel(modelAlias)} can't read ${isPdf ? "PDFs" : "images"} directly \u2014 describing ${rel} with Claude Sonnet 5.5 first\u2026`));
159889
159981
  try {
159890
- const { text } = await (0, import_code_core6.describeAttachment)(isPdf ? "pdf" : "image", data, mimeType, rel);
159982
+ const { text } = await (0, import_code_core7.describeAttachment)(isPdf ? "pdf" : "image", data, mimeType, rel);
159891
159983
  content.push({ type: "text", text: `<attachment_description>
159892
159984
  ${text}
159893
159985
  </attachment_description>` });
@@ -159931,7 +160023,7 @@ ${text}
159931
160023
  process.exit(0);
159932
160024
  }
159933
160025
  case "/plugins": {
159934
- const plugins = (0, import_code_core6.loadPlugins)(workDir);
160026
+ const plugins = (0, import_code_core7.loadPlugins)(workDir);
159935
160027
  if (!plugins.length) {
159936
160028
  console.log(source_default.dim(" No plugins installed. Install one: nex plugin install owner/repo"));
159937
160029
  } else {
@@ -159944,7 +160036,7 @@ ${text}
159944
160036
  }
159945
160037
  console.log();
159946
160038
  }
159947
- skills = (0, import_code_core6.loadSkills)(workDir);
160039
+ skills = (0, import_code_core7.loadSkills)(workDir);
159948
160040
  syncSlashMenu();
159949
160041
  rl.prompt();
159950
160042
  return;
@@ -160080,9 +160172,9 @@ ${text}
160080
160172
  }
160081
160173
  case "/skills":
160082
160174
  case "/commands": {
160083
- skills = (0, import_code_core6.loadSkills)(workDir);
160175
+ skills = (0, import_code_core7.loadSkills)(workDir);
160084
160176
  syncSlashMenu();
160085
- const invocable = (0, import_code_core6.userInvokableSkills)(skills);
160177
+ const invocable = (0, import_code_core7.userInvokableSkills)(skills);
160086
160178
  if (!invocable.length) {
160087
160179
  console.log(source_default.dim(" No skills. Add .nexrall/skills/<name>/SKILL.md (or .nexrall/commands/<name>.md) to create one."));
160088
160180
  } else {
@@ -160099,15 +160191,15 @@ ${text}
160099
160191
  return;
160100
160192
  }
160101
160193
  default: {
160102
- const custom = (0, import_code_core6.findSkill)(skills, cmd);
160194
+ const custom = (0, import_code_core7.findSkill)(skills, cmd);
160103
160195
  if (!custom) {
160104
160196
  console.log(source_default.red(` Unknown command: ${cmd}`));
160105
160197
  console.log(source_default.dim(" Type /help for built-ins or /skills for custom ones."));
160106
160198
  rl.prompt();
160107
160199
  return;
160108
160200
  }
160109
- const expanded = (0, import_code_core6.expandSkill)(custom, arg, workDir);
160110
- const turnModel = (0, import_code_core6.resolveSubAgentModel)(custom.model, modelAlias) ?? modelAlias;
160201
+ const expanded = (0, import_code_core7.expandSkill)(custom, arg, workDir);
160202
+ const turnModel = (0, import_code_core7.resolveSubAgentModel)(custom.model, modelAlias) ?? modelAlias;
160111
160203
  const turnMode = custom.mode ?? agentMode;
160112
160204
  console.log(source_default.dim(` Running /${custom.name}\u2026`));
160113
160205
  checkpoints.beginTurn(`/${custom.name} ${arg}`.trim(), messages.length);
@@ -160169,7 +160261,7 @@ ${text}
160169
160261
  return;
160170
160262
  const notes = cliBackgroundAgents.takeNotifications();
160171
160263
  console.log(source_default.dim(` \u2699 background agent${notes.length > 1 ? "s" : ""} ${notes.map((n) => `"${n.description}"`).join(", ")} finished \u2014 continuing`));
160172
- rl.emit("line", (0, import_code_core6.formatBackgroundNotifications)(notes));
160264
+ rl.emit("line", (0, import_code_core7.formatBackgroundNotifications)(notes));
160173
160265
  }, 400);
160174
160266
  }
160175
160267
  cliBackgroundAgents.onChange(() => {
@@ -160190,7 +160282,7 @@ ${text}
160190
160282
 
160191
160283
  // src/commands/plugin.ts
160192
160284
  var import_prompts = __toESM(require_prompts3(), 1);
160193
- var import_code_core8 = __toESM(require_dist3(), 1);
160285
+ var import_code_core9 = __toESM(require_dist3(), 1);
160194
160286
  function describeInspection(i2, name, ctx) {
160195
160287
  console.log();
160196
160288
  console.log(source_default.bold(` Plugin: ${name}`) + (i2.meta.version ? source_default.dim(` v${i2.meta.version}`) : ""));
@@ -160266,14 +160358,14 @@ async function pluginInstallCommand(spec, opts) {
160266
160358
  let registryName;
160267
160359
  const isShortName = /^[\w.-]+$/.test(spec.trim()) && !spec.includes("/");
160268
160360
  if (isShortName) {
160269
- const alias = (0, import_code_core8.resolveSourceAlias)(spec.trim(), process.cwd(), { includeProject: true });
160361
+ const alias = (0, import_code_core9.resolveSourceAlias)(spec.trim(), process.cwd(), { includeProject: true });
160270
160362
  if (alias) {
160271
160363
  source2 = alias.spec;
160272
160364
  console.log(source_default.dim(` Source: ${alias.name} (${alias.tier}) \u2190 ${alias.spec}`));
160273
160365
  }
160274
160366
  }
160275
160367
  if (isShortName && source2 === spec) {
160276
- const entry = await (0, import_code_core8.getRegistryPlugin)(spec.trim()).catch(() => null);
160368
+ const entry = await (0, import_code_core9.getRegistryPlugin)(spec.trim()).catch(() => null);
160277
160369
  if (entry) {
160278
160370
  source2 = entry.source;
160279
160371
  registryName = entry.name;
@@ -160288,7 +160380,7 @@ async function pluginInstallCommand(spec, opts) {
160288
160380
  return;
160289
160381
  }
160290
160382
  }
160291
- const res = await (0, import_code_core8.installPlugin)(source2, {
160383
+ const res = await (0, import_code_core9.installPlugin)(source2, {
160292
160384
  scope,
160293
160385
  workDir: process.cwd(),
160294
160386
  name: opts.name ?? registryName,
@@ -160296,7 +160388,7 @@ async function pluginInstallCommand(spec, opts) {
160296
160388
  confirm: (i2, name, ctx) => confirmInstall(i2, name, opts.yes === true, ctx)
160297
160389
  });
160298
160390
  if (registryName)
160299
- (0, import_code_core8.reportInstall)(registryName);
160391
+ (0, import_code_core9.reportInstall)(registryName);
160300
160392
  console.log();
160301
160393
  console.log(source_default.green(` \u2713 Installed "${res.name}" (${res.scope}) \u2192 ${res.dir}`) + (res.sha ? source_default.dim(` @ ${res.sha.slice(0, 7)}`) : ""));
160302
160394
  if (res.inspection.commands.length) {
@@ -160310,7 +160402,7 @@ async function pluginInstallCommand(spec, opts) {
160310
160402
  }
160311
160403
  function pluginRemoveCommand(name, opts) {
160312
160404
  try {
160313
- const dir = (0, import_code_core8.removePlugin)(name, {
160405
+ const dir = (0, import_code_core9.removePlugin)(name, {
160314
160406
  scope: opts.project ? "project" : void 0,
160315
160407
  // no flag → search both scopes
160316
160408
  workDir: process.cwd()
@@ -160323,14 +160415,14 @@ function pluginRemoveCommand(name, opts) {
160323
160415
  }
160324
160416
  async function pluginUpdateCommand(name, opts) {
160325
160417
  const workDir = process.cwd();
160326
- const targets = name ? [name] : (0, import_code_core8.loadPlugins)(workDir).filter((p) => (0, import_code_core8.readReceipt)(p.dir)?.kind === "github").map((p) => p.name);
160418
+ const targets = name ? [name] : (0, import_code_core9.loadPlugins)(workDir).filter((p) => (0, import_code_core9.readReceipt)(p.dir)?.kind === "github").map((p) => p.name);
160327
160419
  if (!targets.length) {
160328
160420
  console.log(source_default.dim(" No updatable plugins (only GitHub-installed plugins with receipts can update)."));
160329
160421
  return;
160330
160422
  }
160331
160423
  for (const t2 of targets) {
160332
160424
  try {
160333
- const res = await (0, import_code_core8.updatePlugin)(t2, {
160425
+ const res = await (0, import_code_core9.updatePlugin)(t2, {
160334
160426
  workDir,
160335
160427
  confirm: (i2, n, ctx) => confirmInstall(i2, n, opts.yes === true, ctx)
160336
160428
  });
@@ -160347,7 +160439,7 @@ function badgeFor(p) {
160347
160439
  }
160348
160440
  async function pluginSearchCommand(query) {
160349
160441
  try {
160350
- const results = await (0, import_code_core8.searchRegistry)(query);
160442
+ const results = await (0, import_code_core9.searchRegistry)(query);
160351
160443
  if (!results.length) {
160352
160444
  console.log(source_default.dim(query ? ` No plugins matching "${query}".` : " Registry is empty."));
160353
160445
  return;
@@ -160370,7 +160462,7 @@ async function pluginSearchCommand(query) {
160370
160462
  }
160371
160463
  async function pluginInfoCommand(name) {
160372
160464
  try {
160373
- const p = await (0, import_code_core8.getRegistryPlugin)(name);
160465
+ const p = await (0, import_code_core9.getRegistryPlugin)(name);
160374
160466
  if (!p) {
160375
160467
  console.error(source_default.red(` \u2717 "${name}" not found in the registry.`));
160376
160468
  process.exitCode = 1;
@@ -160406,7 +160498,7 @@ async function pluginInfoCommand(name) {
160406
160498
  }
160407
160499
  }
160408
160500
  function pluginListCommand() {
160409
- const plugins = (0, import_code_core8.loadPlugins)(process.cwd());
160501
+ const plugins = (0, import_code_core9.loadPlugins)(process.cwd());
160410
160502
  if (!plugins.length) {
160411
160503
  console.log(source_default.dim(" No plugins installed."));
160412
160504
  console.log(source_default.dim(" Install one: nex plugin install owner/repo"));
@@ -160415,10 +160507,10 @@ function pluginListCommand() {
160415
160507
  console.log();
160416
160508
  console.log(source_default.bold(" Installed plugins:"));
160417
160509
  for (const p of plugins) {
160418
- const receipt = (0, import_code_core8.readReceipt)(p.dir);
160510
+ const receipt = (0, import_code_core9.readReceipt)(p.dir);
160419
160511
  const src = receipt ? source_default.dim(` \u2190 ${receipt.source}`) : source_default.dim(" (manual)");
160420
160512
  const at = receipt?.sha ? source_default.dim(` @ ${receipt.sha.slice(0, 7)}`) : "";
160421
- const insp = (0, import_code_core8.inspectPluginDir)(p.dir);
160513
+ const insp = (0, import_code_core9.inspectPluginDir)(p.dir);
160422
160514
  const runs = insp.hasHooks || insp.hasMcp ? source_default.yellow(` \u26A0 ${[insp.hasHooks && "hooks", insp.hasMcp && "mcp"].filter(Boolean).join("+")}`) : "";
160423
160515
  console.log(
160424
160516
  " " + source_default.cyan(p.name.padEnd(24)) + source_default.dim(`${(p.version ?? "").padEnd(10)}${p.scope.padEnd(9)}`) + src + at + runs
@@ -160428,8 +160520,8 @@ function pluginListCommand() {
160428
160520
  }
160429
160521
  function pluginSourceListCommand() {
160430
160522
  const workDir = process.cwd();
160431
- const sources = (0, import_code_core8.loadDeclaredSources)(workDir, { includeProject: true });
160432
- const patterns = (0, import_code_core8.allowedSourcePatterns)();
160523
+ const sources = (0, import_code_core9.loadDeclaredSources)(workDir, { includeProject: true });
160524
+ const patterns = (0, import_code_core9.allowedSourcePatterns)();
160433
160525
  if (patterns) {
160434
160526
  console.log();
160435
160527
  console.log(source_default.bold(" Organisation policy: ") + source_default.dim(`installs restricted to ${patterns.join(", ")}`));
@@ -160444,7 +160536,7 @@ function pluginSourceListCommand() {
160444
160536
  console.log(source_default.bold(" Declared plugin sources:"));
160445
160537
  for (const s2 of sources) {
160446
160538
  const tier = s2.tier === "project" ? source_default.yellow(s2.tier.padEnd(9)) : source_default.dim(s2.tier.padEnd(9));
160447
- const blocked = !(0, import_code_core8.isSourceAllowed)(s2.spec, patterns) ? source_default.red(" \u2717 blocked by policy") : "";
160539
+ const blocked = !(0, import_code_core9.isSourceAllowed)(s2.spec, patterns) ? source_default.red(" \u2717 blocked by policy") : "";
160448
160540
  console.log(" " + source_default.cyan(s2.name.padEnd(20)) + tier + source_default.white(s2.spec) + blocked);
160449
160541
  }
160450
160542
  console.log();
@@ -160454,17 +160546,17 @@ function pluginSourceListCommand() {
160454
160546
  }
160455
160547
  function pluginSourceAddCommand(name, spec, opts) {
160456
160548
  try {
160457
- (0, import_code_core8.parsePluginSource)(spec);
160458
- const patterns = (0, import_code_core8.allowedSourcePatterns)();
160459
- if (patterns && !(0, import_code_core8.isSourceAllowed)(spec, patterns)) {
160549
+ (0, import_code_core9.parsePluginSource)(spec);
160550
+ const patterns = (0, import_code_core9.allowedSourcePatterns)();
160551
+ if (patterns && !(0, import_code_core9.isSourceAllowed)(spec, patterns)) {
160460
160552
  console.error(source_default.red(` \u2717 "${spec}" is not permitted by your organisation's policy.`));
160461
160553
  console.error(source_default.dim(` Allowed: ${patterns.join(", ")}`));
160462
160554
  process.exitCode = 1;
160463
160555
  return;
160464
160556
  }
160465
160557
  const scope = opts.project ? "project" : "user";
160466
- const file = (0, import_code_core8.sourcesFileFor)(scope, process.cwd());
160467
- (0, import_code_core8.writeDeclaredSource)(file, name, spec);
160558
+ const file = (0, import_code_core9.sourcesFileFor)(scope, process.cwd());
160559
+ (0, import_code_core9.writeDeclaredSource)(file, name, spec);
160468
160560
  console.log();
160469
160561
  console.log(source_default.green(` \u2713 Declared "${name}" \u2192 ${spec}`) + source_default.dim(` (${scope})`));
160470
160562
  console.log(source_default.dim(` ${file}`));
@@ -160478,13 +160570,13 @@ function pluginSourceAddCommand(name, spec, opts) {
160478
160570
  function pluginSourceRemoveCommand(name, opts) {
160479
160571
  const workDir = process.cwd();
160480
160572
  const scope = opts.project ? "project" : "user";
160481
- const file = (0, import_code_core8.sourcesFileFor)(scope, workDir);
160482
- if ((0, import_code_core8.removeDeclaredSource)(file, name)) {
160573
+ const file = (0, import_code_core9.sourcesFileFor)(scope, workDir);
160574
+ if ((0, import_code_core9.removeDeclaredSource)(file, name)) {
160483
160575
  console.log(source_default.green(` \u2713 Removed source "${name}" (${scope})`));
160484
160576
  console.log(source_default.dim(" Plugins already installed from it are untouched \u2014 remove them with nex plugin remove <name>"));
160485
160577
  return;
160486
160578
  }
160487
- const found = (0, import_code_core8.resolveSourceAlias)(name, workDir, { includeProject: true });
160579
+ const found = (0, import_code_core9.resolveSourceAlias)(name, workDir, { includeProject: true });
160488
160580
  if (found) {
160489
160581
  console.error(source_default.red(` \u2717 "${name}" is declared in the ${found.tier} tier, not ${scope}.`));
160490
160582
  console.error(source_default.dim(` ${found.file}`));
@@ -160581,7 +160673,7 @@ program2.command("sessions").description("List or delete saved chat sessions").o
160581
160673
  console.log();
160582
160674
  });
160583
160675
  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("-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)").argument("[prompt...]", "One-shot prompt \u2014 if omitted, starts interactive mode").action(async (promptParts, options) => {
160584
- if (!(0, import_code_core9.isAuthenticated)()) {
160676
+ if (!(0, import_code_core10.isAuthenticated)()) {
160585
160677
  console.error("Not logged in. Run: nex auth");
160586
160678
  process.exit(1);
160587
160679
  }