nexrall-code 0.5.22 → 0.5.24
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.
- package/dist/index.js +228 -27
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12800,7 +12800,7 @@ ${fresh || "(no new output)"}${trunc}` };
|
|
|
12800
12800
|
}
|
|
12801
12801
|
return { output: `Background shell ${id} ${shell.status}.` };
|
|
12802
12802
|
}
|
|
12803
|
-
async function bash(input, abortSignal, sandbox, workDir) {
|
|
12803
|
+
async function bash(input, abortSignal, sandbox, workDir, onStream) {
|
|
12804
12804
|
const command = typeof input.command === "string" ? input.command : "";
|
|
12805
12805
|
const _rawDesc = typeof input.description === "string" ? input.description : "";
|
|
12806
12806
|
void _rawDesc.slice(0, 200);
|
|
@@ -12951,8 +12951,22 @@ ${body}` : void 0 }));
|
|
|
12951
12951
|
resolve3(prependNote({ output: body, exitCode: 0 }));
|
|
12952
12952
|
}
|
|
12953
12953
|
};
|
|
12954
|
-
child.stdout?.on("data", (chunk) =>
|
|
12955
|
-
|
|
12954
|
+
child.stdout?.on("data", (chunk) => {
|
|
12955
|
+
const text = chunk.toString("utf-8");
|
|
12956
|
+
appendOutput(text);
|
|
12957
|
+
try {
|
|
12958
|
+
onStream?.(text);
|
|
12959
|
+
} catch {
|
|
12960
|
+
}
|
|
12961
|
+
});
|
|
12962
|
+
child.stderr?.on("data", (chunk) => {
|
|
12963
|
+
const text = chunk.toString("utf-8");
|
|
12964
|
+
appendOutput(text);
|
|
12965
|
+
try {
|
|
12966
|
+
onStream?.(text);
|
|
12967
|
+
} catch {
|
|
12968
|
+
}
|
|
12969
|
+
});
|
|
12956
12970
|
child.on("close", (code, signal) => done(code, signal));
|
|
12957
12971
|
child.on("error", (err) => {
|
|
12958
12972
|
settled = true;
|
|
@@ -14148,12 +14162,12 @@ ${expanded}` };
|
|
|
14148
14162
|
"memory_read",
|
|
14149
14163
|
"use_skill"
|
|
14150
14164
|
]);
|
|
14151
|
-
async function executeTool(name, input, abortSignal, sandbox, workDir, agentScope) {
|
|
14165
|
+
async function executeTool(name, input, abortSignal, sandbox, workDir, agentScope, onStream) {
|
|
14152
14166
|
if (!TOOL_MAP[name])
|
|
14153
14167
|
return { error: `Unknown tool: ${name}` };
|
|
14154
14168
|
try {
|
|
14155
14169
|
if (name === "bash")
|
|
14156
|
-
return await bash(input, abortSignal, sandbox, workDir);
|
|
14170
|
+
return await bash(input, abortSignal, sandbox, workDir, input.run_in_background === true ? void 0 : onStream);
|
|
14157
14171
|
if (name === "todo_write")
|
|
14158
14172
|
return await todoWrite(input, agentScope);
|
|
14159
14173
|
if (name === "todo_read")
|
|
@@ -14218,7 +14232,7 @@ var require_agentTypes = __commonJS({
|
|
|
14218
14232
|
};
|
|
14219
14233
|
}();
|
|
14220
14234
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
14221
|
-
exports2.loadAgentTypes =
|
|
14235
|
+
exports2.loadAgentTypes = loadAgentTypes2;
|
|
14222
14236
|
exports2.summariseAgents = summariseAgents;
|
|
14223
14237
|
exports2.findAgentType = findAgentType;
|
|
14224
14238
|
var fs6 = __importStar(require("fs"));
|
|
@@ -14297,7 +14311,7 @@ var require_agentTypes = __commonJS({
|
|
|
14297
14311
|
}
|
|
14298
14312
|
}
|
|
14299
14313
|
}
|
|
14300
|
-
function
|
|
14314
|
+
function loadAgentTypes2(workDir) {
|
|
14301
14315
|
const out = /* @__PURE__ */ new Map();
|
|
14302
14316
|
loadDir(path5.join(workDir, ".nexrall", "agents"), "project", out);
|
|
14303
14317
|
loadDir(path5.join(os5.homedir(), ".nexrall", "agents"), "global", out);
|
|
@@ -14718,14 +14732,16 @@ var require_loop = __commonJS({
|
|
|
14718
14732
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
14719
14733
|
exports2.VERIFY_CMD_RE = exports2.WRITE_TOOL_NAMES = void 0;
|
|
14720
14734
|
exports2.resolveMaxIterations = resolveMaxIterations;
|
|
14721
|
-
exports2.
|
|
14735
|
+
exports2.contextWindowFor = contextWindowFor2;
|
|
14736
|
+
exports2.compactionThresholds = compactionThresholds2;
|
|
14737
|
+
exports2.estimateBodyBytes = estimateBodyBytes2;
|
|
14722
14738
|
exports2.findSafeCutIndex = findSafeCutIndex;
|
|
14723
14739
|
exports2.transcriptOf = transcriptOf;
|
|
14724
14740
|
exports2.createLedger = createLedger;
|
|
14725
14741
|
exports2.ledgerRecord = ledgerRecord;
|
|
14726
14742
|
exports2.ledgerSummary = ledgerSummary;
|
|
14727
14743
|
exports2.pruneOldToolResults = pruneOldToolResults;
|
|
14728
|
-
exports2.estimateTokensRough =
|
|
14744
|
+
exports2.estimateTokensRough = estimateTokensRough2;
|
|
14729
14745
|
exports2.compactMessagesForResume = compactMessagesForResume2;
|
|
14730
14746
|
exports2.runAgentLoop = runAgentLoop2;
|
|
14731
14747
|
exports2.trimToResumableBoundary = trimToResumableBoundary;
|
|
@@ -14961,6 +14977,7 @@ var require_loop = __commonJS({
|
|
|
14961
14977
|
}
|
|
14962
14978
|
var MAX_TASK_DEPTH = 2;
|
|
14963
14979
|
var _subTaskCounter = 0;
|
|
14980
|
+
var SUBTASK_TIMEOUT_MS = Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) > 0 ? Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) : 10 * 60 * 1e3;
|
|
14964
14981
|
async function runSubTask(input, options, agentTypes) {
|
|
14965
14982
|
const prompt2 = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
14966
14983
|
if (!prompt2)
|
|
@@ -14990,6 +15007,14 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
14990
15007
|
const subMessages = [
|
|
14991
15008
|
{ role: "user", content: [{ type: "text", text: prompt2 }] }
|
|
14992
15009
|
];
|
|
15010
|
+
const subAbort = { aborted: false };
|
|
15011
|
+
const timer = setTimeout(() => {
|
|
15012
|
+
subAbort.aborted = true;
|
|
15013
|
+
}, SUBTASK_TIMEOUT_MS);
|
|
15014
|
+
const parentAbortPoll = setInterval(() => {
|
|
15015
|
+
if (options.abortSignal?.aborted)
|
|
15016
|
+
subAbort.aborted = true;
|
|
15017
|
+
}, 250);
|
|
14993
15018
|
try {
|
|
14994
15019
|
const result = await runAgentLoop2(subMessages, {
|
|
14995
15020
|
...options,
|
|
@@ -15000,6 +15025,7 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
15000
15025
|
// fresh isolated context for sub-agent
|
|
15001
15026
|
model: agent?.model ?? options.model,
|
|
15002
15027
|
nexrallMd: subNexrallMd,
|
|
15028
|
+
abortSignal: subAbort,
|
|
15003
15029
|
requestPermission: gatedPermission,
|
|
15004
15030
|
onText: () => {
|
|
15005
15031
|
},
|
|
@@ -15025,11 +15051,15 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
15025
15051
|
// when the name was already labelled, and mixed display concerns into the data layer).
|
|
15026
15052
|
onToolUse: (n, i2) => options.onToolUse(n, i2, true),
|
|
15027
15053
|
onToolResult: (n, r2) => options.onToolResult(n, r2, true),
|
|
15054
|
+
onToolStreamChunk: (n, c) => options.onToolStreamChunk?.(n, c, true),
|
|
15028
15055
|
// Forward thinking so the UI shows the indicator while sub-agent reasons
|
|
15029
15056
|
onThinking: (text2) => options.onThinking?.(text2),
|
|
15030
15057
|
onThinkingDelta: (text2) => options.onThinkingDelta?.(text2),
|
|
15031
15058
|
onThinkingProgress: (tok) => options.onThinkingProgress?.(tok)
|
|
15032
15059
|
});
|
|
15060
|
+
if (subAbort.aborted && !options.abortSignal?.aborted) {
|
|
15061
|
+
return { error: `Sub-task stalled and was stopped after ${Math.round(SUBTASK_TIMEOUT_MS / 6e4)} minutes with no completion. Consider breaking it into smaller sub-tasks or investigating what it may have been stuck on (a hung command, an unresponsive MCP/tool call, or a very large scope).` };
|
|
15062
|
+
}
|
|
15033
15063
|
const lastAssistant = [...result].reverse().find((m2) => m2.role === "assistant");
|
|
15034
15064
|
let text = (lastAssistant?.content ?? []).filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
15035
15065
|
const SUBTASK_MAX = 48e3;
|
|
@@ -15045,6 +15075,9 @@ ${tail}`;
|
|
|
15045
15075
|
return { output: text || "(sub-task completed with no text output)" };
|
|
15046
15076
|
} catch (err) {
|
|
15047
15077
|
return { error: `Sub-task failed: ${err.message}` };
|
|
15078
|
+
} finally {
|
|
15079
|
+
clearTimeout(timer);
|
|
15080
|
+
clearInterval(parentAbortPoll);
|
|
15048
15081
|
}
|
|
15049
15082
|
}
|
|
15050
15083
|
var MODEL_CONTEXT_TOKENS = {
|
|
@@ -15052,16 +15085,22 @@ ${tail}`;
|
|
|
15052
15085
|
pro: 1e6,
|
|
15053
15086
|
ultra: 1e6
|
|
15054
15087
|
};
|
|
15088
|
+
function contextWindowFor2(model) {
|
|
15089
|
+
return MODEL_CONTEXT_TOKENS[model ?? "turbo"] ?? 1e6;
|
|
15090
|
+
}
|
|
15055
15091
|
function envFraction(name, fallback) {
|
|
15056
15092
|
const v = Number(process.env[name]);
|
|
15057
15093
|
return Number.isFinite(v) && v > 0 && v < 1 ? v : fallback;
|
|
15058
15094
|
}
|
|
15059
15095
|
var AUTO_PRUNE_THRESHOLD = envFraction("NEXRALL_PRUNE_THRESHOLD", 0.35);
|
|
15060
15096
|
var AUTO_COMPACT_THRESHOLD = envFraction("NEXRALL_COMPACT_THRESHOLD", 0.8);
|
|
15097
|
+
function compactionThresholds2() {
|
|
15098
|
+
return { prune: AUTO_PRUNE_THRESHOLD, compact: AUTO_COMPACT_THRESHOLD };
|
|
15099
|
+
}
|
|
15061
15100
|
var PRUNE_MIN_RECLAIM_BYTES = 256 * 1024;
|
|
15062
15101
|
var COMPACT_KEEP_MIN = 6;
|
|
15063
15102
|
var MAX_BODY_BYTES = 8 * 1024 * 1024;
|
|
15064
|
-
function
|
|
15103
|
+
function estimateBodyBytes2(messages) {
|
|
15065
15104
|
try {
|
|
15066
15105
|
return Buffer.byteLength(JSON.stringify(messages), "utf-8");
|
|
15067
15106
|
} catch {
|
|
@@ -15327,8 +15366,8 @@ Continue the work from here.` }] });
|
|
|
15327
15366
|
}
|
|
15328
15367
|
}
|
|
15329
15368
|
var RESUME_CHARS_PER_TOKEN = 4;
|
|
15330
|
-
function
|
|
15331
|
-
return Math.ceil(
|
|
15369
|
+
function estimateTokensRough2(messages) {
|
|
15370
|
+
return Math.ceil(estimateBodyBytes2(messages) / RESUME_CHARS_PER_TOKEN);
|
|
15332
15371
|
}
|
|
15333
15372
|
async function compactMessagesForResume2(messages, opts) {
|
|
15334
15373
|
if (messages.length <= COMPACT_KEEP_MIN + 2)
|
|
@@ -15337,8 +15376,8 @@ Continue the work from here.` }] });
|
|
|
15337
15376
|
if (!resolveAutoCompact(void 0, settings.raw))
|
|
15338
15377
|
return false;
|
|
15339
15378
|
const contextWindow = MODEL_CONTEXT_TOKENS[opts.model ?? "turbo"] ?? 1e6;
|
|
15340
|
-
let bodyBytes =
|
|
15341
|
-
let tokenGuess =
|
|
15379
|
+
let bodyBytes = estimateBodyBytes2(messages);
|
|
15380
|
+
let tokenGuess = estimateTokensRough2(messages);
|
|
15342
15381
|
const overPruneThreshold = () => tokenGuess > contextWindow * AUTO_PRUNE_THRESHOLD || bodyBytes > MAX_BODY_BYTES;
|
|
15343
15382
|
const overCompactThreshold = () => tokenGuess > contextWindow * AUTO_COMPACT_THRESHOLD || bodyBytes > MAX_BODY_BYTES;
|
|
15344
15383
|
if (!overPruneThreshold())
|
|
@@ -15347,8 +15386,8 @@ Continue the work from here.` }] });
|
|
|
15347
15386
|
if (messages.length > PRUNE_KEEP_RECENT + 2) {
|
|
15348
15387
|
const reclaimed = pruneOldToolResults(messages, PRUNE_MIN_RECLAIM_BYTES);
|
|
15349
15388
|
if (reclaimed > 0) {
|
|
15350
|
-
bodyBytes =
|
|
15351
|
-
tokenGuess =
|
|
15389
|
+
bodyBytes = estimateBodyBytes2(messages);
|
|
15390
|
+
tokenGuess = estimateTokensRough2(messages);
|
|
15352
15391
|
compacted = true;
|
|
15353
15392
|
opts.onNotice?.(`
|
|
15354
15393
|
\u267B\uFE0F Trimmed ~${(reclaimed / (1024 * 1024)).toFixed(1)}MB of older tool output before resuming this chat.
|
|
@@ -15376,8 +15415,8 @@ Continue the work from here.` }] });
|
|
|
15376
15415
|
if (!did)
|
|
15377
15416
|
break;
|
|
15378
15417
|
compacted = true;
|
|
15379
|
-
bodyBytes =
|
|
15380
|
-
tokenGuess =
|
|
15418
|
+
bodyBytes = estimateBodyBytes2(messages);
|
|
15419
|
+
tokenGuess = estimateTokensRough2(messages);
|
|
15381
15420
|
}
|
|
15382
15421
|
if (compacted) {
|
|
15383
15422
|
opts.onNotice?.(`
|
|
@@ -15422,14 +15461,14 @@ Continue the work from here.` }] });
|
|
|
15422
15461
|
for (; iteration < budget; iteration++) {
|
|
15423
15462
|
if (options.abortSignal?.aborted)
|
|
15424
15463
|
break;
|
|
15425
|
-
let bodyBytes =
|
|
15464
|
+
let bodyBytes = estimateBodyBytes2(messages);
|
|
15426
15465
|
const prunePressure = lastPromptTokens > contextWindow * AUTO_PRUNE_THRESHOLD;
|
|
15427
15466
|
const tokenPressure = lastPromptTokens > contextWindow * AUTO_COMPACT_THRESHOLD;
|
|
15428
15467
|
let bytePressure = bodyBytes > MAX_BODY_BYTES;
|
|
15429
15468
|
if (autoCompact && !compacting && (prunePressure || bytePressure) && messages.length > PRUNE_KEEP_RECENT + 2) {
|
|
15430
15469
|
const reclaimed = pruneOldToolResults(messages, PRUNE_MIN_RECLAIM_BYTES);
|
|
15431
15470
|
if (reclaimed > 0) {
|
|
15432
|
-
bodyBytes =
|
|
15471
|
+
bodyBytes = estimateBodyBytes2(messages);
|
|
15433
15472
|
bytePressure = bodyBytes > MAX_BODY_BYTES;
|
|
15434
15473
|
(options.onNotice ?? options.onText)(`\u267B\uFE0F Trimmed ~${(reclaimed / (1024 * 1024)).toFixed(1)}MB of already-processed tool output to keep this chat cheap to continue.`);
|
|
15435
15474
|
}
|
|
@@ -15649,12 +15688,13 @@ Continue the work from here.` }] });
|
|
|
15649
15688
|
} else {
|
|
15650
15689
|
options.checkpointManager?.recordBeforeMutation(name, input);
|
|
15651
15690
|
const WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file", "multi_edit", "delete_file", "move_file", "copy_file", "notebook_edit"]);
|
|
15691
|
+
const onStream = options.onToolStreamChunk ? (chunk) => options.onToolStreamChunk(name, chunk) : void 0;
|
|
15652
15692
|
if (WRITE_TOOLS.has(name)) {
|
|
15653
15693
|
const targetPath = typeof input.path === "string" ? input.path : typeof input.source === "string" ? input.source : "";
|
|
15654
15694
|
const absTarget = targetPath && options.workDir ? path5.resolve(options.workDir, targetPath) : targetPath;
|
|
15655
|
-
result = await withFileLock(absTarget || name, () => (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope));
|
|
15695
|
+
result = await withFileLock(absTarget || name, () => (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope, onStream));
|
|
15656
15696
|
} else {
|
|
15657
|
-
result = await (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope);
|
|
15697
|
+
result = await (0, executor_1.executeTool)(name, input, options.abortSignal, sandboxCfg, options.workDir, agentScope, onStream);
|
|
15658
15698
|
}
|
|
15659
15699
|
}
|
|
15660
15700
|
} catch (err) {
|
|
@@ -16323,7 +16363,7 @@ var require_manager = __commonJS({
|
|
|
16323
16363
|
var httpClient_1 = require_httpClient();
|
|
16324
16364
|
var sseClient_1 = require_sseClient();
|
|
16325
16365
|
var index_1 = require_plugins();
|
|
16326
|
-
var
|
|
16366
|
+
var McpManager2 = class _McpManager {
|
|
16327
16367
|
constructor() {
|
|
16328
16368
|
this._clients = /* @__PURE__ */ new Map();
|
|
16329
16369
|
this._toolMap = /* @__PURE__ */ new Map();
|
|
@@ -16496,7 +16536,7 @@ var require_manager = __commonJS({
|
|
|
16496
16536
|
this._needsAuth.clear();
|
|
16497
16537
|
}
|
|
16498
16538
|
};
|
|
16499
|
-
exports2.McpManager =
|
|
16539
|
+
exports2.McpManager = McpManager2;
|
|
16500
16540
|
}
|
|
16501
16541
|
});
|
|
16502
16542
|
|
|
@@ -23365,6 +23405,49 @@ function formatToolUse(name, input) {
|
|
|
23365
23405
|
const argDisplay = primaryArg ? source_default.dim(" ") + source_default.white(String(primaryArg).replace(/\n/g, "\u21B5").slice(0, 80)) : "";
|
|
23366
23406
|
return source_default.yellow(icons.tool) + " " + source_default.bold(name) + argDisplay;
|
|
23367
23407
|
}
|
|
23408
|
+
var ToolStreamPrinter = class _ToolStreamPrinter {
|
|
23409
|
+
carry = "";
|
|
23410
|
+
linesShown = 0;
|
|
23411
|
+
// Cap visible lines per command so a runaway/very chatty process (e.g. a
|
|
23412
|
+
// build tool re-printing a progress bar thousands of times) doesn't flood
|
|
23413
|
+
// the terminal — the full output is still available afterward via
|
|
23414
|
+
// formatToolResult's summary and, for large output, the spill file.
|
|
23415
|
+
static MAX_LINES = 200;
|
|
23416
|
+
capped = false;
|
|
23417
|
+
feed(chunk) {
|
|
23418
|
+
if (this.capped)
|
|
23419
|
+
return;
|
|
23420
|
+
this.carry += chunk;
|
|
23421
|
+
const lines = this.carry.split("\n");
|
|
23422
|
+
this.carry = lines.pop() ?? "";
|
|
23423
|
+
for (const line of lines) {
|
|
23424
|
+
if (!this.printLine(line))
|
|
23425
|
+
return;
|
|
23426
|
+
}
|
|
23427
|
+
}
|
|
23428
|
+
printLine(line) {
|
|
23429
|
+
if (this.linesShown >= _ToolStreamPrinter.MAX_LINES) {
|
|
23430
|
+
if (!this.capped) {
|
|
23431
|
+
this.capped = true;
|
|
23432
|
+
process.stdout.write(source_default.dim(" \u2502 \u2026 output continues (full log in the result above once finished)\n"));
|
|
23433
|
+
}
|
|
23434
|
+
return false;
|
|
23435
|
+
}
|
|
23436
|
+
this.linesShown++;
|
|
23437
|
+
process.stdout.write(source_default.dim(" \u2502 ") + source_default.gray(line.slice(0, 300)) + "\n");
|
|
23438
|
+
return true;
|
|
23439
|
+
}
|
|
23440
|
+
flush() {
|
|
23441
|
+
if (!this.capped && this.carry)
|
|
23442
|
+
this.printLine(this.carry);
|
|
23443
|
+
this.carry = "";
|
|
23444
|
+
}
|
|
23445
|
+
reset() {
|
|
23446
|
+
this.carry = "";
|
|
23447
|
+
this.linesShown = 0;
|
|
23448
|
+
this.capped = false;
|
|
23449
|
+
}
|
|
23450
|
+
};
|
|
23368
23451
|
function formatToolResult(name, result, durationMs) {
|
|
23369
23452
|
if (result.error) {
|
|
23370
23453
|
return source_default.red(icons.cross) + " " + source_default.dim(name) + source_default.dim(" \xB7 ") + source_default.red("error") + source_default.dim(` \xB7 ${durationMs}ms`);
|
|
@@ -24500,10 +24583,71 @@ var Spinner = class {
|
|
|
24500
24583
|
return this.interval !== null;
|
|
24501
24584
|
}
|
|
24502
24585
|
};
|
|
24586
|
+
var _mcpManager = null;
|
|
24587
|
+
var _mcpConfig = null;
|
|
24588
|
+
async function initMcp(workDir) {
|
|
24589
|
+
_mcpConfig = import_code_core3.McpManager.loadConfig(workDir);
|
|
24590
|
+
if (Object.keys(_mcpConfig.mcpServers).length === 0) {
|
|
24591
|
+
_mcpManager = null;
|
|
24592
|
+
return;
|
|
24593
|
+
}
|
|
24594
|
+
const manager = new import_code_core3.McpManager();
|
|
24595
|
+
await manager.connectAll(_mcpConfig);
|
|
24596
|
+
_mcpManager = manager;
|
|
24597
|
+
}
|
|
24598
|
+
function formatMcpStatus() {
|
|
24599
|
+
if (!_mcpConfig || Object.keys(_mcpConfig.mcpServers).length === 0) {
|
|
24600
|
+
return source_default.dim(" No MCP servers configured. Add one to .nexrall/mcp.json or ~/.nexrall/mcp.json.");
|
|
24601
|
+
}
|
|
24602
|
+
if (!_mcpManager)
|
|
24603
|
+
return source_default.dim(" MCP servers configured but not connected.");
|
|
24604
|
+
const rows = _mcpManager.getStatus();
|
|
24605
|
+
const lines = rows.map((s2) => {
|
|
24606
|
+
if (s2.connected) {
|
|
24607
|
+
return " " + source_default.green("\u25CF") + " " + source_default.bold(s2.name) + source_default.dim(` (${s2.transport}) \u2014 ${s2.tools.length} tool(s)`);
|
|
24608
|
+
}
|
|
24609
|
+
const authNote = s2.needsAuth ? source_default.yellow(" [needs OAuth \u2014 not supported in CLI yet, add a static Authorization header]") : "";
|
|
24610
|
+
return " " + source_default.red("\u25CF") + " " + source_default.bold(s2.name) + source_default.dim(` (${s2.transport}) \u2014 `) + source_default.red(s2.error ?? "failed to connect") + authNote;
|
|
24611
|
+
});
|
|
24612
|
+
return lines.join("\n");
|
|
24613
|
+
}
|
|
24614
|
+
function formatContextUsage(messages, modelAlias) {
|
|
24615
|
+
const window = (0, import_code_core3.contextWindowFor)(modelAlias);
|
|
24616
|
+
const tokens = (0, import_code_core3.estimateTokensRough)(messages);
|
|
24617
|
+
const bytes = (0, import_code_core3.estimateBodyBytes)(messages);
|
|
24618
|
+
const { prune, compact } = (0, import_code_core3.compactionThresholds)();
|
|
24619
|
+
const pct = Math.min(100, tokens / window * 100);
|
|
24620
|
+
const barWidth = 30;
|
|
24621
|
+
const filled = Math.round(pct / 100 * barWidth);
|
|
24622
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
|
|
24623
|
+
const barColor = pct >= compact * 100 ? source_default.red : pct >= prune * 100 ? source_default.yellow : source_default.green;
|
|
24624
|
+
const lines = [
|
|
24625
|
+
"",
|
|
24626
|
+
" " + source_default.bold("Context usage"),
|
|
24627
|
+
" " + barColor(bar) + source_default.dim(` ~${pct.toFixed(1)}%`),
|
|
24628
|
+
source_default.dim(` ~${tokens.toLocaleString()} / ${window.toLocaleString()} tokens (rough estimate) \xB7 ${(bytes / 1024).toFixed(0)}KB serialized \xB7 ${messages.length} messages`),
|
|
24629
|
+
source_default.dim(` auto-prune at ~${(prune * 100).toFixed(0)}% \xB7 auto-compact (summarise) at ~${(compact * 100).toFixed(0)}%`),
|
|
24630
|
+
""
|
|
24631
|
+
];
|
|
24632
|
+
return lines.join("\n");
|
|
24633
|
+
}
|
|
24634
|
+
function formatAgentsList(workDir) {
|
|
24635
|
+
const types3 = (0, import_code_core3.loadAgentTypes)(workDir);
|
|
24636
|
+
if (!types3.length)
|
|
24637
|
+
return source_default.dim(" No agent types found (this should not happen \u2014 builtins always register).");
|
|
24638
|
+
const lines = types3.map((t2) => {
|
|
24639
|
+
const tools = t2.tools ? source_default.dim(` \u2014 tools: ${t2.tools.join(", ")}`) : source_default.dim(" \u2014 all tools");
|
|
24640
|
+
const model = t2.model ? source_default.dim(` \u2014 model: ${t2.model}`) : "";
|
|
24641
|
+
const scope = source_default.dim(` (${t2.source})`);
|
|
24642
|
+
return " " + source_default.cyan(t2.name.padEnd(16)) + source_default.white(t2.description) + scope + tools + model;
|
|
24643
|
+
});
|
|
24644
|
+
return lines.join("\n");
|
|
24645
|
+
}
|
|
24503
24646
|
async function runTurn(messages, modelAlias, workDir, abortSignal, env2, nexrallMd, mode, effort, checkpointManager, onProgress) {
|
|
24504
24647
|
let lastUsage;
|
|
24505
24648
|
const spinner = new Spinner();
|
|
24506
24649
|
const mdRender = new MarkdownStreamRenderer();
|
|
24650
|
+
const toolStream = new ToolStreamPrinter();
|
|
24507
24651
|
let toolStartTime = 0;
|
|
24508
24652
|
let lastToolName = "";
|
|
24509
24653
|
let thinkingTokens = 0;
|
|
@@ -24551,14 +24695,27 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env2, nexrall
|
|
|
24551
24695
|
return;
|
|
24552
24696
|
mdRender.flush();
|
|
24553
24697
|
spinner.stop();
|
|
24698
|
+
toolStream.reset();
|
|
24554
24699
|
process.stdout.write("\n");
|
|
24555
24700
|
console.log(formatToolUse(name, input));
|
|
24556
24701
|
lastToolName = name;
|
|
24557
24702
|
toolStartTime = Date.now();
|
|
24558
24703
|
spinner.start(`running ${name}\u2026`);
|
|
24559
24704
|
},
|
|
24705
|
+
// Live progress for foreground bash: the FIRST chunk stops the "running…"
|
|
24706
|
+
// spinner (which would otherwise overwrite/interleave badly with printed
|
|
24707
|
+
// lines) and every subsequent chunk streams straight to the terminal —
|
|
24708
|
+
// see ToolStreamPrinter for the line-buffering/cap logic.
|
|
24709
|
+
onToolStreamChunk: (_name, chunk) => {
|
|
24710
|
+
if (abortSignal.aborted)
|
|
24711
|
+
return;
|
|
24712
|
+
if (spinner.active)
|
|
24713
|
+
spinner.stop();
|
|
24714
|
+
toolStream.feed(chunk);
|
|
24715
|
+
},
|
|
24560
24716
|
onToolResult: (_name, res) => {
|
|
24561
24717
|
spinner.stop();
|
|
24718
|
+
toolStream.flush();
|
|
24562
24719
|
const durationMs = Date.now() - toolStartTime;
|
|
24563
24720
|
console.log(formatToolResult(lastToolName, res, durationMs));
|
|
24564
24721
|
toolStartTime = 0;
|
|
@@ -24610,7 +24767,8 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env2, nexrall
|
|
|
24610
24767
|
},
|
|
24611
24768
|
requestPermission,
|
|
24612
24769
|
checkpointManager,
|
|
24613
|
-
onProgress
|
|
24770
|
+
onProgress,
|
|
24771
|
+
mcpManager: _mcpManager ?? void 0
|
|
24614
24772
|
});
|
|
24615
24773
|
mdRender.flush();
|
|
24616
24774
|
spinner.stop();
|
|
@@ -24650,6 +24808,13 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env2
|
|
|
24650
24808
|
onToolUse: (name, input) => {
|
|
24651
24809
|
emit({ type: "tool_use", tool: name, input });
|
|
24652
24810
|
},
|
|
24811
|
+
// Live bash progress for scripted/CI consumers — lets a wrapper tail a
|
|
24812
|
+
// long-running build/test command instead of blocking silently until
|
|
24813
|
+
// tool_result. Chunk is raw and un-truncated (unlike the final result,
|
|
24814
|
+
// which is head/tail-capped), so a chatty command can emit many of these.
|
|
24815
|
+
onToolStreamChunk: (name, chunk) => {
|
|
24816
|
+
emit({ type: "tool_stream", tool: name, chunk });
|
|
24817
|
+
},
|
|
24653
24818
|
onToolResult: (name, res) => {
|
|
24654
24819
|
toolCallCount++;
|
|
24655
24820
|
emit({ type: "tool_result", tool: name, ok: res.error === void 0, ...res.error ? { error: res.error } : {} });
|
|
@@ -24688,7 +24853,8 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env2
|
|
|
24688
24853
|
return true;
|
|
24689
24854
|
},
|
|
24690
24855
|
checkpointManager,
|
|
24691
|
-
onProgress
|
|
24856
|
+
onProgress,
|
|
24857
|
+
mcpManager: _mcpManager ?? void 0
|
|
24692
24858
|
});
|
|
24693
24859
|
if (format === "json") {
|
|
24694
24860
|
process.stdout.write(JSON.stringify({
|
|
@@ -24725,6 +24891,9 @@ function printHelp() {
|
|
|
24725
24891
|
["/add <filepath>", "Add a file to conversation context"],
|
|
24726
24892
|
["/skills", "List skills (.nexrall/skills/<name>/SKILL.md or .nexrall/commands/*.md) \u2014 the model can also auto-invoke these"],
|
|
24727
24893
|
["/plugins", "List installed plugins (.nexrall/plugins/)"],
|
|
24894
|
+
["/mcp", "Show MCP server connection status"],
|
|
24895
|
+
["/agents", "List available sub-agent types (for the task tool)"],
|
|
24896
|
+
["/context", "Show context window usage for this conversation"],
|
|
24728
24897
|
["/update", "Update nex to the latest version"]
|
|
24729
24898
|
];
|
|
24730
24899
|
for (const [cmd, desc] of cmds) {
|
|
@@ -24785,6 +24954,15 @@ async function startChatSession(options) {
|
|
|
24785
24954
|
let skills = (0, import_code_core3.loadSkills)(workDir);
|
|
24786
24955
|
if (skills.length && !headless)
|
|
24787
24956
|
console.log(source_default.dim(` ${skills.length} skill(s) loaded`));
|
|
24957
|
+
try {
|
|
24958
|
+
await initMcp(workDir);
|
|
24959
|
+
const connected = _mcpManager?.serverNames.length ?? 0;
|
|
24960
|
+
const total = _mcpConfig ? Object.keys(_mcpConfig.mcpServers).length : 0;
|
|
24961
|
+
if (total && !headless) {
|
|
24962
|
+
console.log(source_default.dim(` ${connected}/${total} MCP server(s) connected`) + source_default.dim(" (/mcp for details)"));
|
|
24963
|
+
}
|
|
24964
|
+
} catch {
|
|
24965
|
+
}
|
|
24788
24966
|
if (headless && !options.prompt && !options.stdinText) {
|
|
24789
24967
|
process.stdout.write(JSON.stringify({ type: "error", error: "--output-format json/stream-json requires a one-shot prompt (or piped stdin)." }) + "\n");
|
|
24790
24968
|
process.exit(1);
|
|
@@ -25306,6 +25484,29 @@ ${content}
|
|
|
25306
25484
|
rl.prompt();
|
|
25307
25485
|
return;
|
|
25308
25486
|
}
|
|
25487
|
+
case "/mcp": {
|
|
25488
|
+
console.log();
|
|
25489
|
+
console.log(source_default.bold(" MCP servers:"));
|
|
25490
|
+
console.log(formatMcpStatus());
|
|
25491
|
+
console.log();
|
|
25492
|
+
rl.prompt();
|
|
25493
|
+
return;
|
|
25494
|
+
}
|
|
25495
|
+
case "/agents": {
|
|
25496
|
+
console.log();
|
|
25497
|
+
console.log(source_default.bold(" Sub-agent types (used by the `task` tool):"));
|
|
25498
|
+
console.log(formatAgentsList(workDir));
|
|
25499
|
+
console.log();
|
|
25500
|
+
console.log(source_default.dim(" Define your own in .nexrall/agents/<name>.md or ~/.nexrall/agents/<name>.md"));
|
|
25501
|
+
console.log();
|
|
25502
|
+
rl.prompt();
|
|
25503
|
+
return;
|
|
25504
|
+
}
|
|
25505
|
+
case "/context": {
|
|
25506
|
+
console.log(formatContextUsage(messages, modelAlias));
|
|
25507
|
+
rl.prompt();
|
|
25508
|
+
return;
|
|
25509
|
+
}
|
|
25309
25510
|
case "/skills":
|
|
25310
25511
|
case "/commands": {
|
|
25311
25512
|
skills = (0, import_code_core3.loadSkills)(workDir);
|
|
@@ -25596,7 +25797,7 @@ function pluginListCommand() {
|
|
|
25596
25797
|
|
|
25597
25798
|
// src/index.ts
|
|
25598
25799
|
var program2 = new Command();
|
|
25599
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
25800
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.24");
|
|
25600
25801
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
25601
25802
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
25602
25803
|
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.
|
|
3
|
+
"version": "0.5.24",
|
|
4
4
|
"description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"ora": "^8.0.1",
|
|
38
38
|
"prompts": "^2.4.2",
|
|
39
39
|
"readline": "^1.3.0",
|
|
40
|
-
"@nexrall/code-core": "1.4.
|
|
40
|
+
"@nexrall/code-core": "1.4.19"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@aws-sdk/client-s3": "^3.600.0",
|