nexrall-code 0.5.55 → 0.5.56

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 +142 -30
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -15902,7 +15902,11 @@ var require_loop = __commonJS({
15902
15902
  exports.executeAgentMemoryWrite = executeAgentMemoryWrite;
15903
15903
  exports.stopReasonNotice = stopReasonNotice;
15904
15904
  exports.resolveMaxIterations = resolveMaxIterations;
15905
+ exports.bashNeedsRepoLock = bashNeedsRepoLock;
15906
+ exports.lockPathsFor = lockPathsFor;
15907
+ exports.resolveMaxConcurrentSubtasks = resolveMaxConcurrentSubtasks;
15905
15908
  exports.createLimiter = createLimiter;
15909
+ exports._resetSubTaskLimiter = _resetSubTaskLimiter;
15906
15910
  exports.resolveSubtaskTimeoutMs = resolveSubtaskTimeoutMs;
15907
15911
  exports.extractSubTaskText = extractSubTaskText;
15908
15912
  exports.capSubTaskText = capSubTaskText;
@@ -16144,10 +16148,89 @@ ${ctx.repeatError}
16144
16148
  _fileLocks.delete(absPath);
16145
16149
  }
16146
16150
  }
16147
- var MAX_CONCURRENT_SUBTASKS = (() => {
16148
- const raw = Number(process.env.NEXRALL_MAX_CONCURRENT_SUBTASKS);
16149
- return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 4;
16150
- })();
16151
+ async function withFileLocks(absPaths, fn) {
16152
+ const unique = [...new Set(absPaths.filter(Boolean))].sort();
16153
+ if (unique.length === 0)
16154
+ return fn();
16155
+ const [first, ...rest] = unique;
16156
+ return withFileLock(first, () => rest.length ? withFileLocks(rest, fn) : fn());
16157
+ }
16158
+ var WRITE_TOOLS = /* @__PURE__ */ new Set([
16159
+ "write_file",
16160
+ "edit_file",
16161
+ "multi_edit",
16162
+ "delete_file",
16163
+ "move_file",
16164
+ "copy_file",
16165
+ "notebook_edit"
16166
+ ]);
16167
+ var REPO_STATE_LOCK = "\0repo-state";
16168
+ var MUTATING_BASH_VERBS = /* @__PURE__ */ new Set([
16169
+ "git",
16170
+ "npm",
16171
+ "pnpm",
16172
+ "yarn",
16173
+ "bun",
16174
+ "cargo",
16175
+ "go",
16176
+ "pip",
16177
+ "pip3",
16178
+ "poetry",
16179
+ "uv",
16180
+ "bundle",
16181
+ "composer",
16182
+ "gradle",
16183
+ "mvn",
16184
+ "make",
16185
+ "terraform",
16186
+ "helm",
16187
+ "docker",
16188
+ "vsce",
16189
+ "tsc",
16190
+ "pytest",
16191
+ "dotnet",
16192
+ "swift",
16193
+ "gem"
16194
+ ]);
16195
+ function bashNeedsRepoLock(command) {
16196
+ if (!command)
16197
+ return false;
16198
+ const segments = command.split(/\|\||&&|[;|&\n()]|\$\(/);
16199
+ for (const seg of segments) {
16200
+ for (const tokenRaw of seg.trim().split(/\s+/)) {
16201
+ const token = tokenRaw.replace(/^["']|["']$/g, "");
16202
+ if (!token)
16203
+ continue;
16204
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
16205
+ continue;
16206
+ if (token === "sudo" || token === "time" || token === "nice" || token === "env")
16207
+ continue;
16208
+ const verb = token.split("/").pop() ?? token;
16209
+ if (MUTATING_BASH_VERBS.has(verb))
16210
+ return true;
16211
+ break;
16212
+ }
16213
+ }
16214
+ return false;
16215
+ }
16216
+ function lockPathsFor(name, input, workDir) {
16217
+ if (!WRITE_TOOLS.has(name))
16218
+ return [];
16219
+ const raw = [input.path, input.source, input.destination, input.dest].filter((p) => typeof p === "string" && p.length > 0);
16220
+ const abs = raw.map((p) => workDir ? path6.resolve(workDir, p) : p);
16221
+ return abs.length ? abs : [name];
16222
+ }
16223
+ var DEFAULT_MAX_CONCURRENT_SUBTASKS = 4;
16224
+ function resolveMaxConcurrentSubtasks(settingsRaw = {}) {
16225
+ const clamp = (n) => Math.min(Math.floor(n), 16);
16226
+ const fromEnv = Number(process.env.NEXRALL_MAX_CONCURRENT_SUBTASKS);
16227
+ if (Number.isFinite(fromEnv) && fromEnv > 0)
16228
+ return clamp(fromEnv);
16229
+ const fromSettings = Number(settingsRaw.maxConcurrentSubtasks);
16230
+ if (Number.isFinite(fromSettings) && fromSettings > 0)
16231
+ return clamp(fromSettings);
16232
+ return DEFAULT_MAX_CONCURRENT_SUBTASKS;
16233
+ }
16151
16234
  function createLimiter(max) {
16152
16235
  let active = 0;
16153
16236
  const queue = [];
@@ -16166,7 +16249,20 @@ ${ctx.repeatError}
16166
16249
  }
16167
16250
  };
16168
16251
  }
16169
- var _subTaskLimit = createLimiter(MAX_CONCURRENT_SUBTASKS);
16252
+ var _subTaskLimitInstance = null;
16253
+ var _subTaskLimitMax = 0;
16254
+ var _inFlightSubTasks = 0;
16255
+ function subTaskLimiter(workDir) {
16256
+ if (!_subTaskLimitInstance) {
16257
+ _subTaskLimitMax = resolveMaxConcurrentSubtasks(workDir ? (0, rules_1.loadSettings)(workDir).raw : {});
16258
+ _subTaskLimitInstance = createLimiter(_subTaskLimitMax);
16259
+ }
16260
+ return { run: _subTaskLimitInstance, max: _subTaskLimitMax };
16261
+ }
16262
+ function _resetSubTaskLimiter() {
16263
+ _subTaskLimitInstance = null;
16264
+ _subTaskLimitMax = 0;
16265
+ }
16170
16266
  function humanDescription(name, input) {
16171
16267
  switch (name) {
16172
16268
  case "read_file":
@@ -17255,7 +17351,20 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
17255
17351
  if (!permitted) {
17256
17352
  result = { error: deniedReason ?? "Permission denied by user" };
17257
17353
  } else if (name === "task") {
17258
- result = depth === 0 ? await _subTaskLimit(() => runSubTask(input, options, agentTypes)) : await runSubTask(input, options, agentTypes);
17354
+ if (depth === 0) {
17355
+ const { run: limitRun, max: limitMax } = subTaskLimiter(options.workDir);
17356
+ if (_inFlightSubTasks >= limitMax) {
17357
+ (options.onNotice ?? options.onText)(`\u23F3 Queued: ${limitMax} sub-agents are already running, so this one starts when a slot frees up (raise "maxConcurrentSubtasks" in .nexrall/settings.json to widen it).`);
17358
+ }
17359
+ _inFlightSubTasks++;
17360
+ try {
17361
+ result = await limitRun(() => runSubTask(input, options, agentTypes));
17362
+ } finally {
17363
+ _inFlightSubTasks--;
17364
+ }
17365
+ } else {
17366
+ result = await runSubTask(input, options, agentTypes);
17367
+ }
17259
17368
  } else {
17260
17369
  const pre = runToolHooks(hooks.PreToolUse, "PreToolUse", name, input, options.workDir);
17261
17370
  if (pre.block) {
@@ -17275,15 +17384,10 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
17275
17384
  result = { output: mcpOutput ?? "" };
17276
17385
  } else {
17277
17386
  options.checkpointManager?.recordBeforeMutation(name, input);
17278
- const WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file", "multi_edit", "delete_file", "move_file", "copy_file", "notebook_edit"]);
17279
17387
  const onStream = options.onToolStreamChunk ? (chunk) => options.onToolStreamChunk(name, chunk) : void 0;
17280
- if (WRITE_TOOLS.has(name)) {
17281
- const targetPath = typeof input.path === "string" ? input.path : typeof input.source === "string" ? input.source : "";
17282
- const absTarget = targetPath && options.workDir ? path6.resolve(options.workDir, targetPath) : targetPath;
17283
- result = await withFileLock(absTarget || name, () => (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope, onStream));
17284
- } else {
17285
- result = await (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope, onStream);
17286
- }
17388
+ const run = () => (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope, onStream);
17389
+ const locks = name === "bash" ? bashNeedsRepoLock(String(input.command ?? "")) ? [REPO_STATE_LOCK] : [] : lockPathsFor(name, input, options.workDir);
17390
+ result = locks.length ? await withFileLocks(locks, run) : await run();
17287
17391
  }
17288
17392
  } catch (err) {
17289
17393
  result = { error: `Tool execution failed: ${err.message}` };
@@ -54708,22 +54812,30 @@ var _replInterface = null;
54708
54812
  function setReadlineInterface(rl) {
54709
54813
  _replInterface = rl;
54710
54814
  }
54815
+ var _promptChain = Promise.resolve();
54816
+ function serialisePrompt(fn) {
54817
+ const next = _promptChain.then(fn, fn);
54818
+ _promptChain = next.catch(() => void 0);
54819
+ return next;
54820
+ }
54711
54821
  function askUser(question) {
54712
- if (_replInterface) {
54713
- const rl = _replInterface;
54822
+ return serialisePrompt(() => {
54823
+ if (_replInterface) {
54824
+ const rl = _replInterface;
54825
+ return new Promise((resolve3) => {
54826
+ rl.question(question, (answer) => resolve3(answer.trim()));
54827
+ });
54828
+ }
54714
54829
  return new Promise((resolve3) => {
54715
- rl.question(question, (answer) => resolve3(answer.trim()));
54716
- });
54717
- }
54718
- return new Promise((resolve3) => {
54719
- const rl = readline2.createInterface({
54720
- input: process.stdin,
54721
- output: process.stderr,
54722
- terminal: true
54723
- });
54724
- rl.question(question, (answer) => {
54725
- rl.close();
54726
- resolve3(answer.trim());
54830
+ const rl = readline2.createInterface({
54831
+ input: process.stdin,
54832
+ output: process.stderr,
54833
+ terminal: true
54834
+ });
54835
+ rl.question(question, (answer) => {
54836
+ rl.close();
54837
+ resolve3(answer.trim());
54838
+ });
54727
54839
  });
54728
54840
  });
54729
54841
  }
@@ -64349,7 +64461,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
64349
64461
  };
64350
64462
 
64351
64463
  // src/commands/chat.ts
64352
- var CLI_VERSION = "0.5.55";
64464
+ var CLI_VERSION = "0.5.56";
64353
64465
  var MODEL_LABELS = {
64354
64466
  turbo: "Nexrall Turbo",
64355
64467
  pro: "Nexrall Pro",
@@ -66016,7 +66128,7 @@ function pluginSourceRemoveCommand(name, opts) {
66016
66128
 
66017
66129
  // src/index.ts
66018
66130
  var program2 = new Command();
66019
- program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.55").enablePositionalOptions();
66131
+ program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.56").enablePositionalOptions();
66020
66132
  program2.command("auth").description("Login to your Nexrall account").action(authCommand);
66021
66133
  program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
66022
66134
  program2.command("update").description("Update nex to the latest version").option("-c, --check", "Check for updates without installing").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.55",
3
+ "version": "0.5.56",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",
@@ -41,7 +41,7 @@
41
41
  "react": "^19.2.8",
42
42
  "readline": "^1.3.0",
43
43
  "string-width": "^7.2.0",
44
- "@nexrall/code-core": "1.4.30"
44
+ "@nexrall/code-core": "1.4.31"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@aws-sdk/client-s3": "^3.600.0",