nexrall-code 0.5.12 → 0.5.18
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 +1572 -597
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -3070,6 +3070,27 @@ var require_types = __commonJS({
|
|
|
3070
3070
|
"../core/dist/types.js"(exports2) {
|
|
3071
3071
|
"use strict";
|
|
3072
3072
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3073
|
+
exports2.AgentTurnError = void 0;
|
|
3074
|
+
exports2.isAgentTurnError = isAgentTurnError;
|
|
3075
|
+
exports2.salvageHistory = salvageHistory2;
|
|
3076
|
+
var AgentTurnError = class extends Error {
|
|
3077
|
+
constructor(message, messages, progressCount, cause) {
|
|
3078
|
+
super(message);
|
|
3079
|
+
this.name = "AgentTurnError";
|
|
3080
|
+
this.messages = messages;
|
|
3081
|
+
this.progressCount = Math.max(0, progressCount);
|
|
3082
|
+
this.cause = cause;
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
exports2.AgentTurnError = AgentTurnError;
|
|
3086
|
+
function isAgentTurnError(err) {
|
|
3087
|
+
return err instanceof AgentTurnError;
|
|
3088
|
+
}
|
|
3089
|
+
function salvageHistory2(err) {
|
|
3090
|
+
if (!isAgentTurnError(err))
|
|
3091
|
+
return null;
|
|
3092
|
+
return err.progressCount > 0 ? err.messages : null;
|
|
3093
|
+
}
|
|
3073
3094
|
}
|
|
3074
3095
|
});
|
|
3075
3096
|
|
|
@@ -9812,18 +9833,46 @@ var require_client = __commonJS({
|
|
|
9812
9833
|
exports2.API_BASE = void 0;
|
|
9813
9834
|
exports2.chooseFinalContent = chooseFinalContent;
|
|
9814
9835
|
exports2.streamChat = streamChat;
|
|
9836
|
+
exports2.cancelTurn = cancelTurn;
|
|
9815
9837
|
exports2.getBalance = getBalance3;
|
|
9816
9838
|
exports2.exchangeVscodeCode = exchangeVscodeCode;
|
|
9817
9839
|
exports2.login = login2;
|
|
9818
9840
|
var eventsource_parser_1 = require_dist();
|
|
9819
9841
|
var node_fetch_1 = __importDefault((init_src(), __toCommonJS(src_exports)));
|
|
9842
|
+
var crypto_1 = require("crypto");
|
|
9820
9843
|
var index_1 = require_auth();
|
|
9821
|
-
|
|
9844
|
+
var DEFAULT_API_BASE = "https://api.nexrall.com";
|
|
9845
|
+
function resolveApiBase() {
|
|
9846
|
+
const raw = process.env.NEXRALL_API_BASE?.trim();
|
|
9847
|
+
if (!raw)
|
|
9848
|
+
return DEFAULT_API_BASE;
|
|
9849
|
+
const isLoopback = /^http:\/\/(127\.0\.0\.1|\[::1\]|localhost)(:\d+)?(\/|$)/i.test(raw);
|
|
9850
|
+
if (isLoopback) {
|
|
9851
|
+
console.error(`\u26A0\uFE0F Nexrall API override in effect (loopback): ${raw.replace(/\/+$/, "")}`);
|
|
9852
|
+
return raw.replace(/\/+$/, "");
|
|
9853
|
+
}
|
|
9854
|
+
if (process.env.NEXRALL_ALLOW_API_OVERRIDE !== "1") {
|
|
9855
|
+
console.error(`\u26A0\uFE0F Ignoring NEXRALL_API_BASE="${raw}": redirecting to a remote host also requires NEXRALL_ALLOW_API_OVERRIDE=1, because this variable receives your auth token and source code. Falling back to ${DEFAULT_API_BASE}.`);
|
|
9856
|
+
return DEFAULT_API_BASE;
|
|
9857
|
+
}
|
|
9858
|
+
if (!/^https:\/\//i.test(raw)) {
|
|
9859
|
+
console.error(`\u26A0\uFE0F Ignoring NEXRALL_API_BASE="${raw}": a remote override must be https:// so your token is never sent in plaintext. Falling back to ${DEFAULT_API_BASE}.`);
|
|
9860
|
+
return DEFAULT_API_BASE;
|
|
9861
|
+
}
|
|
9862
|
+
const base = raw.replace(/\/+$/, "");
|
|
9863
|
+
console.error(`\u26A0\uFE0F Nexrall API override in effect: ${base}`);
|
|
9864
|
+
return base;
|
|
9865
|
+
}
|
|
9866
|
+
exports2.API_BASE = resolveApiBase();
|
|
9822
9867
|
function chooseFinalContent(rebuilt, rawContent) {
|
|
9823
9868
|
if (!Array.isArray(rawContent))
|
|
9824
9869
|
return rebuilt;
|
|
9825
9870
|
const hasServerSideBlocks = rawContent.some((b) => b && b.type !== "text" && b.type !== "tool_use");
|
|
9826
|
-
|
|
9871
|
+
if (hasServerSideBlocks)
|
|
9872
|
+
return rawContent;
|
|
9873
|
+
if (rebuilt.length === 0 && rawContent.length > 0)
|
|
9874
|
+
return rawContent;
|
|
9875
|
+
return rebuilt;
|
|
9827
9876
|
}
|
|
9828
9877
|
function authHeaders() {
|
|
9829
9878
|
const token = (0, index_1.getToken)();
|
|
@@ -9838,6 +9887,10 @@ var require_client = __commonJS({
|
|
|
9838
9887
|
var MAX_RETRIES = 5;
|
|
9839
9888
|
var RETRY_BASE_MS = 1e3;
|
|
9840
9889
|
var RETRY_MAX_MS = 3e4;
|
|
9890
|
+
var MAX_TOTAL_RETRY_MS = (() => {
|
|
9891
|
+
const raw = Number(process.env.NEXRALL_MAX_RETRY_MS);
|
|
9892
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 5 * 6e4;
|
|
9893
|
+
})();
|
|
9841
9894
|
function backoffMs(attempt) {
|
|
9842
9895
|
const exp = Math.min(RETRY_MAX_MS, RETRY_BASE_MS * Math.pow(2, attempt));
|
|
9843
9896
|
return Math.round(exp / 2 + Math.random() * (exp / 2));
|
|
@@ -9847,30 +9900,62 @@ var require_client = __commonJS({
|
|
|
9847
9900
|
}
|
|
9848
9901
|
var MAX_TOTAL_ATTEMPTS = (MAX_RETRIES + 1) * (MAX_RETRIES + 1);
|
|
9849
9902
|
async function streamChat(messages, options, onEvent) {
|
|
9850
|
-
const { model, env: env2, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents } = options;
|
|
9903
|
+
const { model, env: env2, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender } = options;
|
|
9904
|
+
let turnId = (0, crypto_1.randomUUID)();
|
|
9851
9905
|
const controller = new AbortController();
|
|
9852
9906
|
if (abortSignal?.aborted)
|
|
9853
9907
|
throw Object.assign(new Error("Aborted"), { name: "AbortError" });
|
|
9854
9908
|
let abortPoll;
|
|
9909
|
+
let cancelSent = false;
|
|
9855
9910
|
if (abortSignal) {
|
|
9856
9911
|
abortPoll = setInterval(() => {
|
|
9857
|
-
if (abortSignal.aborted)
|
|
9858
|
-
|
|
9912
|
+
if (!abortSignal.aborted)
|
|
9913
|
+
return;
|
|
9914
|
+
if (cancelSent)
|
|
9915
|
+
return;
|
|
9916
|
+
cancelSent = true;
|
|
9917
|
+
void cancelTurn(turnId);
|
|
9918
|
+
controller.abort();
|
|
9859
9919
|
}, 50);
|
|
9860
9920
|
}
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9921
|
+
let serverResumable = false;
|
|
9922
|
+
let lastEventId = 0;
|
|
9923
|
+
let resuming = false;
|
|
9924
|
+
let carryText = [];
|
|
9925
|
+
let carryToolUse = [];
|
|
9926
|
+
let emittedAnythingAcrossAttempts = false;
|
|
9927
|
+
let emittedCharsAcrossAttempts = 0;
|
|
9928
|
+
const buildFetchArgs = () => {
|
|
9929
|
+
if (resuming) {
|
|
9930
|
+
return [
|
|
9931
|
+
`${exports2.API_BASE}/api/code/chat/resume?turnId=${encodeURIComponent(turnId)}`,
|
|
9932
|
+
{
|
|
9933
|
+
method: "GET",
|
|
9934
|
+
headers: { ...authHeaders(), "Last-Event-ID": String(lastEventId) },
|
|
9935
|
+
signal: controller.signal
|
|
9936
|
+
}
|
|
9937
|
+
];
|
|
9868
9938
|
}
|
|
9869
|
-
|
|
9939
|
+
return [
|
|
9940
|
+
`${exports2.API_BASE}/api/code/chat`,
|
|
9941
|
+
{
|
|
9942
|
+
method: "POST",
|
|
9943
|
+
headers: authHeaders(),
|
|
9944
|
+
body: JSON.stringify({ messages, model, env: env2, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId }),
|
|
9945
|
+
signal: controller.signal
|
|
9946
|
+
}
|
|
9947
|
+
];
|
|
9948
|
+
};
|
|
9870
9949
|
const isRetryableStreamMsg = (m2) => /overloaded|rate.?limit|temporarily|unavailable|try again|internal server error/i.test(String(m2 ?? ""));
|
|
9871
9950
|
let didRetry = false;
|
|
9872
9951
|
let totalAttemptsMade = 0;
|
|
9952
|
+
let retryDeadline = 0;
|
|
9953
|
+
const retryBudgetLeft = () => retryDeadline === 0 ? MAX_TOTAL_RETRY_MS : retryDeadline - Date.now();
|
|
9954
|
+
const canRetry = () => retryBudgetLeft() > RETRY_BASE_MS;
|
|
9955
|
+
const sleepWithinBudget = (ms) => sleep(Math.max(0, Math.min(ms, retryBudgetLeft())));
|
|
9873
9956
|
const reportRetry = (reason) => {
|
|
9957
|
+
if (retryDeadline === 0)
|
|
9958
|
+
retryDeadline = Date.now() + MAX_TOTAL_RETRY_MS;
|
|
9874
9959
|
totalAttemptsMade++;
|
|
9875
9960
|
didRetry = true;
|
|
9876
9961
|
onEvent({ type: "retry", attempt: totalAttemptsMade, maxAttempts: MAX_TOTAL_ATTEMPTS, reason });
|
|
@@ -9884,36 +9969,114 @@ var require_client = __commonJS({
|
|
|
9884
9969
|
async function runAttempt() {
|
|
9885
9970
|
let response;
|
|
9886
9971
|
let lastErr;
|
|
9972
|
+
let inFlightWaits = 0;
|
|
9973
|
+
let errorBodyOverride = null;
|
|
9974
|
+
let forbiddenExhausted = false;
|
|
9887
9975
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
9888
9976
|
try {
|
|
9889
|
-
response = await (0, node_fetch_1.default)(...
|
|
9890
|
-
if (response.status === 429 && attempt < MAX_RETRIES) {
|
|
9977
|
+
response = await (0, node_fetch_1.default)(...buildFetchArgs());
|
|
9978
|
+
if (response.status === 429 && attempt < MAX_RETRIES && canRetry()) {
|
|
9891
9979
|
reportRetry("Rate limited by the API \u2014 retrying");
|
|
9892
9980
|
const retryAfter = parseInt(response.headers.get("retry-after") ?? "0", 10);
|
|
9893
|
-
await
|
|
9981
|
+
await sleepWithinBudget(retryAfter > 0 ? retryAfter * 1e3 : backoffMs(attempt));
|
|
9894
9982
|
continue;
|
|
9895
9983
|
}
|
|
9896
|
-
if (response.status >= 500 && response.status < 600 && attempt < MAX_RETRIES) {
|
|
9984
|
+
if (response.status >= 500 && response.status < 600 && attempt < MAX_RETRIES && canRetry()) {
|
|
9897
9985
|
reportRetry(`Server error (${response.status}) \u2014 retrying`);
|
|
9898
|
-
await
|
|
9986
|
+
await sleepWithinBudget(backoffMs(attempt));
|
|
9899
9987
|
continue;
|
|
9900
9988
|
}
|
|
9989
|
+
const FORBIDDEN_RETRY_LIMIT = 2;
|
|
9990
|
+
if (response.status === 403) {
|
|
9991
|
+
const bodyText = await response.text().catch(() => "");
|
|
9992
|
+
let isUpstreamForbidden = false;
|
|
9993
|
+
try {
|
|
9994
|
+
const parsed = JSON.parse(bodyText);
|
|
9995
|
+
isUpstreamForbidden = parsed?.error?.type === "forbidden";
|
|
9996
|
+
} catch {
|
|
9997
|
+
}
|
|
9998
|
+
if (isUpstreamForbidden) {
|
|
9999
|
+
if (attempt < FORBIDDEN_RETRY_LIMIT && canRetry()) {
|
|
10000
|
+
reportRetry("Upstream rejected the request \u2014 retrying");
|
|
10001
|
+
await sleepWithinBudget(backoffMs(attempt));
|
|
10002
|
+
continue;
|
|
10003
|
+
}
|
|
10004
|
+
forbiddenExhausted = true;
|
|
10005
|
+
errorBodyOverride = bodyText;
|
|
10006
|
+
break;
|
|
10007
|
+
}
|
|
10008
|
+
errorBodyOverride = bodyText;
|
|
10009
|
+
break;
|
|
10010
|
+
}
|
|
10011
|
+
if (response.status === 410 && resuming) {
|
|
10012
|
+
await response.text().catch(() => "");
|
|
10013
|
+
await cancelTurn(turnId);
|
|
10014
|
+
turnId = (0, crypto_1.randomUUID)();
|
|
10015
|
+
resuming = false;
|
|
10016
|
+
serverResumable = false;
|
|
10017
|
+
lastEventId = 0;
|
|
10018
|
+
carryText = [];
|
|
10019
|
+
carryToolUse = [];
|
|
10020
|
+
const hadRendered = emittedAnythingAcrossAttempts;
|
|
10021
|
+
const renderedChars = emittedCharsAcrossAttempts;
|
|
10022
|
+
emittedAnythingAcrossAttempts = false;
|
|
10023
|
+
emittedCharsAcrossAttempts = 0;
|
|
10024
|
+
if (hadRendered && !allowRestartAfterRender) {
|
|
10025
|
+
throw new Error("Connection lost and this turn could no longer be resumed. Send your message again.");
|
|
10026
|
+
}
|
|
10027
|
+
if (hadRendered) {
|
|
10028
|
+
onEvent({
|
|
10029
|
+
type: "stream_restart",
|
|
10030
|
+
reason: "Connection lost too long to resume",
|
|
10031
|
+
discardedChars: renderedChars
|
|
10032
|
+
});
|
|
10033
|
+
}
|
|
10034
|
+
if (attempt < MAX_RETRIES && canRetry()) {
|
|
10035
|
+
reportRetry("Could not resume \u2014 restarting this turn");
|
|
10036
|
+
await sleepWithinBudget(backoffMs(attempt));
|
|
10037
|
+
continue;
|
|
10038
|
+
}
|
|
10039
|
+
errorBodyOverride = JSON.stringify({ error: "Could not resume this turn \u2014 send your message again." });
|
|
10040
|
+
break;
|
|
10041
|
+
}
|
|
10042
|
+
if (response.status === 409) {
|
|
10043
|
+
const body = await response.text();
|
|
10044
|
+
let inFlight = false;
|
|
10045
|
+
try {
|
|
10046
|
+
inFlight = JSON.parse(body)?.turnInFlight === true;
|
|
10047
|
+
} catch {
|
|
10048
|
+
}
|
|
10049
|
+
if (inFlight && canRetry()) {
|
|
10050
|
+
inFlightWaits++;
|
|
10051
|
+
reportRetry("This turn is already being processed \u2014 waiting for it");
|
|
10052
|
+
const retryAfter = parseInt(response.headers.get("retry-after") ?? "0", 10);
|
|
10053
|
+
await sleepWithinBudget(Math.max(retryAfter * 1e3, backoffMs(inFlightWaits)));
|
|
10054
|
+
attempt--;
|
|
10055
|
+
continue;
|
|
10056
|
+
}
|
|
10057
|
+
errorBodyOverride = body;
|
|
10058
|
+
break;
|
|
10059
|
+
}
|
|
9901
10060
|
break;
|
|
9902
10061
|
} catch (err) {
|
|
9903
10062
|
if (err.name === "AbortError")
|
|
9904
10063
|
throw err;
|
|
9905
10064
|
lastErr = err;
|
|
9906
|
-
if (attempt < MAX_RETRIES) {
|
|
10065
|
+
if (attempt < MAX_RETRIES && canRetry()) {
|
|
9907
10066
|
reportRetry("Connection lost \u2014 attempting to reconnect");
|
|
9908
|
-
await
|
|
10067
|
+
await sleepWithinBudget(backoffMs(attempt));
|
|
9909
10068
|
continue;
|
|
9910
10069
|
}
|
|
10070
|
+
if (!canRetry() && lastErr && typeof lastErr === "object") {
|
|
10071
|
+
Object.assign(lastErr, { retryable: true });
|
|
10072
|
+
}
|
|
10073
|
+
break;
|
|
9911
10074
|
}
|
|
9912
10075
|
}
|
|
9913
10076
|
if (!response)
|
|
9914
10077
|
throw lastErr ?? new Error("Request failed after max retries");
|
|
9915
10078
|
if (!response.ok) {
|
|
9916
|
-
const errText = await response.text();
|
|
10079
|
+
const errText = errorBodyOverride ?? await response.text();
|
|
9917
10080
|
let errMsg = `API error ${response.status}`;
|
|
9918
10081
|
let balance;
|
|
9919
10082
|
try {
|
|
@@ -9925,39 +10088,61 @@ var require_client = __commonJS({
|
|
|
9925
10088
|
} catch {
|
|
9926
10089
|
errMsg = errText || errMsg;
|
|
9927
10090
|
}
|
|
9928
|
-
throw Object.assign(new Error(errMsg), { status: response.status, balance });
|
|
10091
|
+
throw Object.assign(new Error(errMsg), { status: response.status, balance, ...forbiddenExhausted ? { retryable: true } : {} });
|
|
9929
10092
|
}
|
|
9930
10093
|
if (!response.body) {
|
|
9931
10094
|
throw new Error("Response body is null");
|
|
9932
10095
|
}
|
|
9933
|
-
const textParts = [];
|
|
9934
|
-
const toolUseBlocks = [];
|
|
10096
|
+
const textParts = resuming ? [...carryText] : [];
|
|
10097
|
+
const toolUseBlocks = resuming ? [...carryToolUse] : [];
|
|
9935
10098
|
let completedMessage = null;
|
|
10099
|
+
carryText = textParts;
|
|
10100
|
+
carryToolUse = toolUseBlocks;
|
|
9936
10101
|
let emittedToCaller = false;
|
|
10102
|
+
let emittedChars = 0;
|
|
10103
|
+
const tagTransient = (err) => {
|
|
10104
|
+
if (!emittedToCaller)
|
|
10105
|
+
return Object.assign(err, { retryable: true });
|
|
10106
|
+
if (allowRestartAfterRender) {
|
|
10107
|
+
return Object.assign(err, { retryable: true, needsRestart: true, discardedChars: emittedChars });
|
|
10108
|
+
}
|
|
10109
|
+
return err;
|
|
10110
|
+
};
|
|
10111
|
+
const haveCompleteMessage = () => completedMessage !== null;
|
|
9937
10112
|
const partialInputs = {};
|
|
9938
10113
|
await new Promise((resolve3, reject) => {
|
|
9939
10114
|
const stream = response.body;
|
|
9940
|
-
const HEARTBEAT_TIMEOUT_MS =
|
|
9941
|
-
const FIRST_EVENT_TIMEOUT_MS = 3e5;
|
|
9942
|
-
const PROGRESS_TIMEOUT_MS = 15e4;
|
|
10115
|
+
const HEARTBEAT_TIMEOUT_MS = 9e4;
|
|
10116
|
+
const FIRST_EVENT_TIMEOUT_MS = Number(process.env.NEXRALL_FIRST_EVENT_TIMEOUT_MS) || 3e5;
|
|
10117
|
+
const PROGRESS_TIMEOUT_MS = Number(process.env.NEXRALL_PROGRESS_TIMEOUT_MS) || 15e4;
|
|
9943
10118
|
let sawModelEvent = false;
|
|
9944
10119
|
let lastDataAt = Date.now();
|
|
9945
10120
|
let lastProgressAt = Date.now();
|
|
9946
10121
|
const heartbeatWatchdog = setInterval(() => {
|
|
9947
10122
|
const now = Date.now();
|
|
10123
|
+
if (haveCompleteMessage() && now - lastDataAt > HEARTBEAT_TIMEOUT_MS) {
|
|
10124
|
+
clearInterval(heartbeatWatchdog);
|
|
10125
|
+
stream.destroy?.();
|
|
10126
|
+
resolve3();
|
|
10127
|
+
return;
|
|
10128
|
+
}
|
|
9948
10129
|
if (now - lastDataAt > HEARTBEAT_TIMEOUT_MS) {
|
|
9949
10130
|
clearInterval(heartbeatWatchdog);
|
|
9950
10131
|
stream.destroy?.();
|
|
9951
|
-
const
|
|
9952
|
-
|
|
10132
|
+
const recoverable = !emittedToCaller || !!allowRestartAfterRender;
|
|
10133
|
+
const secs = Math.round(HEARTBEAT_TIMEOUT_MS / 1e3);
|
|
10134
|
+
reject(tagTransient(new Error(recoverable ? `Connection lost \u2014 no data received for ${secs} s. Reconnecting\u2026` : `Connection lost \u2014 no data received for ${secs} s. Retry your message.`)));
|
|
9953
10135
|
return;
|
|
9954
10136
|
}
|
|
9955
10137
|
const stallLimitMs = sawModelEvent ? PROGRESS_TIMEOUT_MS : FIRST_EVENT_TIMEOUT_MS;
|
|
9956
10138
|
if (now - lastProgressAt > stallLimitMs) {
|
|
9957
10139
|
clearInterval(heartbeatWatchdog);
|
|
9958
10140
|
stream.destroy?.();
|
|
9959
|
-
|
|
9960
|
-
|
|
10141
|
+
if (haveCompleteMessage()) {
|
|
10142
|
+
resolve3();
|
|
10143
|
+
return;
|
|
10144
|
+
}
|
|
10145
|
+
reject(tagTransient(new Error(sawModelEvent ? `The model stopped responding mid-stream (no output for ${Math.round(stallLimitMs / 1e3)} s).` : `The model did not start responding within ${Math.round(stallLimitMs / 1e3)} s (large context can take a while to process).`)));
|
|
9961
10146
|
return;
|
|
9962
10147
|
}
|
|
9963
10148
|
}, 5e3);
|
|
@@ -9967,6 +10152,12 @@ var require_client = __commonJS({
|
|
|
9967
10152
|
return;
|
|
9968
10153
|
}
|
|
9969
10154
|
{
|
|
10155
|
+
const frameId = event.id;
|
|
10156
|
+
if (frameId) {
|
|
10157
|
+
const n = Number(frameId);
|
|
10158
|
+
if (Number.isFinite(n) && n > lastEventId)
|
|
10159
|
+
lastEventId = n;
|
|
10160
|
+
}
|
|
9970
10161
|
const raw = event.data;
|
|
9971
10162
|
if (!raw || raw === "[DONE]") {
|
|
9972
10163
|
resolve3();
|
|
@@ -9990,6 +10181,9 @@ var require_client = __commonJS({
|
|
|
9990
10181
|
const text = typeof evt.text === "string" ? evt.text : "";
|
|
9991
10182
|
textParts.push(text);
|
|
9992
10183
|
emittedToCaller = true;
|
|
10184
|
+
emittedChars += text.length;
|
|
10185
|
+
emittedAnythingAcrossAttempts = true;
|
|
10186
|
+
emittedCharsAcrossAttempts += text.length;
|
|
9993
10187
|
onEvent({ type: "text", text });
|
|
9994
10188
|
break;
|
|
9995
10189
|
}
|
|
@@ -10010,6 +10204,7 @@ var require_client = __commonJS({
|
|
|
10010
10204
|
}
|
|
10011
10205
|
toolUseBlocks.push(block);
|
|
10012
10206
|
emittedToCaller = true;
|
|
10207
|
+
emittedAnythingAcrossAttempts = true;
|
|
10013
10208
|
onEvent({ type: "tool_use", id: block.id, name: block.name, input: block.input });
|
|
10014
10209
|
break;
|
|
10015
10210
|
}
|
|
@@ -10036,6 +10231,17 @@ var require_client = __commonJS({
|
|
|
10036
10231
|
if (typeof evt.input_tokens === "number" && typeof evt.output_tokens === "number") {
|
|
10037
10232
|
onEvent({
|
|
10038
10233
|
type: "usage",
|
|
10234
|
+
// Forwarded, not dropped: the backend tags the usage of an attempt it
|
|
10235
|
+
// billed but never completed (its stream `abort` path). Without this
|
|
10236
|
+
// the flag dies here and every consumer that sums usage silently folds
|
|
10237
|
+
// a discarded attempt's tokens into the successful turn's total.
|
|
10238
|
+
...evt.partial === true ? { partial: true } : {},
|
|
10239
|
+
// `replayed` means these tokens are being reported a SECOND time: the
|
|
10240
|
+
// turn completed and was billed on an earlier attempt whose `done` never
|
|
10241
|
+
// reached us, and the backend served this one from its idempotency cache
|
|
10242
|
+
// instead of re-running the model. Nothing new was charged, so a cost
|
|
10243
|
+
// display must not add them again.
|
|
10244
|
+
...evt.replayed === true ? { replayed: true } : {},
|
|
10039
10245
|
usage: {
|
|
10040
10246
|
input_tokens: evt.input_tokens,
|
|
10041
10247
|
output_tokens: evt.output_tokens,
|
|
@@ -10056,6 +10262,9 @@ var require_client = __commonJS({
|
|
|
10056
10262
|
const text = typeof evt.text === "string" ? evt.text : "";
|
|
10057
10263
|
if (text) {
|
|
10058
10264
|
emittedToCaller = true;
|
|
10265
|
+
emittedChars += text.length;
|
|
10266
|
+
emittedAnythingAcrossAttempts = true;
|
|
10267
|
+
emittedCharsAcrossAttempts += text.length;
|
|
10059
10268
|
onEvent({ type: "thinking", text });
|
|
10060
10269
|
}
|
|
10061
10270
|
break;
|
|
@@ -10069,10 +10278,17 @@ var require_client = __commonJS({
|
|
|
10069
10278
|
const text = typeof evt.text === "string" ? evt.text : "";
|
|
10070
10279
|
if (text) {
|
|
10071
10280
|
emittedToCaller = true;
|
|
10281
|
+
emittedChars += text.length;
|
|
10282
|
+
emittedAnythingAcrossAttempts = true;
|
|
10283
|
+
emittedCharsAcrossAttempts += text.length;
|
|
10072
10284
|
onEvent({ type: "thinking_delta", text });
|
|
10073
10285
|
}
|
|
10074
10286
|
break;
|
|
10075
10287
|
}
|
|
10288
|
+
case "resumable": {
|
|
10289
|
+
serverResumable = true;
|
|
10290
|
+
break;
|
|
10291
|
+
}
|
|
10076
10292
|
case "balance_status": {
|
|
10077
10293
|
const balance = typeof evt.balance === "number" ? evt.balance : 0;
|
|
10078
10294
|
const zero = !!evt.zero;
|
|
@@ -10086,10 +10302,19 @@ var require_client = __commonJS({
|
|
|
10086
10302
|
}
|
|
10087
10303
|
case "error": {
|
|
10088
10304
|
const message = typeof evt.message === "string" ? evt.message : typeof evt.error === "string" ? evt.error : "Unknown SSE error";
|
|
10089
|
-
|
|
10305
|
+
const notResumable = evt.notResumable === true;
|
|
10306
|
+
if (haveCompleteMessage()) {
|
|
10307
|
+
clearInterval(heartbeatWatchdog);
|
|
10308
|
+
onEvent({ type: "error", message });
|
|
10309
|
+
resolve3();
|
|
10310
|
+
} else if (notResumable && (!emittedToCaller || allowRestartAfterRender)) {
|
|
10311
|
+
clearInterval(heartbeatWatchdog);
|
|
10312
|
+
stream.destroy?.();
|
|
10313
|
+
reject(Object.assign(tagTransient(new Error(message)), { forceRestart: true }));
|
|
10314
|
+
} else if (isRetryableStreamMsg(message) && (!emittedToCaller || allowRestartAfterRender)) {
|
|
10090
10315
|
clearInterval(heartbeatWatchdog);
|
|
10091
10316
|
stream.destroy?.();
|
|
10092
|
-
reject(
|
|
10317
|
+
reject(tagTransient(new Error(message)));
|
|
10093
10318
|
} else {
|
|
10094
10319
|
onEvent({ type: "error", message });
|
|
10095
10320
|
reject(new Error(message));
|
|
@@ -10121,11 +10346,11 @@ var require_client = __commonJS({
|
|
|
10121
10346
|
resolve3();
|
|
10122
10347
|
return;
|
|
10123
10348
|
}
|
|
10124
|
-
if (
|
|
10125
|
-
|
|
10349
|
+
if (haveCompleteMessage()) {
|
|
10350
|
+
resolve3();
|
|
10126
10351
|
return;
|
|
10127
10352
|
}
|
|
10128
|
-
reject(err);
|
|
10353
|
+
reject(tagTransient(err));
|
|
10129
10354
|
});
|
|
10130
10355
|
});
|
|
10131
10356
|
if (completedMessage) {
|
|
@@ -10148,11 +10373,54 @@ var require_client = __commonJS({
|
|
|
10148
10373
|
} catch (err) {
|
|
10149
10374
|
if (abortSignal?.aborted || controller.signal.aborted || err.name === "AbortError")
|
|
10150
10375
|
throw err;
|
|
10151
|
-
|
|
10376
|
+
const e2 = err;
|
|
10377
|
+
if (e2.retryable && sAttempt < MAX_RETRIES && canRetry()) {
|
|
10378
|
+
if (e2.forceRestart) {
|
|
10379
|
+
await cancelTurn(turnId);
|
|
10380
|
+
turnId = (0, crypto_1.randomUUID)();
|
|
10381
|
+
resuming = false;
|
|
10382
|
+
serverResumable = false;
|
|
10383
|
+
lastEventId = 0;
|
|
10384
|
+
carryText = [];
|
|
10385
|
+
carryToolUse = [];
|
|
10386
|
+
const hadRendered = emittedAnythingAcrossAttempts;
|
|
10387
|
+
const renderedChars = emittedCharsAcrossAttempts;
|
|
10388
|
+
emittedAnythingAcrossAttempts = false;
|
|
10389
|
+
emittedCharsAcrossAttempts = 0;
|
|
10390
|
+
if (hadRendered && !allowRestartAfterRender) {
|
|
10391
|
+
throw new Error("Connection lost and this turn could no longer be resumed. Send your message again.");
|
|
10392
|
+
}
|
|
10393
|
+
if (hadRendered) {
|
|
10394
|
+
onEvent({
|
|
10395
|
+
type: "stream_restart",
|
|
10396
|
+
reason: err.message || "Could not resume \u2014 restarting this turn",
|
|
10397
|
+
discardedChars: renderedChars
|
|
10398
|
+
});
|
|
10399
|
+
}
|
|
10400
|
+
reportRetry("Could not resume \u2014 restarting this turn");
|
|
10401
|
+
await sleepWithinBudget(backoffMs(sAttempt));
|
|
10402
|
+
continue;
|
|
10403
|
+
}
|
|
10404
|
+
if (serverResumable && lastEventId > 0) {
|
|
10405
|
+
resuming = true;
|
|
10406
|
+
reportRetry(err.message || "Connection interrupted \u2014 resuming");
|
|
10407
|
+
await sleepWithinBudget(backoffMs(sAttempt));
|
|
10408
|
+
continue;
|
|
10409
|
+
}
|
|
10410
|
+
if (e2.needsRestart) {
|
|
10411
|
+
onEvent({
|
|
10412
|
+
type: "stream_restart",
|
|
10413
|
+
reason: err.message || "Connection interrupted",
|
|
10414
|
+
discardedChars: e2.discardedChars ?? 0
|
|
10415
|
+
});
|
|
10416
|
+
}
|
|
10152
10417
|
reportRetry(err.message || "Connection interrupted \u2014 reconnecting");
|
|
10153
|
-
await
|
|
10418
|
+
await sleepWithinBudget(backoffMs(sAttempt));
|
|
10154
10419
|
continue;
|
|
10155
10420
|
}
|
|
10421
|
+
if (e2.retryable && !canRetry()) {
|
|
10422
|
+
throw Object.assign(new Error(`${err.message} \u2014 gave up after ${Math.round(MAX_TOTAL_RETRY_MS / 6e4)} minutes of reconnect attempts. Check your connection and resend; completed work in this turn is preserved.`), { retryBudgetExhausted: true });
|
|
10423
|
+
}
|
|
10156
10424
|
throw err;
|
|
10157
10425
|
}
|
|
10158
10426
|
}
|
|
@@ -10162,6 +10430,16 @@ var require_client = __commonJS({
|
|
|
10162
10430
|
clearInterval(abortPoll);
|
|
10163
10431
|
}
|
|
10164
10432
|
}
|
|
10433
|
+
async function cancelTurn(turnId) {
|
|
10434
|
+
try {
|
|
10435
|
+
await (0, node_fetch_1.default)(`${exports2.API_BASE}/api/code/chat/cancel`, {
|
|
10436
|
+
method: "POST",
|
|
10437
|
+
headers: { ...authHeaders(), "Content-Type": "application/json" },
|
|
10438
|
+
body: JSON.stringify({ turnId })
|
|
10439
|
+
});
|
|
10440
|
+
} catch {
|
|
10441
|
+
}
|
|
10442
|
+
}
|
|
10165
10443
|
async function getBalance3() {
|
|
10166
10444
|
const response = await (0, node_fetch_1.default)(`${exports2.API_BASE}/api/code/balance`, {
|
|
10167
10445
|
method: "GET",
|
|
@@ -10408,126 +10686,890 @@ var require_editCompleteness = __commonJS({
|
|
|
10408
10686
|
}
|
|
10409
10687
|
});
|
|
10410
10688
|
|
|
10411
|
-
// ../core/dist/agent/
|
|
10412
|
-
var
|
|
10413
|
-
"../core/dist/agent/
|
|
10689
|
+
// ../core/dist/agent/memory.js
|
|
10690
|
+
var require_memory = __commonJS({
|
|
10691
|
+
"../core/dist/agent/memory.js"(exports2) {
|
|
10414
10692
|
"use strict";
|
|
10693
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
10694
|
+
if (k2 === void 0)
|
|
10695
|
+
k2 = k;
|
|
10696
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
10697
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
10698
|
+
desc = { enumerable: true, get: function() {
|
|
10699
|
+
return m2[k];
|
|
10700
|
+
} };
|
|
10701
|
+
}
|
|
10702
|
+
Object.defineProperty(o, k2, desc);
|
|
10703
|
+
} : function(o, m2, k, k2) {
|
|
10704
|
+
if (k2 === void 0)
|
|
10705
|
+
k2 = k;
|
|
10706
|
+
o[k2] = m2[k];
|
|
10707
|
+
});
|
|
10708
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
10709
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
10710
|
+
} : function(o, v) {
|
|
10711
|
+
o["default"] = v;
|
|
10712
|
+
});
|
|
10713
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
10714
|
+
var ownKeys = function(o) {
|
|
10715
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
10716
|
+
var ar = [];
|
|
10717
|
+
for (var k in o2)
|
|
10718
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
10719
|
+
ar[ar.length] = k;
|
|
10720
|
+
return ar;
|
|
10721
|
+
};
|
|
10722
|
+
return ownKeys(o);
|
|
10723
|
+
};
|
|
10724
|
+
return function(mod) {
|
|
10725
|
+
if (mod && mod.__esModule)
|
|
10726
|
+
return mod;
|
|
10727
|
+
var result = {};
|
|
10728
|
+
if (mod != null) {
|
|
10729
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
10730
|
+
if (k[i2] !== "default")
|
|
10731
|
+
__createBinding(result, mod, k[i2]);
|
|
10732
|
+
}
|
|
10733
|
+
__setModuleDefault(result, mod);
|
|
10734
|
+
return result;
|
|
10735
|
+
};
|
|
10736
|
+
}();
|
|
10415
10737
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
10416
|
-
exports2.
|
|
10417
|
-
exports2.
|
|
10418
|
-
exports2.
|
|
10419
|
-
exports2.
|
|
10420
|
-
exports2.
|
|
10421
|
-
exports2.
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
var
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
/\bfunc\s+Test\w+/g,
|
|
10457
|
-
// go
|
|
10458
|
-
/@Test\b/g,
|
|
10459
|
-
// junit
|
|
10460
|
-
/\bfn\s+\w*test\w*\s*\(/gi
|
|
10461
|
-
// rust (best-effort)
|
|
10462
|
-
];
|
|
10463
|
-
var SKIP_RES = [
|
|
10464
|
-
/\b(?:it|test|describe|context|suite)\s*\.\s*(?:skip|only)\b/g,
|
|
10465
|
-
/\bx(?:it|describe|test|context)\s*\(/g,
|
|
10466
|
-
// xit / xdescribe
|
|
10467
|
-
/\bf(?:it|describe)\s*\(/g,
|
|
10468
|
-
// fit / fdescribe (focus)
|
|
10469
|
-
/@pytest\.mark\.skip\b|@pytest\.mark\.skipif\b|@unittest\.skip\b|@skip\b/g,
|
|
10470
|
-
/\bpytest\.skip\s*\(/g,
|
|
10471
|
-
/\bt\.Skip\s*\(|\bt\.SkipNow\s*\(/g,
|
|
10472
|
-
// go
|
|
10473
|
-
/@Disabled\b|@Ignore\b/g,
|
|
10474
|
-
// junit / kotlin
|
|
10475
|
-
/\.only\s*\(/g
|
|
10476
|
-
// test.only leaks CI coverage
|
|
10477
|
-
];
|
|
10478
|
-
var TAUTOLOGY_RES = [
|
|
10479
|
-
/\bassert\s+True\b|\bassert\s+1\b|\bassert\s+not\s+False\b/g,
|
|
10480
|
-
// python
|
|
10481
|
-
/\bassert\s*\(\s*true\s*\)|\bassert\.ok\s*\(\s*true\s*\)/gi,
|
|
10482
|
-
// node
|
|
10483
|
-
/\bexpect\s*\(\s*true\s*\)\s*\.\s*to(?:Be|Equal|BeTruthy)\s*\(\s*true\s*\)?/gi,
|
|
10484
|
-
// jest
|
|
10485
|
-
/\bexpect\s*\(\s*(\w+)\s*\)\s*\.\s*toBe\s*\(\s*\1\s*\)/g,
|
|
10486
|
-
// expect(x).toBe(x)
|
|
10487
|
-
/\bassert_eq!\s*\(\s*true\s*,\s*true\s*\)/g,
|
|
10488
|
-
// rust
|
|
10489
|
-
/\bassert!\s*\(\s*true\s*\)/g
|
|
10490
|
-
];
|
|
10491
|
-
function countMatches(res, text) {
|
|
10492
|
-
let n = 0;
|
|
10493
|
-
for (const re of res) {
|
|
10494
|
-
re.lastIndex = 0;
|
|
10495
|
-
const m2 = text.match(re);
|
|
10496
|
-
if (m2)
|
|
10497
|
-
n += m2.length;
|
|
10738
|
+
exports2.MEMORY_HARD_CAP_BYTES = exports2.MEMORY_COMPACT_TRIGGER_BYTES = exports2.MEMORY_MAX_BYTES = exports2.MEMORY_ENTRY_MAX_CHARS = void 0;
|
|
10739
|
+
exports2.memoryFilePath = memoryFilePath;
|
|
10740
|
+
exports2.writeMemory = writeMemory;
|
|
10741
|
+
exports2.readMemory = readMemory2;
|
|
10742
|
+
exports2.readAllMemory = readAllMemory2;
|
|
10743
|
+
exports2.clearMemory = clearMemory2;
|
|
10744
|
+
exports2.memoryStats = memoryStats2;
|
|
10745
|
+
exports2.compactMemoryIfNeeded = compactMemoryIfNeeded2;
|
|
10746
|
+
var fs6 = __importStar(require("fs"));
|
|
10747
|
+
var os5 = __importStar(require("os"));
|
|
10748
|
+
var path5 = __importStar(require("path"));
|
|
10749
|
+
var crypto2 = __importStar(require("crypto"));
|
|
10750
|
+
var MEMORY_ROOT = process.env.NEXRALL_MEMORY_DIR || path5.join(os5.homedir(), ".nexrall", "memory");
|
|
10751
|
+
var GLOBAL_FILE = path5.join(MEMORY_ROOT, "global.md");
|
|
10752
|
+
function memoryFilePath(scope, workDir) {
|
|
10753
|
+
if (scope === "global" || !workDir)
|
|
10754
|
+
return GLOBAL_FILE;
|
|
10755
|
+
const key = crypto2.createHash("sha1").update(path5.resolve(workDir)).digest("hex").slice(0, 16);
|
|
10756
|
+
return path5.join(MEMORY_ROOT, `project-${key}.md`);
|
|
10757
|
+
}
|
|
10758
|
+
exports2.MEMORY_ENTRY_MAX_CHARS = 400;
|
|
10759
|
+
exports2.MEMORY_MAX_BYTES = 12e3;
|
|
10760
|
+
exports2.MEMORY_COMPACT_TRIGGER_BYTES = 16e3;
|
|
10761
|
+
exports2.MEMORY_HARD_CAP_BYTES = 4 * exports2.MEMORY_COMPACT_TRIGGER_BYTES;
|
|
10762
|
+
var FINGERPRINT_LEN = 60;
|
|
10763
|
+
var _locks = /* @__PURE__ */ new Map();
|
|
10764
|
+
async function withLock(key, fn) {
|
|
10765
|
+
const prev = _locks.get(key) ?? Promise.resolve();
|
|
10766
|
+
let release2;
|
|
10767
|
+
const next = new Promise((res) => {
|
|
10768
|
+
release2 = res;
|
|
10769
|
+
});
|
|
10770
|
+
_locks.set(key, prev.then(() => next));
|
|
10771
|
+
try {
|
|
10772
|
+
await prev;
|
|
10773
|
+
return await fn();
|
|
10774
|
+
} finally {
|
|
10775
|
+
release2();
|
|
10776
|
+
if (_locks.get(key) === next)
|
|
10777
|
+
_locks.delete(key);
|
|
10498
10778
|
}
|
|
10499
|
-
return n;
|
|
10500
10779
|
}
|
|
10501
|
-
function
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
const isComment = /^(?:\/\/|#|\/\*|\*)/.test(line);
|
|
10507
|
-
if (!isComment)
|
|
10508
|
-
continue;
|
|
10509
|
-
const body = line.replace(/^(?:\/\/+|#+|\/\*+|\*+)\s?/, "");
|
|
10510
|
-
const looksLikeTest = /\b(expect|assert|it\(|test\(|def test_|func Test|EXPECT_|ASSERT_)\b/.test(body);
|
|
10511
|
-
if (looksLikeTest && oldLines.has(body))
|
|
10512
|
-
n += 1;
|
|
10780
|
+
function readMemoryFile(file) {
|
|
10781
|
+
try {
|
|
10782
|
+
return fs6.readFileSync(file, "utf-8");
|
|
10783
|
+
} catch {
|
|
10784
|
+
return "";
|
|
10513
10785
|
}
|
|
10514
|
-
return n;
|
|
10515
10786
|
}
|
|
10516
|
-
function
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10787
|
+
function writeMemoryFile(file, content) {
|
|
10788
|
+
fs6.mkdirSync(path5.dirname(file), { recursive: true });
|
|
10789
|
+
fs6.writeFileSync(file, content, "utf-8");
|
|
10790
|
+
}
|
|
10791
|
+
function evictOldest(content, maxBytes) {
|
|
10792
|
+
if (Buffer.byteLength(content, "utf-8") <= maxBytes)
|
|
10793
|
+
return content;
|
|
10794
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
10795
|
+
while (lines.length > 1 && Buffer.byteLength(lines.join("\n"), "utf-8") > maxBytes)
|
|
10796
|
+
lines.shift();
|
|
10797
|
+
return "\n" + lines.join("\n");
|
|
10798
|
+
}
|
|
10799
|
+
async function writeMemory(content, scope, workDir) {
|
|
10800
|
+
let trimmed = content.trim();
|
|
10801
|
+
if (!trimmed)
|
|
10802
|
+
return { ok: false, already: false, scope, file: "" };
|
|
10803
|
+
if (trimmed.length > exports2.MEMORY_ENTRY_MAX_CHARS)
|
|
10804
|
+
trimmed = trimmed.slice(0, exports2.MEMORY_ENTRY_MAX_CHARS - 1).trimEnd() + "\u2026";
|
|
10805
|
+
const file = memoryFilePath(scope, workDir);
|
|
10806
|
+
return withLock(file, async () => {
|
|
10807
|
+
const existing = readMemoryFile(file);
|
|
10808
|
+
const fingerprint = trimmed.toLowerCase().slice(0, FINGERPRINT_LEN);
|
|
10809
|
+
if (existing.toLowerCase().includes(fingerprint)) {
|
|
10810
|
+
return { ok: true, already: true, scope, file };
|
|
10811
|
+
}
|
|
10812
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10813
|
+
let next = existing + `
|
|
10814
|
+
- [${today}] ${trimmed}`;
|
|
10815
|
+
if (Buffer.byteLength(next, "utf-8") > exports2.MEMORY_HARD_CAP_BYTES) {
|
|
10816
|
+
next = evictOldest(next, exports2.MEMORY_MAX_BYTES);
|
|
10817
|
+
}
|
|
10818
|
+
writeMemoryFile(file, next);
|
|
10819
|
+
return { ok: true, already: false, scope, file };
|
|
10820
|
+
});
|
|
10821
|
+
}
|
|
10822
|
+
function readMemory2(scope, workDir) {
|
|
10823
|
+
const file = memoryFilePath(scope, workDir);
|
|
10824
|
+
return readMemoryFile(file).trim();
|
|
10825
|
+
}
|
|
10826
|
+
function readAllMemory2(workDir) {
|
|
10827
|
+
const parts = [];
|
|
10828
|
+
const globalMem = readMemory2("global");
|
|
10829
|
+
if (globalMem)
|
|
10830
|
+
parts.push(`[Global memories \u2014 apply to every project]
|
|
10831
|
+
${globalMem}`);
|
|
10832
|
+
if (workDir) {
|
|
10833
|
+
const projMem = readMemory2("project", workDir);
|
|
10834
|
+
if (projMem)
|
|
10835
|
+
parts.push(`[Project memories \u2014 ${path5.basename(path5.resolve(workDir))}]
|
|
10836
|
+
${projMem}`);
|
|
10837
|
+
}
|
|
10838
|
+
return parts.join("\n\n");
|
|
10839
|
+
}
|
|
10840
|
+
function clearMemory2(scope, workDir) {
|
|
10841
|
+
const file = memoryFilePath(scope, workDir);
|
|
10842
|
+
try {
|
|
10843
|
+
fs6.rmSync(file, { force: true });
|
|
10844
|
+
} catch {
|
|
10528
10845
|
}
|
|
10529
|
-
|
|
10530
|
-
|
|
10846
|
+
}
|
|
10847
|
+
function memoryStats2(scope, workDir) {
|
|
10848
|
+
const file = memoryFilePath(scope, workDir);
|
|
10849
|
+
const content = readMemoryFile(file);
|
|
10850
|
+
const entries = content.split("\n").filter((l) => /^-\s\[\d{4}-\d{2}-\d{2}\]/.test(l)).length;
|
|
10851
|
+
return { file, bytes: Buffer.byteLength(content, "utf-8"), entries };
|
|
10852
|
+
}
|
|
10853
|
+
async function compactMemoryIfNeeded2(scope, workDir, summarize) {
|
|
10854
|
+
const file = memoryFilePath(scope, workDir);
|
|
10855
|
+
return withLock(file, async () => {
|
|
10856
|
+
const content = readMemoryFile(file);
|
|
10857
|
+
if (Buffer.byteLength(content, "utf-8") <= exports2.MEMORY_COMPACT_TRIGGER_BYTES)
|
|
10858
|
+
return false;
|
|
10859
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
10860
|
+
if (lines.length < 8)
|
|
10861
|
+
return false;
|
|
10862
|
+
const cut = Math.floor(lines.length / 2);
|
|
10863
|
+
const older = lines.slice(0, cut);
|
|
10864
|
+
const recent = lines.slice(cut);
|
|
10865
|
+
const prompt2 = `Consolidate these persistent-memory bullet entries into a shorter set of bullets, merging duplicates/near-duplicates and dropping anything clearly stale or superseded by a later entry. Keep each resulting bullet under ${exports2.MEMORY_ENTRY_MAX_CHARS} characters, one fact per line, prefixed "- [YYYY-MM-DD] " using the LATEST date among the entries it draws from. Output ONLY the bullet lines, nothing else.
|
|
10866
|
+
|
|
10867
|
+
${older.join("\n")}`;
|
|
10868
|
+
let summarized;
|
|
10869
|
+
try {
|
|
10870
|
+
summarized = (await summarize(prompt2)).trim();
|
|
10871
|
+
} catch {
|
|
10872
|
+
return false;
|
|
10873
|
+
}
|
|
10874
|
+
if (!summarized)
|
|
10875
|
+
return false;
|
|
10876
|
+
const next = summarized + "\n" + recent.join("\n");
|
|
10877
|
+
writeMemoryFile(file, Buffer.byteLength(next, "utf-8") > exports2.MEMORY_MAX_BYTES ? evictOldest(next, exports2.MEMORY_MAX_BYTES) : next);
|
|
10878
|
+
return true;
|
|
10879
|
+
});
|
|
10880
|
+
}
|
|
10881
|
+
}
|
|
10882
|
+
});
|
|
10883
|
+
|
|
10884
|
+
// ../core/dist/plugins/index.js
|
|
10885
|
+
var require_plugins = __commonJS({
|
|
10886
|
+
"../core/dist/plugins/index.js"(exports2) {
|
|
10887
|
+
"use strict";
|
|
10888
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
10889
|
+
if (k2 === void 0)
|
|
10890
|
+
k2 = k;
|
|
10891
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
10892
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
10893
|
+
desc = { enumerable: true, get: function() {
|
|
10894
|
+
return m2[k];
|
|
10895
|
+
} };
|
|
10896
|
+
}
|
|
10897
|
+
Object.defineProperty(o, k2, desc);
|
|
10898
|
+
} : function(o, m2, k, k2) {
|
|
10899
|
+
if (k2 === void 0)
|
|
10900
|
+
k2 = k;
|
|
10901
|
+
o[k2] = m2[k];
|
|
10902
|
+
});
|
|
10903
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
10904
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
10905
|
+
} : function(o, v) {
|
|
10906
|
+
o["default"] = v;
|
|
10907
|
+
});
|
|
10908
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
10909
|
+
var ownKeys = function(o) {
|
|
10910
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
10911
|
+
var ar = [];
|
|
10912
|
+
for (var k in o2)
|
|
10913
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
10914
|
+
ar[ar.length] = k;
|
|
10915
|
+
return ar;
|
|
10916
|
+
};
|
|
10917
|
+
return ownKeys(o);
|
|
10918
|
+
};
|
|
10919
|
+
return function(mod) {
|
|
10920
|
+
if (mod && mod.__esModule)
|
|
10921
|
+
return mod;
|
|
10922
|
+
var result = {};
|
|
10923
|
+
if (mod != null) {
|
|
10924
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
10925
|
+
if (k[i2] !== "default")
|
|
10926
|
+
__createBinding(result, mod, k[i2]);
|
|
10927
|
+
}
|
|
10928
|
+
__setModuleDefault(result, mod);
|
|
10929
|
+
return result;
|
|
10930
|
+
};
|
|
10931
|
+
}();
|
|
10932
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
10933
|
+
exports2.loadPlugins = loadPlugins3;
|
|
10934
|
+
exports2.pluginAssetDirs = pluginAssetDirs;
|
|
10935
|
+
exports2.pluginHooks = pluginHooks;
|
|
10936
|
+
exports2.pluginMcpServers = pluginMcpServers;
|
|
10937
|
+
var fs6 = __importStar(require("fs"));
|
|
10938
|
+
var path5 = __importStar(require("path"));
|
|
10939
|
+
var os5 = __importStar(require("os"));
|
|
10940
|
+
function readMeta(dir) {
|
|
10941
|
+
try {
|
|
10942
|
+
const raw = fs6.readFileSync(path5.join(dir, "plugin.json"), "utf-8");
|
|
10943
|
+
const j = JSON.parse(raw);
|
|
10944
|
+
return {
|
|
10945
|
+
name: typeof j.name === "string" ? j.name : void 0,
|
|
10946
|
+
version: typeof j.version === "string" ? j.version : void 0,
|
|
10947
|
+
description: typeof j.description === "string" ? j.description : void 0
|
|
10948
|
+
};
|
|
10949
|
+
} catch {
|
|
10950
|
+
return {};
|
|
10951
|
+
}
|
|
10952
|
+
}
|
|
10953
|
+
function scanRoot(root, scope, into) {
|
|
10954
|
+
let entries;
|
|
10955
|
+
try {
|
|
10956
|
+
entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
10957
|
+
} catch {
|
|
10958
|
+
return;
|
|
10959
|
+
}
|
|
10960
|
+
for (const entry of entries) {
|
|
10961
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
10962
|
+
continue;
|
|
10963
|
+
const dir = path5.join(root, entry.name);
|
|
10964
|
+
try {
|
|
10965
|
+
if (!fs6.statSync(dir).isDirectory())
|
|
10966
|
+
continue;
|
|
10967
|
+
} catch {
|
|
10968
|
+
continue;
|
|
10969
|
+
}
|
|
10970
|
+
const meta = readMeta(dir);
|
|
10971
|
+
const name = meta.name || entry.name;
|
|
10972
|
+
if (into.has(name))
|
|
10973
|
+
continue;
|
|
10974
|
+
into.set(name, { name, version: meta.version, description: meta.description, dir, scope });
|
|
10975
|
+
}
|
|
10976
|
+
}
|
|
10977
|
+
function loadPlugins3(workDir) {
|
|
10978
|
+
const out = /* @__PURE__ */ new Map();
|
|
10979
|
+
scanRoot(path5.join(workDir, ".nexrall", "plugins"), "project", out);
|
|
10980
|
+
scanRoot(path5.join(os5.homedir(), ".nexrall", "plugins"), "global", out);
|
|
10981
|
+
return [...out.values()];
|
|
10982
|
+
}
|
|
10983
|
+
function pluginAssetDirs(workDir, kind) {
|
|
10984
|
+
return loadPlugins3(workDir).map((p) => path5.join(p.dir, kind)).filter((d) => {
|
|
10985
|
+
try {
|
|
10986
|
+
return fs6.statSync(d).isDirectory();
|
|
10987
|
+
} catch {
|
|
10988
|
+
return false;
|
|
10989
|
+
}
|
|
10990
|
+
});
|
|
10991
|
+
}
|
|
10992
|
+
function pluginHooks(workDir) {
|
|
10993
|
+
const merged = {};
|
|
10994
|
+
for (const p of loadPlugins3(workDir)) {
|
|
10995
|
+
try {
|
|
10996
|
+
const raw = fs6.readFileSync(path5.join(p.dir, "hooks.json"), "utf-8");
|
|
10997
|
+
const j = JSON.parse(raw);
|
|
10998
|
+
const hooks = j.hooks ?? j;
|
|
10999
|
+
for (const [phase, entries] of Object.entries(hooks)) {
|
|
11000
|
+
if (!Array.isArray(entries))
|
|
11001
|
+
continue;
|
|
11002
|
+
merged[phase] = [...merged[phase] ?? [], ...entries];
|
|
11003
|
+
}
|
|
11004
|
+
} catch {
|
|
11005
|
+
}
|
|
11006
|
+
}
|
|
11007
|
+
return merged;
|
|
11008
|
+
}
|
|
11009
|
+
function pluginMcpServers(workDir) {
|
|
11010
|
+
const merged = {};
|
|
11011
|
+
for (const p of loadPlugins3(workDir)) {
|
|
11012
|
+
try {
|
|
11013
|
+
const raw = fs6.readFileSync(path5.join(p.dir, "mcp.json"), "utf-8");
|
|
11014
|
+
const j = JSON.parse(raw);
|
|
11015
|
+
const servers = j.mcpServers ?? j;
|
|
11016
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
11017
|
+
if (!(name in merged))
|
|
11018
|
+
merged[name] = cfg;
|
|
11019
|
+
}
|
|
11020
|
+
} catch {
|
|
11021
|
+
}
|
|
11022
|
+
}
|
|
11023
|
+
return merged;
|
|
11024
|
+
}
|
|
11025
|
+
}
|
|
11026
|
+
});
|
|
11027
|
+
|
|
11028
|
+
// ../core/dist/commands/loader.js
|
|
11029
|
+
var require_loader = __commonJS({
|
|
11030
|
+
"../core/dist/commands/loader.js"(exports2) {
|
|
11031
|
+
"use strict";
|
|
11032
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
11033
|
+
if (k2 === void 0)
|
|
11034
|
+
k2 = k;
|
|
11035
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
11036
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
11037
|
+
desc = { enumerable: true, get: function() {
|
|
11038
|
+
return m2[k];
|
|
11039
|
+
} };
|
|
11040
|
+
}
|
|
11041
|
+
Object.defineProperty(o, k2, desc);
|
|
11042
|
+
} : function(o, m2, k, k2) {
|
|
11043
|
+
if (k2 === void 0)
|
|
11044
|
+
k2 = k;
|
|
11045
|
+
o[k2] = m2[k];
|
|
11046
|
+
});
|
|
11047
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
11048
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11049
|
+
} : function(o, v) {
|
|
11050
|
+
o["default"] = v;
|
|
11051
|
+
});
|
|
11052
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
11053
|
+
var ownKeys = function(o) {
|
|
11054
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
11055
|
+
var ar = [];
|
|
11056
|
+
for (var k in o2)
|
|
11057
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
11058
|
+
ar[ar.length] = k;
|
|
11059
|
+
return ar;
|
|
11060
|
+
};
|
|
11061
|
+
return ownKeys(o);
|
|
11062
|
+
};
|
|
11063
|
+
return function(mod) {
|
|
11064
|
+
if (mod && mod.__esModule)
|
|
11065
|
+
return mod;
|
|
11066
|
+
var result = {};
|
|
11067
|
+
if (mod != null) {
|
|
11068
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
11069
|
+
if (k[i2] !== "default")
|
|
11070
|
+
__createBinding(result, mod, k[i2]);
|
|
11071
|
+
}
|
|
11072
|
+
__setModuleDefault(result, mod);
|
|
11073
|
+
return result;
|
|
11074
|
+
};
|
|
11075
|
+
}();
|
|
11076
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11077
|
+
exports2.loadSlashCommands = loadSlashCommands;
|
|
11078
|
+
exports2.findSlashCommand = findSlashCommand;
|
|
11079
|
+
exports2.expandBody = expandBody;
|
|
11080
|
+
exports2.expandCommand = expandCommand;
|
|
11081
|
+
var fs6 = __importStar(require("fs"));
|
|
11082
|
+
var path5 = __importStar(require("path"));
|
|
11083
|
+
var os5 = __importStar(require("os"));
|
|
11084
|
+
var child_process_1 = require("child_process");
|
|
11085
|
+
var index_1 = require_plugins();
|
|
11086
|
+
var BUILTIN_COMMANDS = [
|
|
11087
|
+
{
|
|
11088
|
+
name: "review",
|
|
11089
|
+
description: "Review uncommitted changes (or a PR/branch diff) for bugs and risks",
|
|
11090
|
+
source: "builtin",
|
|
11091
|
+
body: [
|
|
11092
|
+
"Review the following diff like a meticulous senior engineer. Target: $ARGUMENTS",
|
|
11093
|
+
"(If no target given, review the uncommitted working-tree changes below. If a branch or PR",
|
|
11094
|
+
"number is given, run the appropriate `git diff <base>...` or `gh pr diff <n>` yourself first.)",
|
|
11095
|
+
"",
|
|
11096
|
+
"Branch: !`git branch --show-current`",
|
|
11097
|
+
"Status: !`git status --short`",
|
|
11098
|
+
"",
|
|
11099
|
+
"Diff (uncommitted):",
|
|
11100
|
+
"```diff",
|
|
11101
|
+
"!`git diff HEAD --unified=5 --no-color | head -4000`",
|
|
11102
|
+
"```",
|
|
11103
|
+
"",
|
|
11104
|
+
"Review methodology:",
|
|
11105
|
+
"1. Read the surrounding code of every changed hunk (read_file with offset/limit) \u2014 never judge a hunk in isolation.",
|
|
11106
|
+
"2. Look for: correctness bugs, edge cases (empty/null/unicode/concurrency), security issues",
|
|
11107
|
+
" (injection, path traversal, secrets), breaking API changes (find_references / search callers),",
|
|
11108
|
+
" silent behaviour changes, and missing error handling.",
|
|
11109
|
+
"3. Check tests: do existing tests cover the change? Are assertions weakened?",
|
|
11110
|
+
"",
|
|
11111
|
+
"Output format:",
|
|
11112
|
+
"- \u{1F534} Critical (must fix before merge) \u2014 with file:line and a concrete fix",
|
|
11113
|
+
"- \u{1F7E1} Warning (should fix) \u2014 with file:line",
|
|
11114
|
+
"- \u{1F7E2} Suggestion (nice to have)",
|
|
11115
|
+
"- Verdict: APPROVE / REQUEST CHANGES with a one-paragraph summary.",
|
|
11116
|
+
"Do NOT modify any files \u2014 this is a read-only review."
|
|
11117
|
+
].join("\n")
|
|
11118
|
+
}
|
|
11119
|
+
];
|
|
11120
|
+
function parseFrontmatter(raw) {
|
|
11121
|
+
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
11122
|
+
if (!m2)
|
|
11123
|
+
return { meta: {}, body: raw.trim() };
|
|
11124
|
+
const meta = {};
|
|
11125
|
+
for (const line of m2[1].split(/\r?\n/)) {
|
|
11126
|
+
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
11127
|
+
if (kv)
|
|
11128
|
+
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
|
|
11129
|
+
}
|
|
11130
|
+
return { meta, body: (m2[2] ?? "").trim() };
|
|
11131
|
+
}
|
|
11132
|
+
function loadDir(dir, source, into) {
|
|
11133
|
+
let files;
|
|
11134
|
+
try {
|
|
11135
|
+
files = fs6.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
11136
|
+
} catch {
|
|
11137
|
+
return;
|
|
11138
|
+
}
|
|
11139
|
+
for (const file of files) {
|
|
11140
|
+
try {
|
|
11141
|
+
const raw = fs6.readFileSync(path5.join(dir, file), "utf-8");
|
|
11142
|
+
const { meta, body } = parseFrontmatter(raw);
|
|
11143
|
+
const name = (meta.name || path5.basename(file, ".md")).trim().toLowerCase();
|
|
11144
|
+
if (!name)
|
|
11145
|
+
continue;
|
|
11146
|
+
if (source !== "project" && into.has(name))
|
|
11147
|
+
continue;
|
|
11148
|
+
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
11149
|
+
into.set(name, {
|
|
11150
|
+
name,
|
|
11151
|
+
description: meta.description || `Custom /${name} command`,
|
|
11152
|
+
model,
|
|
11153
|
+
mode: meta.mode || void 0,
|
|
11154
|
+
body,
|
|
11155
|
+
source
|
|
11156
|
+
});
|
|
11157
|
+
} catch {
|
|
11158
|
+
}
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11161
|
+
function loadSlashCommands(workDir) {
|
|
11162
|
+
const out = /* @__PURE__ */ new Map();
|
|
11163
|
+
loadDir(path5.join(workDir, ".nexrall", "commands"), "project", out);
|
|
11164
|
+
loadDir(path5.join(os5.homedir(), ".nexrall", "commands"), "global", out);
|
|
11165
|
+
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
11166
|
+
loadDir(dir, "plugin", out);
|
|
11167
|
+
for (const cmd of BUILTIN_COMMANDS) {
|
|
11168
|
+
if (!out.has(cmd.name))
|
|
11169
|
+
out.set(cmd.name, cmd);
|
|
11170
|
+
}
|
|
11171
|
+
return [...out.values()];
|
|
11172
|
+
}
|
|
11173
|
+
function findSlashCommand(cmds, name) {
|
|
11174
|
+
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
11175
|
+
return cmds.find((c) => c.name === want);
|
|
11176
|
+
}
|
|
11177
|
+
function expandBody(body, argString, workDir) {
|
|
11178
|
+
const args = argString.trim();
|
|
11179
|
+
const positional = args ? args.split(/\s+/) : [];
|
|
11180
|
+
let out = body;
|
|
11181
|
+
out = out.replace(/!`([^`]+)`/g, (_m, c) => {
|
|
11182
|
+
try {
|
|
11183
|
+
const stdout = (0, child_process_1.execSync)(c, { cwd: workDir, encoding: "utf-8", timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] });
|
|
11184
|
+
return stdout.trim();
|
|
11185
|
+
} catch (err) {
|
|
11186
|
+
return `[command failed: ${c} \u2014 ${err.message}]`;
|
|
11187
|
+
}
|
|
11188
|
+
});
|
|
11189
|
+
out = out.replace(/(^|\s)@([^\s]+)/g, (_m, lead, rel) => {
|
|
11190
|
+
const abs = path5.isAbsolute(rel) ? rel : path5.join(workDir, rel);
|
|
11191
|
+
try {
|
|
11192
|
+
const content = fs6.readFileSync(abs, "utf-8").slice(0, 12e3);
|
|
11193
|
+
return `${lead}
|
|
11194
|
+
[File: ${rel}]
|
|
11195
|
+
\`\`\`
|
|
11196
|
+
${content}
|
|
11197
|
+
\`\`\`
|
|
11198
|
+
`;
|
|
11199
|
+
} catch {
|
|
11200
|
+
return `${lead}[missing file: ${rel}]`;
|
|
11201
|
+
}
|
|
11202
|
+
});
|
|
11203
|
+
out = out.replace(/\$(\d+)/g, (_m, n) => positional[Number(n) - 1] ?? "");
|
|
11204
|
+
const hadArgsToken = /\$ARGUMENTS/.test(out);
|
|
11205
|
+
out = out.replace(/\$ARGUMENTS/g, args);
|
|
11206
|
+
if (!hadArgsToken && !/\$\d+/.test(body) && args) {
|
|
11207
|
+
out = `${out}
|
|
11208
|
+
|
|
11209
|
+
${args}`;
|
|
11210
|
+
}
|
|
11211
|
+
return out.trim();
|
|
11212
|
+
}
|
|
11213
|
+
function expandCommand(cmd, argString, workDir) {
|
|
11214
|
+
return expandBody(cmd.body, argString, workDir);
|
|
11215
|
+
}
|
|
11216
|
+
}
|
|
11217
|
+
});
|
|
11218
|
+
|
|
11219
|
+
// ../core/dist/agent/skills.js
|
|
11220
|
+
var require_skills = __commonJS({
|
|
11221
|
+
"../core/dist/agent/skills.js"(exports2) {
|
|
11222
|
+
"use strict";
|
|
11223
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
11224
|
+
if (k2 === void 0)
|
|
11225
|
+
k2 = k;
|
|
11226
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
11227
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
11228
|
+
desc = { enumerable: true, get: function() {
|
|
11229
|
+
return m2[k];
|
|
11230
|
+
} };
|
|
11231
|
+
}
|
|
11232
|
+
Object.defineProperty(o, k2, desc);
|
|
11233
|
+
} : function(o, m2, k, k2) {
|
|
11234
|
+
if (k2 === void 0)
|
|
11235
|
+
k2 = k;
|
|
11236
|
+
o[k2] = m2[k];
|
|
11237
|
+
});
|
|
11238
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
11239
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11240
|
+
} : function(o, v) {
|
|
11241
|
+
o["default"] = v;
|
|
11242
|
+
});
|
|
11243
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
11244
|
+
var ownKeys = function(o) {
|
|
11245
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
11246
|
+
var ar = [];
|
|
11247
|
+
for (var k in o2)
|
|
11248
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
11249
|
+
ar[ar.length] = k;
|
|
11250
|
+
return ar;
|
|
11251
|
+
};
|
|
11252
|
+
return ownKeys(o);
|
|
11253
|
+
};
|
|
11254
|
+
return function(mod) {
|
|
11255
|
+
if (mod && mod.__esModule)
|
|
11256
|
+
return mod;
|
|
11257
|
+
var result = {};
|
|
11258
|
+
if (mod != null) {
|
|
11259
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
11260
|
+
if (k[i2] !== "default")
|
|
11261
|
+
__createBinding(result, mod, k[i2]);
|
|
11262
|
+
}
|
|
11263
|
+
__setModuleDefault(result, mod);
|
|
11264
|
+
return result;
|
|
11265
|
+
};
|
|
11266
|
+
}();
|
|
11267
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11268
|
+
exports2.loadSkills = loadSkills2;
|
|
11269
|
+
exports2.findSkill = findSkill2;
|
|
11270
|
+
exports2.autoInvokableSkills = autoInvokableSkills;
|
|
11271
|
+
exports2.userInvokableSkills = userInvokableSkills2;
|
|
11272
|
+
exports2.summariseSkills = summariseSkills;
|
|
11273
|
+
exports2.expandSkill = expandSkill2;
|
|
11274
|
+
var fs6 = __importStar(require("fs"));
|
|
11275
|
+
var path5 = __importStar(require("path"));
|
|
11276
|
+
var os5 = __importStar(require("os"));
|
|
11277
|
+
var index_1 = require_plugins();
|
|
11278
|
+
var loader_1 = require_loader();
|
|
11279
|
+
function parseFrontmatter(raw) {
|
|
11280
|
+
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
11281
|
+
if (!m2)
|
|
11282
|
+
return { meta: {}, body: raw.trim() };
|
|
11283
|
+
const meta = {};
|
|
11284
|
+
for (const line of m2[1].split(/\r?\n/)) {
|
|
11285
|
+
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
11286
|
+
if (kv)
|
|
11287
|
+
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
|
|
11288
|
+
}
|
|
11289
|
+
return { meta, body: (m2[2] ?? "").trim() };
|
|
11290
|
+
}
|
|
11291
|
+
function parseBool(v, fallback) {
|
|
11292
|
+
if (v === void 0)
|
|
11293
|
+
return fallback;
|
|
11294
|
+
const s2 = v.trim().toLowerCase();
|
|
11295
|
+
if (s2 === "true" || s2 === "1" || s2 === "yes")
|
|
11296
|
+
return true;
|
|
11297
|
+
if (s2 === "false" || s2 === "0" || s2 === "no")
|
|
11298
|
+
return false;
|
|
11299
|
+
return fallback;
|
|
11300
|
+
}
|
|
11301
|
+
function toSkill(meta, body, name, source, dir) {
|
|
11302
|
+
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
11303
|
+
return {
|
|
11304
|
+
name,
|
|
11305
|
+
description: meta.description || `Custom /${name} skill`,
|
|
11306
|
+
model,
|
|
11307
|
+
mode: meta.mode || void 0,
|
|
11308
|
+
body,
|
|
11309
|
+
source,
|
|
11310
|
+
dir,
|
|
11311
|
+
disableModelInvocation: parseBool(meta["disable-model-invocation"], false),
|
|
11312
|
+
userInvocable: parseBool(meta["user-invocable"], true)
|
|
11313
|
+
};
|
|
11314
|
+
}
|
|
11315
|
+
function loadFlatCommandDir(dir, source, into) {
|
|
11316
|
+
let files;
|
|
11317
|
+
try {
|
|
11318
|
+
files = fs6.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
11319
|
+
} catch {
|
|
11320
|
+
return;
|
|
11321
|
+
}
|
|
11322
|
+
for (const file of files) {
|
|
11323
|
+
try {
|
|
11324
|
+
const raw = fs6.readFileSync(path5.join(dir, file), "utf-8");
|
|
11325
|
+
const { meta, body } = parseFrontmatter(raw);
|
|
11326
|
+
const name = (meta.name || path5.basename(file, ".md")).trim().toLowerCase();
|
|
11327
|
+
if (!name)
|
|
11328
|
+
continue;
|
|
11329
|
+
if (source !== "project" && into.has(name))
|
|
11330
|
+
continue;
|
|
11331
|
+
into.set(name, toSkill(meta, body, name, source));
|
|
11332
|
+
} catch {
|
|
11333
|
+
}
|
|
11334
|
+
}
|
|
11335
|
+
}
|
|
11336
|
+
function loadSkillDir(root, source, into) {
|
|
11337
|
+
let entries;
|
|
11338
|
+
try {
|
|
11339
|
+
entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
11340
|
+
} catch {
|
|
11341
|
+
return;
|
|
11342
|
+
}
|
|
11343
|
+
for (const entry of entries) {
|
|
11344
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
11345
|
+
continue;
|
|
11346
|
+
const skillDir = path5.join(root, entry.name);
|
|
11347
|
+
const skillFile = path5.join(skillDir, "SKILL.md");
|
|
11348
|
+
try {
|
|
11349
|
+
if (!fs6.statSync(skillFile).isFile())
|
|
11350
|
+
continue;
|
|
11351
|
+
} catch {
|
|
11352
|
+
continue;
|
|
11353
|
+
}
|
|
11354
|
+
try {
|
|
11355
|
+
const raw = fs6.readFileSync(skillFile, "utf-8");
|
|
11356
|
+
const { meta, body } = parseFrontmatter(raw);
|
|
11357
|
+
const name = (meta.name || entry.name).trim().toLowerCase();
|
|
11358
|
+
if (!name)
|
|
11359
|
+
continue;
|
|
11360
|
+
if (source !== "project" && into.has(name))
|
|
11361
|
+
continue;
|
|
11362
|
+
into.set(name, toSkill(meta, body, name, source, skillDir));
|
|
11363
|
+
} catch {
|
|
11364
|
+
}
|
|
11365
|
+
}
|
|
11366
|
+
}
|
|
11367
|
+
var BUILTIN_SKILLS = [
|
|
11368
|
+
{
|
|
11369
|
+
name: "review",
|
|
11370
|
+
description: "Review uncommitted changes (or a PR/branch diff) for correctness bugs, edge cases, and security issues. Use when the user asks to review a diff, check their changes before committing, or audit a PR.",
|
|
11371
|
+
source: "builtin",
|
|
11372
|
+
disableModelInvocation: false,
|
|
11373
|
+
userInvocable: true,
|
|
11374
|
+
body: [
|
|
11375
|
+
"Review the following diff like a meticulous senior engineer. Target: $ARGUMENTS",
|
|
11376
|
+
"(If no target given, review the uncommitted working-tree changes below. If a branch or PR",
|
|
11377
|
+
"number is given, run the appropriate `git diff <base>...` or `gh pr diff <n>` yourself first.)",
|
|
11378
|
+
"",
|
|
11379
|
+
"Branch: !`git branch --show-current`",
|
|
11380
|
+
"Status: !`git status --short`",
|
|
11381
|
+
"",
|
|
11382
|
+
"Diff (uncommitted):",
|
|
11383
|
+
"```diff",
|
|
11384
|
+
"!`git diff HEAD --unified=5 --no-color | head -4000`",
|
|
11385
|
+
"```",
|
|
11386
|
+
"",
|
|
11387
|
+
"Review methodology:",
|
|
11388
|
+
"1. Read the surrounding code of every changed hunk (read_file with offset/limit) \u2014 never judge a hunk in isolation.",
|
|
11389
|
+
"2. Look for: correctness bugs, edge cases (empty/null/unicode/concurrency), security issues",
|
|
11390
|
+
" (injection, path traversal, secrets), breaking API changes (find_references / search callers),",
|
|
11391
|
+
" silent behaviour changes, and missing error handling.",
|
|
11392
|
+
"3. Check tests: do existing tests cover the change? Are assertions weakened?",
|
|
11393
|
+
"",
|
|
11394
|
+
"Output format:",
|
|
11395
|
+
"- \u{1F534} Critical (must fix before merge) \u2014 with file:line and a concrete fix",
|
|
11396
|
+
"- \u{1F7E1} Warning (should fix) \u2014 with file:line",
|
|
11397
|
+
"- \u{1F7E2} Suggestion (nice to have)",
|
|
11398
|
+
"- Verdict: APPROVE / REQUEST CHANGES with a one-paragraph summary.",
|
|
11399
|
+
"Do NOT modify any files \u2014 this is a read-only review."
|
|
11400
|
+
].join("\n")
|
|
11401
|
+
}
|
|
11402
|
+
];
|
|
11403
|
+
function loadSkills2(workDir) {
|
|
11404
|
+
const out = /* @__PURE__ */ new Map();
|
|
11405
|
+
loadFlatCommandDir(path5.join(workDir, ".nexrall", "commands"), "project", out);
|
|
11406
|
+
loadSkillDir(path5.join(workDir, ".nexrall", "skills"), "project", out);
|
|
11407
|
+
loadFlatCommandDir(path5.join(os5.homedir(), ".nexrall", "commands"), "global", out);
|
|
11408
|
+
loadSkillDir(path5.join(os5.homedir(), ".nexrall", "skills"), "global", out);
|
|
11409
|
+
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
11410
|
+
loadFlatCommandDir(dir, "plugin", out);
|
|
11411
|
+
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "skills"))
|
|
11412
|
+
loadSkillDir(dir, "plugin", out);
|
|
11413
|
+
for (const skill of BUILTIN_SKILLS) {
|
|
11414
|
+
if (!out.has(skill.name))
|
|
11415
|
+
out.set(skill.name, skill);
|
|
11416
|
+
}
|
|
11417
|
+
return [...out.values()];
|
|
11418
|
+
}
|
|
11419
|
+
function findSkill2(skills, name) {
|
|
11420
|
+
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
11421
|
+
return skills.find((s2) => s2.name === want);
|
|
11422
|
+
}
|
|
11423
|
+
function autoInvokableSkills(skills) {
|
|
11424
|
+
return skills.filter((s2) => !s2.disableModelInvocation);
|
|
11425
|
+
}
|
|
11426
|
+
function userInvokableSkills2(skills) {
|
|
11427
|
+
return skills.filter((s2) => s2.userInvocable);
|
|
11428
|
+
}
|
|
11429
|
+
function summariseSkills(skills) {
|
|
11430
|
+
const list = autoInvokableSkills(skills);
|
|
11431
|
+
if (!list.length)
|
|
11432
|
+
return "";
|
|
11433
|
+
return list.map((s2) => `- ${s2.name}: ${s2.description}`).join("\n");
|
|
11434
|
+
}
|
|
11435
|
+
function expandSkill2(skill, argString, workDir) {
|
|
11436
|
+
const expanded = (0, loader_1.expandBody)(skill.body, argString, workDir);
|
|
11437
|
+
if (!skill.dir)
|
|
11438
|
+
return expanded;
|
|
11439
|
+
let siblings = [];
|
|
11440
|
+
try {
|
|
11441
|
+
siblings = fs6.readdirSync(skill.dir).filter((f3) => f3 !== "SKILL.md");
|
|
11442
|
+
} catch {
|
|
11443
|
+
}
|
|
11444
|
+
if (!siblings.length)
|
|
11445
|
+
return expanded;
|
|
11446
|
+
return `[Skill "${skill.name}" \u2014 supporting files available in ${skill.dir} (use read_file with an absolute path to load any of these only if the instructions below need them): ${siblings.join(", ")}]
|
|
11447
|
+
|
|
11448
|
+
${expanded}`;
|
|
11449
|
+
}
|
|
11450
|
+
}
|
|
11451
|
+
});
|
|
11452
|
+
|
|
11453
|
+
// ../core/dist/agent/testIntegrity.js
|
|
11454
|
+
var require_testIntegrity = __commonJS({
|
|
11455
|
+
"../core/dist/agent/testIntegrity.js"(exports2) {
|
|
11456
|
+
"use strict";
|
|
11457
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11458
|
+
exports2.isTestFile = isTestFile;
|
|
11459
|
+
exports2.analyzeTestEdit = analyzeTestEdit;
|
|
11460
|
+
exports2.analyzeWriteToolForTestIntegrity = analyzeWriteToolForTestIntegrity;
|
|
11461
|
+
exports2.encodeTestIntegrityMarker = encodeTestIntegrityMarker;
|
|
11462
|
+
exports2.decodeTestIntegrityMarker = decodeTestIntegrityMarker;
|
|
11463
|
+
exports2.stripTestIntegrityMarker = stripTestIntegrityMarker;
|
|
11464
|
+
var TEST_PATH_RE = /(?:^|[\/\\])(?:tests?|spec|__tests__|testing)[\/\\]|(?:\.|_|-)(?:test|spec|tests)\.[a-z]+$|(?:^|[\/\\])test_[^\/\\]+\.py$|(?:^|[\/\\])[^\/\\]+_test\.(?:go|py|rb)$|Test[^\/\\]*\.(?:java|kt|cs)$|(?:^|[\/\\])[^\/\\]*Tests?\.(?:java|kt|cs)$/i;
|
|
11465
|
+
function isTestFile(path5) {
|
|
11466
|
+
if (!path5 || typeof path5 !== "string")
|
|
11467
|
+
return false;
|
|
11468
|
+
return TEST_PATH_RE.test(path5);
|
|
11469
|
+
}
|
|
11470
|
+
var ASSERTION_RES = [
|
|
11471
|
+
/\bexpect\s*\(/g,
|
|
11472
|
+
// jest / chai / vitest / jasmine
|
|
11473
|
+
/\bassert(?:Equals?|True|False|That|Null|NotNull|Same)?\b/g,
|
|
11474
|
+
// junit / python unittest / generic
|
|
11475
|
+
/\bassert\s*[.(]/g,
|
|
11476
|
+
// node:assert (assert.equal / assert( ) )
|
|
11477
|
+
/\.should\b/g,
|
|
11478
|
+
// chai should / rspec should
|
|
11479
|
+
/\bshould\s*[.(]/g,
|
|
11480
|
+
/\bEXPECT_[A-Z]+\s*\(/g,
|
|
11481
|
+
// gtest
|
|
11482
|
+
/\bASSERT_[A-Z]+\s*\(/g,
|
|
11483
|
+
// gtest
|
|
11484
|
+
/\brequire\.\w+\s*\(/g,
|
|
11485
|
+
// testify require
|
|
11486
|
+
/\bassert\.\w+\s*\(/g,
|
|
11487
|
+
// testify assert
|
|
11488
|
+
/\bt\.(?:Error|Fatal|Errorf|Fatalf)\b/g,
|
|
11489
|
+
// go testing
|
|
11490
|
+
/\bassert!\s*\(|\bassert_eq!\s*\(|\bassert_ne!\s*\(/g
|
|
11491
|
+
// rust
|
|
11492
|
+
];
|
|
11493
|
+
var TESTCASE_RES = [
|
|
11494
|
+
/\b(?:it|test)\s*\(/g,
|
|
11495
|
+
// jest / mocha / jasmine
|
|
11496
|
+
/\bdef\s+test_\w+/g,
|
|
11497
|
+
// python
|
|
11498
|
+
/\bfunc\s+Test\w+/g,
|
|
11499
|
+
// go
|
|
11500
|
+
/@Test\b/g,
|
|
11501
|
+
// junit
|
|
11502
|
+
/\bfn\s+\w*test\w*\s*\(/gi
|
|
11503
|
+
// rust (best-effort)
|
|
11504
|
+
];
|
|
11505
|
+
var SKIP_RES = [
|
|
11506
|
+
/\b(?:it|test|describe|context|suite)\s*\.\s*(?:skip|only)\b/g,
|
|
11507
|
+
/\bx(?:it|describe|test|context)\s*\(/g,
|
|
11508
|
+
// xit / xdescribe
|
|
11509
|
+
/\bf(?:it|describe)\s*\(/g,
|
|
11510
|
+
// fit / fdescribe (focus)
|
|
11511
|
+
/@pytest\.mark\.skip\b|@pytest\.mark\.skipif\b|@unittest\.skip\b|@skip\b/g,
|
|
11512
|
+
/\bpytest\.skip\s*\(/g,
|
|
11513
|
+
/\bt\.Skip\s*\(|\bt\.SkipNow\s*\(/g,
|
|
11514
|
+
// go
|
|
11515
|
+
/@Disabled\b|@Ignore\b/g,
|
|
11516
|
+
// junit / kotlin
|
|
11517
|
+
/\.only\s*\(/g
|
|
11518
|
+
// test.only leaks CI coverage
|
|
11519
|
+
];
|
|
11520
|
+
var TAUTOLOGY_RES = [
|
|
11521
|
+
/\bassert\s+True\b|\bassert\s+1\b|\bassert\s+not\s+False\b/g,
|
|
11522
|
+
// python
|
|
11523
|
+
/\bassert\s*\(\s*true\s*\)|\bassert\.ok\s*\(\s*true\s*\)/gi,
|
|
11524
|
+
// node
|
|
11525
|
+
/\bexpect\s*\(\s*true\s*\)\s*\.\s*to(?:Be|Equal|BeTruthy)\s*\(\s*true\s*\)?/gi,
|
|
11526
|
+
// jest
|
|
11527
|
+
/\bexpect\s*\(\s*(\w+)\s*\)\s*\.\s*toBe\s*\(\s*\1\s*\)/g,
|
|
11528
|
+
// expect(x).toBe(x)
|
|
11529
|
+
/\bassert_eq!\s*\(\s*true\s*,\s*true\s*\)/g,
|
|
11530
|
+
// rust
|
|
11531
|
+
/\bassert!\s*\(\s*true\s*\)/g
|
|
11532
|
+
];
|
|
11533
|
+
function countMatches(res, text) {
|
|
11534
|
+
let n = 0;
|
|
11535
|
+
for (const re of res) {
|
|
11536
|
+
re.lastIndex = 0;
|
|
11537
|
+
const m2 = text.match(re);
|
|
11538
|
+
if (m2)
|
|
11539
|
+
n += m2.length;
|
|
11540
|
+
}
|
|
11541
|
+
return n;
|
|
11542
|
+
}
|
|
11543
|
+
function countCommentedOutTestLines(oldText, newText) {
|
|
11544
|
+
const oldLines = new Set(oldText.split("\n").map((l) => l.trim()));
|
|
11545
|
+
let n = 0;
|
|
11546
|
+
for (const raw of newText.split("\n")) {
|
|
11547
|
+
const line = raw.trim();
|
|
11548
|
+
const isComment = /^(?:\/\/|#|\/\*|\*)/.test(line);
|
|
11549
|
+
if (!isComment)
|
|
11550
|
+
continue;
|
|
11551
|
+
const body = line.replace(/^(?:\/\/+|#+|\/\*+|\*+)\s?/, "");
|
|
11552
|
+
const looksLikeTest = /\b(expect|assert|it\(|test\(|def test_|func Test|EXPECT_|ASSERT_)\b/.test(body);
|
|
11553
|
+
if (looksLikeTest && oldLines.has(body))
|
|
11554
|
+
n += 1;
|
|
11555
|
+
}
|
|
11556
|
+
return n;
|
|
11557
|
+
}
|
|
11558
|
+
function analyzeTestEdit(path5, oldText, newText) {
|
|
11559
|
+
if (!isTestFile(path5)) {
|
|
11560
|
+
return { isTestFile: false, suspicious: false, findings: [] };
|
|
11561
|
+
}
|
|
11562
|
+
const findings = [];
|
|
11563
|
+
const skipsBefore = countMatches(SKIP_RES, oldText);
|
|
11564
|
+
const skipsAfter = countMatches(SKIP_RES, newText);
|
|
11565
|
+
if (skipsAfter > skipsBefore) {
|
|
11566
|
+
findings.push({
|
|
11567
|
+
kind: "skip-added",
|
|
11568
|
+
reason: `added ${skipsAfter - skipsBefore} skip/only/disabled marker(s) \u2014 tests are being skipped, not fixed`
|
|
11569
|
+
});
|
|
11570
|
+
}
|
|
11571
|
+
const tautoBefore = countMatches(TAUTOLOGY_RES, oldText);
|
|
11572
|
+
const tautoAfter = countMatches(TAUTOLOGY_RES, newText);
|
|
10531
11573
|
if (tautoAfter > tautoBefore) {
|
|
10532
11574
|
findings.push({
|
|
10533
11575
|
kind: "tautology-added",
|
|
@@ -11307,6 +12349,8 @@ var require_executor = __commonJS({
|
|
|
11307
12349
|
var sandbox_1 = require_sandbox();
|
|
11308
12350
|
var auth_1 = require_auth();
|
|
11309
12351
|
var editCompleteness_1 = require_editCompleteness();
|
|
12352
|
+
var memory_1 = require_memory();
|
|
12353
|
+
var skills_1 = require_skills();
|
|
11310
12354
|
var testIntegrity_1 = require_testIntegrity();
|
|
11311
12355
|
var crossFile_1 = require_crossFile();
|
|
11312
12356
|
var client_1 = require_client();
|
|
@@ -11316,6 +12360,10 @@ var require_executor = __commonJS({
|
|
|
11316
12360
|
var MAX_FETCH_BYTES = 200 * 1024;
|
|
11317
12361
|
var MAX_READ_BYTES = 500 * 1024;
|
|
11318
12362
|
var MAX_OUTPUT_CHARS = 1e5;
|
|
12363
|
+
var MAX_READ_LINES = 2e3;
|
|
12364
|
+
var MAX_READ_TOKENS = 25e3;
|
|
12365
|
+
var READ_CHARS_PER_TOKEN = 4;
|
|
12366
|
+
var MAX_READ_CHARS = MAX_READ_TOKENS * READ_CHARS_PER_TOKEN;
|
|
11319
12367
|
var MAX_SPILL_BYTES = 20 * 1024 * 1024;
|
|
11320
12368
|
var SPILL_DIR = path5.join(os5.tmpdir(), "nexrall-code", "bash-output");
|
|
11321
12369
|
var BLOCKED_REGEXES = [
|
|
@@ -11410,24 +12458,50 @@ var require_executor = __commonJS({
|
|
|
11410
12458
|
return false;
|
|
11411
12459
|
}
|
|
11412
12460
|
}
|
|
11413
|
-
|
|
12461
|
+
function formatNumberedLine(offset, indexInKept, line) {
|
|
12462
|
+
return `${String(offset + indexInKept + 1).padStart(4, " ")} ${line}`;
|
|
12463
|
+
}
|
|
12464
|
+
async function readFileWindowed(resolved, offset, requestedLimit) {
|
|
11414
12465
|
return new Promise((resolve3) => {
|
|
11415
|
-
const
|
|
12466
|
+
const explicitLimit = requestedLimit > 0;
|
|
12467
|
+
const lineScanCap = explicitLimit ? Math.min(requestedLimit, MAX_READ_LINES) : MAX_READ_LINES;
|
|
12468
|
+
const hardEndLine = offset + lineScanCap;
|
|
11416
12469
|
const kept = [];
|
|
12470
|
+
let keptChars = 0;
|
|
11417
12471
|
let lineNo = 0;
|
|
11418
12472
|
let carry = "";
|
|
11419
12473
|
let stopped = false;
|
|
12474
|
+
let hitTokenCap = false;
|
|
12475
|
+
let sawEof = false;
|
|
11420
12476
|
const stream = fs6.createReadStream(resolved, { encoding: "utf-8", highWaterMark: 256 * 1024 });
|
|
12477
|
+
const tryPushLine = (line) => {
|
|
12478
|
+
const formatted = formatNumberedLine(offset, kept.length, line);
|
|
12479
|
+
const added = formatted.length + 1;
|
|
12480
|
+
if (keptChars + added > MAX_READ_CHARS) {
|
|
12481
|
+
hitTokenCap = true;
|
|
12482
|
+
return false;
|
|
12483
|
+
}
|
|
12484
|
+
kept.push(line);
|
|
12485
|
+
keptChars += added;
|
|
12486
|
+
return true;
|
|
12487
|
+
};
|
|
11421
12488
|
const finish = () => {
|
|
11422
12489
|
if (stopped)
|
|
11423
12490
|
return;
|
|
11424
12491
|
stopped = true;
|
|
11425
12492
|
stream.destroy();
|
|
12493
|
+
if (hitTokenCap && explicitLimit) {
|
|
12494
|
+
resolve3({
|
|
12495
|
+
error: `Requested range (offset:${offset}, limit:${requestedLimit}) is too large \u2014 exceeds ${MAX_READ_TOKENS} tokens. Pass a smaller limit, or use search_files to locate the relevant part first.`
|
|
12496
|
+
});
|
|
12497
|
+
return;
|
|
12498
|
+
}
|
|
11426
12499
|
const first = offset + 1;
|
|
11427
12500
|
const last = offset + kept.length;
|
|
11428
|
-
const numbered = kept.map((l, i2) =>
|
|
11429
|
-
const
|
|
11430
|
-
|
|
12501
|
+
const numbered = kept.map((l, i2) => formatNumberedLine(offset, i2, l)).join("\n");
|
|
12502
|
+
const totalNote = sawEof ? `/${lineNo}` : "";
|
|
12503
|
+
const note = sawEof ? ` (reached end of file at line ${last})` : ` (more lines follow \u2014 pass offset:${last} to continue, or search_files to locate the relevant part)`;
|
|
12504
|
+
resolve3({ output: `[File: ${resolved} \u2014 lines ${first}-${last}${totalNote}${note}]
|
|
11431
12505
|
${numbered}` });
|
|
11432
12506
|
};
|
|
11433
12507
|
stream.on("data", (chunk) => {
|
|
@@ -11435,18 +12509,30 @@ ${numbered}` });
|
|
|
11435
12509
|
const lines = text.split("\n");
|
|
11436
12510
|
carry = lines.pop() ?? "";
|
|
11437
12511
|
for (const line of lines) {
|
|
11438
|
-
if (lineNo >= offset
|
|
11439
|
-
|
|
12512
|
+
if (lineNo >= offset) {
|
|
12513
|
+
if (lineNo >= hardEndLine) {
|
|
12514
|
+
finish();
|
|
12515
|
+
return;
|
|
12516
|
+
}
|
|
12517
|
+
if (!tryPushLine(line)) {
|
|
12518
|
+
finish();
|
|
12519
|
+
return;
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
11440
12522
|
lineNo++;
|
|
11441
|
-
|
|
12523
|
+
}
|
|
12524
|
+
});
|
|
12525
|
+
stream.on("end", () => {
|
|
12526
|
+
if (stopped)
|
|
12527
|
+
return;
|
|
12528
|
+
if (carry !== "" && lineNo >= offset && lineNo < hardEndLine) {
|
|
12529
|
+
if (!tryPushLine(carry)) {
|
|
11442
12530
|
finish();
|
|
11443
12531
|
return;
|
|
11444
12532
|
}
|
|
12533
|
+
lineNo++;
|
|
11445
12534
|
}
|
|
11446
|
-
|
|
11447
|
-
stream.on("end", () => {
|
|
11448
|
-
if (!stopped && carry !== "" && lineNo >= offset && lineNo < endLine)
|
|
11449
|
-
kept.push(carry);
|
|
12535
|
+
sawEof = true;
|
|
11450
12536
|
finish();
|
|
11451
12537
|
});
|
|
11452
12538
|
stream.on("error", (err) => {
|
|
@@ -11465,31 +12551,12 @@ ${numbered}` });
|
|
|
11465
12551
|
return { error: "Missing required parameter: path" };
|
|
11466
12552
|
try {
|
|
11467
12553
|
const resolved = resolvePath(filePath, workDir);
|
|
11468
|
-
|
|
12554
|
+
fs6.statSync(resolved);
|
|
11469
12555
|
if (isBinaryFile(resolved)) {
|
|
11470
12556
|
const ext = path5.extname(resolved).toLowerCase();
|
|
11471
12557
|
return { error: `Cannot read binary file: ${resolved} (${ext || "no extension"}). Use a text-based tool or convert it first.` };
|
|
11472
12558
|
}
|
|
11473
|
-
|
|
11474
|
-
const kb = (stat2.size / 1024).toFixed(0);
|
|
11475
|
-
if (limit <= 0) {
|
|
11476
|
-
return { error: `File too large (${kb} KB, max ${MAX_READ_BYTES / 1024} KB). Pass offset + limit to read a section (e.g. {offset:0, limit:500}), or search_files to locate the relevant part first.` };
|
|
11477
|
-
}
|
|
11478
|
-
return await readLargeFileWindow(resolved, offset, limit, kb);
|
|
11479
|
-
}
|
|
11480
|
-
const content = fs6.readFileSync(resolved, "utf-8");
|
|
11481
|
-
const allLines = content.split("\n");
|
|
11482
|
-
const totalLines = allLines.length;
|
|
11483
|
-
const startLine = offset;
|
|
11484
|
-
const endLine = limit > 0 ? Math.min(startLine + limit, totalLines) : totalLines;
|
|
11485
|
-
const slice = allLines.slice(startLine, endLine);
|
|
11486
|
-
const numbered = slice.map((l, i2) => {
|
|
11487
|
-
const lineNo = String(startLine + i2 + 1).padStart(4, " ");
|
|
11488
|
-
return `${lineNo} ${l}`;
|
|
11489
|
-
}).join("\n");
|
|
11490
|
-
const rangeNote = offset > 0 || limit > 0 ? ` lines ${startLine + 1}-${endLine}/${totalLines}` : ` ${totalLines} lines`;
|
|
11491
|
-
return { output: `[File: ${resolved} (${rangeNote})]
|
|
11492
|
-
${numbered}` };
|
|
12559
|
+
return await readFileWindowed(resolved, offset, limit);
|
|
11493
12560
|
} catch (err) {
|
|
11494
12561
|
return { error: err.message };
|
|
11495
12562
|
}
|
|
@@ -12025,7 +13092,9 @@ Update these call-sites (or restore the symbol), then run a build/typecheck to c
|
|
|
12025
13092
|
walkDir(resolved, (filePath) => {
|
|
12026
13093
|
const name = path5.basename(filePath);
|
|
12027
13094
|
const nameLower = name.toLowerCase();
|
|
12028
|
-
|
|
13095
|
+
const rel = path5.relative(resolved, filePath).replace(/\\/g, "/");
|
|
13096
|
+
const relLower = rel.toLowerCase();
|
|
13097
|
+
if (nameLower.includes(patternLower) || matchesGlob(name, pattern) || matchesPattern(name, pattern) || relLower.includes(patternLower) || matchesGlob(rel, pattern) || matchesPattern(rel, pattern)) {
|
|
12029
13098
|
matches.push(filePath);
|
|
12030
13099
|
}
|
|
12031
13100
|
});
|
|
@@ -12929,36 +13998,48 @@ ${lines.join("\n")}`;
|
|
|
12929
13998
|
return { output: `Found ${photos.length} photo(s) for "${query}":
|
|
12930
13999
|
${lines.join("\n")}` };
|
|
12931
14000
|
}
|
|
12932
|
-
|
|
12933
|
-
|
|
14001
|
+
function memoryScopeOf(input) {
|
|
14002
|
+
return input.scope === "global" ? "global" : "project";
|
|
14003
|
+
}
|
|
14004
|
+
async function memoryWrite(input, workDir) {
|
|
12934
14005
|
const content = typeof input.content === "string" ? input.content.trim() : "";
|
|
12935
14006
|
if (!content)
|
|
12936
14007
|
return { error: "content is required" };
|
|
12937
|
-
const
|
|
12938
|
-
|
|
12939
|
-
if (
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
}
|
|
12945
|
-
}
|
|
12946
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
12947
|
-
const entry = `
|
|
12948
|
-
- [${timestamp}] ${content}`;
|
|
12949
|
-
fs6.appendFileSync(MEMORY_FILE, entry, "utf-8");
|
|
12950
|
-
return { output: `Memory saved: ${content}` };
|
|
12951
|
-
}
|
|
12952
|
-
async function memoryRead(_input) {
|
|
14008
|
+
const scope = memoryScopeOf(input);
|
|
14009
|
+
const r2 = await (0, memory_1.writeMemory)(content, scope, workDir);
|
|
14010
|
+
if (!r2.ok)
|
|
14011
|
+
return { error: "failed to save memory" };
|
|
14012
|
+
return { output: r2.already ? `Memory already recorded (skipped duplicate): ${content}` : `Memory saved (${scope}): ${content}` };
|
|
14013
|
+
}
|
|
14014
|
+
async function memoryRead(input, workDir) {
|
|
12953
14015
|
try {
|
|
12954
|
-
if (
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
14016
|
+
if (input && (input.scope === "project" || input.scope === "global")) {
|
|
14017
|
+
const mem2 = (0, memory_1.readMemory)(input.scope, workDir);
|
|
14018
|
+
return { output: mem2 || "No memories saved yet." };
|
|
14019
|
+
}
|
|
14020
|
+
const mem = (0, memory_1.readAllMemory)(workDir);
|
|
14021
|
+
return { output: mem || "No memories saved yet." };
|
|
12958
14022
|
} catch (err) {
|
|
12959
14023
|
return { error: err.message };
|
|
12960
14024
|
}
|
|
12961
14025
|
}
|
|
14026
|
+
async function useSkill(input, workDir) {
|
|
14027
|
+
const name = typeof input.name === "string" ? input.name.trim() : "";
|
|
14028
|
+
if (!name)
|
|
14029
|
+
return { error: "name is required (the skill to invoke)" };
|
|
14030
|
+
const dir = workDir ?? process.cwd();
|
|
14031
|
+
const skills = (0, skills_1.loadSkills)(dir);
|
|
14032
|
+
const skill = (0, skills_1.findSkill)(skills, name);
|
|
14033
|
+
if (!skill) {
|
|
14034
|
+
const known = skills.map((s2) => s2.name).join(", ") || "(none defined)";
|
|
14035
|
+
return { error: `Unknown skill "${name}". Available: ${known}.` };
|
|
14036
|
+
}
|
|
14037
|
+
const args = typeof input.args === "string" ? input.args : "";
|
|
14038
|
+
const expanded = (0, skills_1.expandSkill)(skill, args, dir);
|
|
14039
|
+
return { output: `[Skill "${skill.name}" loaded \u2014 follow these instructions:]
|
|
14040
|
+
|
|
14041
|
+
${expanded}` };
|
|
14042
|
+
}
|
|
12962
14043
|
function semanticPreamble(input, workDir) {
|
|
12963
14044
|
const p = typeof input.path === "string" ? input.path : "";
|
|
12964
14045
|
const line = typeof input.line === "number" ? Math.floor(input.line) : 0;
|
|
@@ -13020,6 +14101,7 @@ ${lines.join("\n")}` };
|
|
|
13020
14101
|
notebook_edit: notebookEdit,
|
|
13021
14102
|
memory_write: memoryWrite,
|
|
13022
14103
|
memory_read: memoryRead,
|
|
14104
|
+
use_skill: useSkill,
|
|
13023
14105
|
bash_output: bashOutput,
|
|
13024
14106
|
kill_shell: killShell,
|
|
13025
14107
|
// LSP-lite fallback (regex-based). In VS Code these names are intercepted by
|
|
@@ -13049,172 +14131,31 @@ ${lines.join("\n")}` };
|
|
|
13049
14131
|
"generate_image",
|
|
13050
14132
|
"stock_photo",
|
|
13051
14133
|
"get_symbols",
|
|
13052
|
-
"get_workspace_symbols",
|
|
13053
|
-
"go_to_definition",
|
|
13054
|
-
"find_references",
|
|
13055
|
-
"get_hover"
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
return { error: `Unexpected error in tool ${name}: ${err.message}` };
|
|
13073
|
-
}
|
|
13074
|
-
}
|
|
13075
|
-
}
|
|
13076
|
-
});
|
|
13077
|
-
|
|
13078
|
-
// ../core/dist/plugins/index.js
|
|
13079
|
-
var require_plugins = __commonJS({
|
|
13080
|
-
"../core/dist/plugins/index.js"(exports2) {
|
|
13081
|
-
"use strict";
|
|
13082
|
-
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
13083
|
-
if (k2 === void 0)
|
|
13084
|
-
k2 = k;
|
|
13085
|
-
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
13086
|
-
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
13087
|
-
desc = { enumerable: true, get: function() {
|
|
13088
|
-
return m2[k];
|
|
13089
|
-
} };
|
|
13090
|
-
}
|
|
13091
|
-
Object.defineProperty(o, k2, desc);
|
|
13092
|
-
} : function(o, m2, k, k2) {
|
|
13093
|
-
if (k2 === void 0)
|
|
13094
|
-
k2 = k;
|
|
13095
|
-
o[k2] = m2[k];
|
|
13096
|
-
});
|
|
13097
|
-
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
13098
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
13099
|
-
} : function(o, v) {
|
|
13100
|
-
o["default"] = v;
|
|
13101
|
-
});
|
|
13102
|
-
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
13103
|
-
var ownKeys = function(o) {
|
|
13104
|
-
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
13105
|
-
var ar = [];
|
|
13106
|
-
for (var k in o2)
|
|
13107
|
-
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
13108
|
-
ar[ar.length] = k;
|
|
13109
|
-
return ar;
|
|
13110
|
-
};
|
|
13111
|
-
return ownKeys(o);
|
|
13112
|
-
};
|
|
13113
|
-
return function(mod) {
|
|
13114
|
-
if (mod && mod.__esModule)
|
|
13115
|
-
return mod;
|
|
13116
|
-
var result = {};
|
|
13117
|
-
if (mod != null) {
|
|
13118
|
-
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
13119
|
-
if (k[i2] !== "default")
|
|
13120
|
-
__createBinding(result, mod, k[i2]);
|
|
13121
|
-
}
|
|
13122
|
-
__setModuleDefault(result, mod);
|
|
13123
|
-
return result;
|
|
13124
|
-
};
|
|
13125
|
-
}();
|
|
13126
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
13127
|
-
exports2.loadPlugins = loadPlugins3;
|
|
13128
|
-
exports2.pluginAssetDirs = pluginAssetDirs;
|
|
13129
|
-
exports2.pluginHooks = pluginHooks;
|
|
13130
|
-
exports2.pluginMcpServers = pluginMcpServers;
|
|
13131
|
-
var fs6 = __importStar(require("fs"));
|
|
13132
|
-
var path5 = __importStar(require("path"));
|
|
13133
|
-
var os5 = __importStar(require("os"));
|
|
13134
|
-
function readMeta(dir) {
|
|
13135
|
-
try {
|
|
13136
|
-
const raw = fs6.readFileSync(path5.join(dir, "plugin.json"), "utf-8");
|
|
13137
|
-
const j = JSON.parse(raw);
|
|
13138
|
-
return {
|
|
13139
|
-
name: typeof j.name === "string" ? j.name : void 0,
|
|
13140
|
-
version: typeof j.version === "string" ? j.version : void 0,
|
|
13141
|
-
description: typeof j.description === "string" ? j.description : void 0
|
|
13142
|
-
};
|
|
13143
|
-
} catch {
|
|
13144
|
-
return {};
|
|
13145
|
-
}
|
|
13146
|
-
}
|
|
13147
|
-
function scanRoot(root, scope, into) {
|
|
13148
|
-
let entries;
|
|
13149
|
-
try {
|
|
13150
|
-
entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
13151
|
-
} catch {
|
|
13152
|
-
return;
|
|
13153
|
-
}
|
|
13154
|
-
for (const entry of entries) {
|
|
13155
|
-
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
13156
|
-
continue;
|
|
13157
|
-
const dir = path5.join(root, entry.name);
|
|
13158
|
-
try {
|
|
13159
|
-
if (!fs6.statSync(dir).isDirectory())
|
|
13160
|
-
continue;
|
|
13161
|
-
} catch {
|
|
13162
|
-
continue;
|
|
13163
|
-
}
|
|
13164
|
-
const meta = readMeta(dir);
|
|
13165
|
-
const name = meta.name || entry.name;
|
|
13166
|
-
if (into.has(name))
|
|
13167
|
-
continue;
|
|
13168
|
-
into.set(name, { name, version: meta.version, description: meta.description, dir, scope });
|
|
13169
|
-
}
|
|
13170
|
-
}
|
|
13171
|
-
function loadPlugins3(workDir) {
|
|
13172
|
-
const out = /* @__PURE__ */ new Map();
|
|
13173
|
-
scanRoot(path5.join(workDir, ".nexrall", "plugins"), "project", out);
|
|
13174
|
-
scanRoot(path5.join(os5.homedir(), ".nexrall", "plugins"), "global", out);
|
|
13175
|
-
return [...out.values()];
|
|
13176
|
-
}
|
|
13177
|
-
function pluginAssetDirs(workDir, kind) {
|
|
13178
|
-
return loadPlugins3(workDir).map((p) => path5.join(p.dir, kind)).filter((d) => {
|
|
13179
|
-
try {
|
|
13180
|
-
return fs6.statSync(d).isDirectory();
|
|
13181
|
-
} catch {
|
|
13182
|
-
return false;
|
|
13183
|
-
}
|
|
13184
|
-
});
|
|
13185
|
-
}
|
|
13186
|
-
function pluginHooks(workDir) {
|
|
13187
|
-
const merged = {};
|
|
13188
|
-
for (const p of loadPlugins3(workDir)) {
|
|
13189
|
-
try {
|
|
13190
|
-
const raw = fs6.readFileSync(path5.join(p.dir, "hooks.json"), "utf-8");
|
|
13191
|
-
const j = JSON.parse(raw);
|
|
13192
|
-
const hooks = j.hooks ?? j;
|
|
13193
|
-
for (const [phase, entries] of Object.entries(hooks)) {
|
|
13194
|
-
if (!Array.isArray(entries))
|
|
13195
|
-
continue;
|
|
13196
|
-
merged[phase] = [...merged[phase] ?? [], ...entries];
|
|
13197
|
-
}
|
|
13198
|
-
} catch {
|
|
13199
|
-
}
|
|
13200
|
-
}
|
|
13201
|
-
return merged;
|
|
13202
|
-
}
|
|
13203
|
-
function pluginMcpServers(workDir) {
|
|
13204
|
-
const merged = {};
|
|
13205
|
-
for (const p of loadPlugins3(workDir)) {
|
|
13206
|
-
try {
|
|
13207
|
-
const raw = fs6.readFileSync(path5.join(p.dir, "mcp.json"), "utf-8");
|
|
13208
|
-
const j = JSON.parse(raw);
|
|
13209
|
-
const servers = j.mcpServers ?? j;
|
|
13210
|
-
for (const [name, cfg] of Object.entries(servers)) {
|
|
13211
|
-
if (!(name in merged))
|
|
13212
|
-
merged[name] = cfg;
|
|
13213
|
-
}
|
|
13214
|
-
} catch {
|
|
14134
|
+
"get_workspace_symbols",
|
|
14135
|
+
"go_to_definition",
|
|
14136
|
+
"find_references",
|
|
14137
|
+
"get_hover",
|
|
14138
|
+
"memory_write",
|
|
14139
|
+
"memory_read",
|
|
14140
|
+
"use_skill"
|
|
14141
|
+
]);
|
|
14142
|
+
async function executeTool(name, input, abortSignal, sandbox, workDir, agentScope) {
|
|
14143
|
+
if (!TOOL_MAP[name])
|
|
14144
|
+
return { error: `Unknown tool: ${name}` };
|
|
14145
|
+
try {
|
|
14146
|
+
if (name === "bash")
|
|
14147
|
+
return await bash(input, abortSignal, sandbox, workDir);
|
|
14148
|
+
if (name === "todo_write")
|
|
14149
|
+
return await todoWrite(input, agentScope);
|
|
14150
|
+
if (name === "todo_read")
|
|
14151
|
+
return await todoRead(input, agentScope);
|
|
14152
|
+
if (WORKDIR_TOOLS.has(name)) {
|
|
14153
|
+
return await TOOL_MAP[name](input, workDir);
|
|
13215
14154
|
}
|
|
14155
|
+
return await TOOL_MAP[name](input);
|
|
14156
|
+
} catch (err) {
|
|
14157
|
+
return { error: `Unexpected error in tool ${name}: ${err.message}` };
|
|
13216
14158
|
}
|
|
13217
|
-
return merged;
|
|
13218
14159
|
}
|
|
13219
14160
|
}
|
|
13220
14161
|
});
|
|
@@ -13778,15 +14719,19 @@ var require_loop = __commonJS({
|
|
|
13778
14719
|
exports2.estimateTokensRough = estimateTokensRough;
|
|
13779
14720
|
exports2.compactMessagesForResume = compactMessagesForResume2;
|
|
13780
14721
|
exports2.runAgentLoop = runAgentLoop2;
|
|
14722
|
+
exports2.trimToResumableBoundary = trimToResumableBoundary;
|
|
14723
|
+
var types_1 = require_types();
|
|
13781
14724
|
var client_1 = require_client();
|
|
13782
14725
|
var executor_1 = require_executor();
|
|
13783
14726
|
var agentTypes_1 = require_agentTypes();
|
|
14727
|
+
var skills_1 = require_skills();
|
|
13784
14728
|
var rules_1 = require_rules();
|
|
13785
14729
|
var sandbox_1 = require_sandbox();
|
|
13786
14730
|
var index_1 = require_plugins();
|
|
13787
14731
|
var testIntegrity_1 = require_testIntegrity();
|
|
13788
14732
|
var flaky_1 = require_flaky();
|
|
13789
14733
|
var claimEvidence_1 = require_claimEvidence();
|
|
14734
|
+
var memory_1 = require_memory();
|
|
13790
14735
|
var fs6 = __importStar(require("fs"));
|
|
13791
14736
|
var path5 = __importStar(require("path"));
|
|
13792
14737
|
var child_process_1 = require("child_process");
|
|
@@ -13985,6 +14930,8 @@ var require_loop = __commonJS({
|
|
|
13985
14930
|
const preview = typeof input.prompt === "string" ? input.prompt.slice(0, 60) : "";
|
|
13986
14931
|
return `Sub-task: ${desc || preview}${!desc && preview.length === 60 ? "\u2026" : ""}`;
|
|
13987
14932
|
}
|
|
14933
|
+
case "use_skill":
|
|
14934
|
+
return `Use skill: /${input.name ?? "(unknown)"}`;
|
|
13988
14935
|
case "get_diagnostics":
|
|
13989
14936
|
return input.path ? `Get diagnostics: ${input.path}` : "Get workspace diagnostics";
|
|
13990
14937
|
case "go_to_definition":
|
|
@@ -14048,6 +14995,22 @@ ${options.nexrallMd}` : "") : options.nexrallMd;
|
|
|
14048
14995
|
onText: () => {
|
|
14049
14996
|
},
|
|
14050
14997
|
// sub-agent text is returned as the tool result, not streamed live
|
|
14998
|
+
// Forwarded DELIBERATELY, and it must be a real handler rather than a no-op.
|
|
14999
|
+
//
|
|
15000
|
+
// A sub-agent streams no text (onText above is a no-op) but it DOES stream thinking
|
|
15001
|
+
// through the parent's UI (see onThinking/onThinkingDelta below), and thinking sets
|
|
15002
|
+
// `emittedToCaller`. So a sub-agent stream that dies after reasoning genuinely has
|
|
15003
|
+
// rendered output to discard — a no-op here would let the restart proceed and then
|
|
15004
|
+
// re-stream that reasoning on top of the copy still on screen, the exact duplication
|
|
15005
|
+
// the opt-in exists to prevent.
|
|
15006
|
+
//
|
|
15007
|
+
// Forwarding is safe because the parent's own output is already closed by this
|
|
15008
|
+
// point: dispatching the `task` tool goes through options.onToolUse, which finalizes
|
|
15009
|
+
// the parent's bubble (VS Code) / flushes the renderer (CLI) before the sub-agent
|
|
15010
|
+
// starts. The only live, discardable element at restart time is the sub-agent's own
|
|
15011
|
+
// thinking block. Completed tool rows are left alone — those are real side effects
|
|
15012
|
+
// that actually happened.
|
|
15013
|
+
onStreamRestart: (reason, chars) => options.onStreamRestart?.(reason, chars),
|
|
14051
15014
|
// Forward tool events with isSubTask=true so the UI can render a badge
|
|
14052
15015
|
// instead of prepending "[sub-task]" to the tool name (which caused double-prefix
|
|
14053
15016
|
// when the name was already labelled, and mixed display concerns into the data layer).
|
|
@@ -14301,7 +15264,12 @@ ${tail}`;
|
|
|
14301
15264
|
// summariser must not call tools; ask-mode discourages action
|
|
14302
15265
|
env: options.env,
|
|
14303
15266
|
clientType: options.clientType,
|
|
14304
|
-
abortSignal: options.abortSignal
|
|
15267
|
+
abortSignal: options.abortSignal,
|
|
15268
|
+
// The summariser renders NOTHING (onEvent below is a no-op) and its result is
|
|
15269
|
+
// read only from the returned message, so a restart has nothing to roll back —
|
|
15270
|
+
// always safe. Worth enabling: a blip here used to abandon compaction entirely,
|
|
15271
|
+
// which then let the very next turn hit the context wall it was meant to prevent.
|
|
15272
|
+
allowRestartAfterRender: true
|
|
14305
15273
|
}, () => {
|
|
14306
15274
|
});
|
|
14307
15275
|
summary = reply.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("").trim();
|
|
@@ -14327,6 +15295,28 @@ ${summary}
|
|
|
14327
15295
|
Continue the work from here.` }] });
|
|
14328
15296
|
return true;
|
|
14329
15297
|
}
|
|
15298
|
+
async function maybeCompactMemory(scope, options) {
|
|
15299
|
+
try {
|
|
15300
|
+
await (0, memory_1.compactMemoryIfNeeded)(scope, options.workDir, async (prompt2) => {
|
|
15301
|
+
const reply = await (0, client_1.streamChat)([{ role: "user", content: [{ type: "text", text: prompt2 }] }], {
|
|
15302
|
+
// Same reasoning as autoCompactMessages' summariser: a mechanical
|
|
15303
|
+
// consolidation task, always run on the cheapest 1M-context tier
|
|
15304
|
+
// regardless of the user's chosen model for the actual conversation.
|
|
15305
|
+
model: "turbo",
|
|
15306
|
+
mode: "ask",
|
|
15307
|
+
env: options.env,
|
|
15308
|
+
clientType: options.clientType,
|
|
15309
|
+
abortSignal: options.abortSignal,
|
|
15310
|
+
// Same as the transcript summariser: no rendered output, result read only from
|
|
15311
|
+
// the returned message, so restarting on a blip is always safe.
|
|
15312
|
+
allowRestartAfterRender: true
|
|
15313
|
+
}, () => {
|
|
15314
|
+
});
|
|
15315
|
+
return reply.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
15316
|
+
});
|
|
15317
|
+
} catch {
|
|
15318
|
+
}
|
|
15319
|
+
}
|
|
14330
15320
|
var RESUME_CHARS_PER_TOKEN = 4;
|
|
14331
15321
|
function estimateTokensRough(messages) {
|
|
14332
15322
|
return Math.ceil(estimateBodyBytes(messages) / RESUME_CHARS_PER_TOKEN);
|
|
@@ -14395,6 +15385,7 @@ Continue the work from here.` }] });
|
|
|
14395
15385
|
const agentScope = options._agentScope ?? "root";
|
|
14396
15386
|
const agentTypes = (0, agentTypes_1.loadAgentTypes)(options.workDir);
|
|
14397
15387
|
const agentsCatalogue = depth === 0 ? (0, agentTypes_1.summariseAgents)(agentTypes) : "";
|
|
15388
|
+
const skillsCatalogue = (0, skills_1.summariseSkills)((0, skills_1.loadSkills)(options.workDir));
|
|
14398
15389
|
const settings = (0, rules_1.loadSettings)(options.workDir);
|
|
14399
15390
|
const sandboxCfg = (0, sandbox_1.parseSandboxConfig)(settings.raw.sandbox) ?? void 0;
|
|
14400
15391
|
const maxIterations = resolveMaxIterations(options.maxIterations, settings.raw);
|
|
@@ -14406,6 +15397,7 @@ Continue the work from here.` }] });
|
|
|
14406
15397
|
let compacting = false;
|
|
14407
15398
|
const hardCap = autoContinue ? Math.max(maxIterations, MAX_ITERATIONS_CEILING) : maxIterations;
|
|
14408
15399
|
let completedCleanly = false;
|
|
15400
|
+
let completedRounds = 0;
|
|
14409
15401
|
let stalledOut = false;
|
|
14410
15402
|
let consecutiveErrorRounds = 0;
|
|
14411
15403
|
let budget = maxIterations;
|
|
@@ -14451,7 +15443,6 @@ Continue the work from here.` }] });
|
|
|
14451
15443
|
\u26A0\uFE0F Approaching the ${budget}-step limit (step ${iteration + 1}). Please wrap up and summarise what has been done.
|
|
14452
15444
|
`);
|
|
14453
15445
|
}
|
|
14454
|
-
const pendingToolUse = [];
|
|
14455
15446
|
const onEvent = (event) => {
|
|
14456
15447
|
switch (event.type) {
|
|
14457
15448
|
case "text":
|
|
@@ -14467,16 +15458,12 @@ Continue the work from here.` }] });
|
|
|
14467
15458
|
options.onThinkingProgress?.(event.tokens);
|
|
14468
15459
|
break;
|
|
14469
15460
|
case "tool_use":
|
|
14470
|
-
pendingToolUse.push({
|
|
14471
|
-
type: "tool_use",
|
|
14472
|
-
id: event.id,
|
|
14473
|
-
name: event.name,
|
|
14474
|
-
input: event.input
|
|
14475
|
-
});
|
|
14476
15461
|
break;
|
|
14477
15462
|
case "usage":
|
|
14478
|
-
|
|
14479
|
-
|
|
15463
|
+
if (!event.partial) {
|
|
15464
|
+
lastPromptTokens = (event.usage.input_tokens ?? 0) + (event.usage.cache_creation_input_tokens ?? 0) + (event.usage.cache_read_input_tokens ?? 0);
|
|
15465
|
+
}
|
|
15466
|
+
options.onUsage(event.usage, event.partial === true || event.replayed === true);
|
|
14480
15467
|
break;
|
|
14481
15468
|
case "message_complete":
|
|
14482
15469
|
break;
|
|
@@ -14486,11 +15473,16 @@ Continue the work from here.` }] });
|
|
|
14486
15473
|
case "retry_resolved":
|
|
14487
15474
|
options.onRetryResolved?.();
|
|
14488
15475
|
break;
|
|
15476
|
+
case "stream_restart":
|
|
15477
|
+
options.onStreamRestart?.(event.reason, event.discardedChars);
|
|
15478
|
+
break;
|
|
14489
15479
|
case "balance_status":
|
|
14490
15480
|
options.onBalanceStatus?.(event.balance, event.zero);
|
|
14491
15481
|
break;
|
|
14492
15482
|
case "done":
|
|
15483
|
+
break;
|
|
14493
15484
|
case "error":
|
|
15485
|
+
options.onNotice?.(`\u26A0\uFE0F ${event.message}`);
|
|
14494
15486
|
break;
|
|
14495
15487
|
}
|
|
14496
15488
|
};
|
|
@@ -14506,7 +15498,13 @@ Continue the work from here.` }] });
|
|
|
14506
15498
|
clientType: options.clientType,
|
|
14507
15499
|
abortSignal: options.abortSignal,
|
|
14508
15500
|
extraTools: options.mcpManager?.getAnthropicTools(),
|
|
14509
|
-
agents: agentsCatalogue || void 0
|
|
15501
|
+
agents: agentsCatalogue || void 0,
|
|
15502
|
+
skills: skillsCatalogue || void 0,
|
|
15503
|
+
// Only allow a post-render restart when the caller actually implements the
|
|
15504
|
+
// rollback. Without a handler the partial output can't be un-rendered, so we
|
|
15505
|
+
// keep the old conservative behaviour (fail the turn) rather than duplicate
|
|
15506
|
+
// text on screen.
|
|
15507
|
+
allowRestartAfterRender: !!options.onStreamRestart
|
|
14510
15508
|
}, onEvent);
|
|
14511
15509
|
} catch (err) {
|
|
14512
15510
|
if (options.abortSignal?.aborted || err.name === "AbortError")
|
|
@@ -14518,7 +15516,7 @@ Continue the work from here.` }] });
|
|
|
14518
15516
|
break;
|
|
14519
15517
|
}
|
|
14520
15518
|
runSimpleHooks(hooks.OnError, options.workDir);
|
|
14521
|
-
throw new
|
|
15519
|
+
throw new types_1.AgentTurnError(`Stream failed: ${err.message}`, trimToResumableBoundary(messages), completedRounds, err);
|
|
14522
15520
|
}
|
|
14523
15521
|
if (options.abortSignal?.aborted)
|
|
14524
15522
|
break;
|
|
@@ -14653,6 +15651,10 @@ Continue the work from here.` }] });
|
|
|
14653
15651
|
} catch (err) {
|
|
14654
15652
|
result = { error: `Tool execution failed: ${err.message}` };
|
|
14655
15653
|
}
|
|
15654
|
+
if (name === "memory_write" && result.error === void 0) {
|
|
15655
|
+
const scope = input.scope === "global" ? "global" : "project";
|
|
15656
|
+
void maybeCompactMemory(scope, options);
|
|
15657
|
+
}
|
|
14656
15658
|
const post = runToolHooks(hooks.PostToolUse, "PostToolUse", name, input, options.workDir, result);
|
|
14657
15659
|
const injected = [pre.context, post.context].filter(Boolean).join("\n");
|
|
14658
15660
|
if (injected) {
|
|
@@ -14714,6 +15716,7 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
14714
15716
|
content: toolResultContent
|
|
14715
15717
|
};
|
|
14716
15718
|
messages.push(toolResultMessage);
|
|
15719
|
+
completedRounds++;
|
|
14717
15720
|
options.onProgress?.(messages);
|
|
14718
15721
|
const allErrored = toolResults.length > 0 && toolResults.every(({ result }) => result.error !== void 0);
|
|
14719
15722
|
consecutiveErrorRounds = allErrored ? consecutiveErrorRounds + 1 : 0;
|
|
@@ -14739,11 +15742,32 @@ ${result.output}` : `Error: ${result.error}` : result.output ?? "",
|
|
|
14739
15742
|
`);
|
|
14740
15743
|
}
|
|
14741
15744
|
}
|
|
15745
|
+
} catch (err) {
|
|
15746
|
+
if (err instanceof types_1.AgentTurnError)
|
|
15747
|
+
throw err;
|
|
15748
|
+
if (options.abortSignal?.aborted || err.name === "AbortError")
|
|
15749
|
+
throw err;
|
|
15750
|
+
throw new types_1.AgentTurnError(err?.message || "The turn ended unexpectedly", trimToResumableBoundary(messages), completedRounds, err);
|
|
14742
15751
|
} finally {
|
|
14743
15752
|
runSimpleHooks(hooks.OnStop, options.workDir);
|
|
14744
15753
|
}
|
|
14745
15754
|
return messages;
|
|
14746
15755
|
}
|
|
15756
|
+
function trimToResumableBoundary(messages) {
|
|
15757
|
+
const isUnansweredInvocation = (m2) => {
|
|
15758
|
+
if (m2?.role !== "assistant" || !Array.isArray(m2.content))
|
|
15759
|
+
return false;
|
|
15760
|
+
const blocks = m2.content;
|
|
15761
|
+
if (blocks.some((b) => b?.type === "tool_use"))
|
|
15762
|
+
return true;
|
|
15763
|
+
const answeredIds = new Set(blocks.filter((b) => /_tool_result$/.test(b?.type ?? "")).map((b) => b?.tool_use_id));
|
|
15764
|
+
return blocks.some((b) => b?.type === "server_tool_use" && !answeredIds.has(b?.id));
|
|
15765
|
+
};
|
|
15766
|
+
let end = messages.length;
|
|
15767
|
+
while (end > 0 && isUnansweredInvocation(messages[end - 1]))
|
|
15768
|
+
end--;
|
|
15769
|
+
return end === messages.length ? messages : messages.slice(0, end);
|
|
15770
|
+
}
|
|
14747
15771
|
}
|
|
14748
15772
|
});
|
|
14749
15773
|
|
|
@@ -15842,193 +16866,6 @@ var require_manager2 = __commonJS({
|
|
|
15842
16866
|
}
|
|
15843
16867
|
});
|
|
15844
16868
|
|
|
15845
|
-
// ../core/dist/commands/loader.js
|
|
15846
|
-
var require_loader = __commonJS({
|
|
15847
|
-
"../core/dist/commands/loader.js"(exports2) {
|
|
15848
|
-
"use strict";
|
|
15849
|
-
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
15850
|
-
if (k2 === void 0)
|
|
15851
|
-
k2 = k;
|
|
15852
|
-
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
15853
|
-
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
15854
|
-
desc = { enumerable: true, get: function() {
|
|
15855
|
-
return m2[k];
|
|
15856
|
-
} };
|
|
15857
|
-
}
|
|
15858
|
-
Object.defineProperty(o, k2, desc);
|
|
15859
|
-
} : function(o, m2, k, k2) {
|
|
15860
|
-
if (k2 === void 0)
|
|
15861
|
-
k2 = k;
|
|
15862
|
-
o[k2] = m2[k];
|
|
15863
|
-
});
|
|
15864
|
-
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
15865
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15866
|
-
} : function(o, v) {
|
|
15867
|
-
o["default"] = v;
|
|
15868
|
-
});
|
|
15869
|
-
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
15870
|
-
var ownKeys = function(o) {
|
|
15871
|
-
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
15872
|
-
var ar = [];
|
|
15873
|
-
for (var k in o2)
|
|
15874
|
-
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
15875
|
-
ar[ar.length] = k;
|
|
15876
|
-
return ar;
|
|
15877
|
-
};
|
|
15878
|
-
return ownKeys(o);
|
|
15879
|
-
};
|
|
15880
|
-
return function(mod) {
|
|
15881
|
-
if (mod && mod.__esModule)
|
|
15882
|
-
return mod;
|
|
15883
|
-
var result = {};
|
|
15884
|
-
if (mod != null) {
|
|
15885
|
-
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
15886
|
-
if (k[i2] !== "default")
|
|
15887
|
-
__createBinding(result, mod, k[i2]);
|
|
15888
|
-
}
|
|
15889
|
-
__setModuleDefault(result, mod);
|
|
15890
|
-
return result;
|
|
15891
|
-
};
|
|
15892
|
-
}();
|
|
15893
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
15894
|
-
exports2.loadSlashCommands = loadSlashCommands2;
|
|
15895
|
-
exports2.findSlashCommand = findSlashCommand2;
|
|
15896
|
-
exports2.expandCommand = expandCommand2;
|
|
15897
|
-
var fs6 = __importStar(require("fs"));
|
|
15898
|
-
var path5 = __importStar(require("path"));
|
|
15899
|
-
var os5 = __importStar(require("os"));
|
|
15900
|
-
var child_process_1 = require("child_process");
|
|
15901
|
-
var index_1 = require_plugins();
|
|
15902
|
-
var BUILTIN_COMMANDS = [
|
|
15903
|
-
{
|
|
15904
|
-
name: "review",
|
|
15905
|
-
description: "Review uncommitted changes (or a PR/branch diff) for bugs and risks",
|
|
15906
|
-
source: "builtin",
|
|
15907
|
-
body: [
|
|
15908
|
-
"Review the following diff like a meticulous senior engineer. Target: $ARGUMENTS",
|
|
15909
|
-
"(If no target given, review the uncommitted working-tree changes below. If a branch or PR",
|
|
15910
|
-
"number is given, run the appropriate `git diff <base>...` or `gh pr diff <n>` yourself first.)",
|
|
15911
|
-
"",
|
|
15912
|
-
"Branch: !`git branch --show-current`",
|
|
15913
|
-
"Status: !`git status --short`",
|
|
15914
|
-
"",
|
|
15915
|
-
"Diff (uncommitted):",
|
|
15916
|
-
"```diff",
|
|
15917
|
-
"!`git diff HEAD --unified=5 --no-color | head -4000`",
|
|
15918
|
-
"```",
|
|
15919
|
-
"",
|
|
15920
|
-
"Review methodology:",
|
|
15921
|
-
"1. Read the surrounding code of every changed hunk (read_file with offset/limit) \u2014 never judge a hunk in isolation.",
|
|
15922
|
-
"2. Look for: correctness bugs, edge cases (empty/null/unicode/concurrency), security issues",
|
|
15923
|
-
" (injection, path traversal, secrets), breaking API changes (find_references / search callers),",
|
|
15924
|
-
" silent behaviour changes, and missing error handling.",
|
|
15925
|
-
"3. Check tests: do existing tests cover the change? Are assertions weakened?",
|
|
15926
|
-
"",
|
|
15927
|
-
"Output format:",
|
|
15928
|
-
"- \u{1F534} Critical (must fix before merge) \u2014 with file:line and a concrete fix",
|
|
15929
|
-
"- \u{1F7E1} Warning (should fix) \u2014 with file:line",
|
|
15930
|
-
"- \u{1F7E2} Suggestion (nice to have)",
|
|
15931
|
-
"- Verdict: APPROVE / REQUEST CHANGES with a one-paragraph summary.",
|
|
15932
|
-
"Do NOT modify any files \u2014 this is a read-only review."
|
|
15933
|
-
].join("\n")
|
|
15934
|
-
}
|
|
15935
|
-
];
|
|
15936
|
-
function parseFrontmatter(raw) {
|
|
15937
|
-
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
15938
|
-
if (!m2)
|
|
15939
|
-
return { meta: {}, body: raw.trim() };
|
|
15940
|
-
const meta = {};
|
|
15941
|
-
for (const line of m2[1].split(/\r?\n/)) {
|
|
15942
|
-
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
15943
|
-
if (kv)
|
|
15944
|
-
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
|
|
15945
|
-
}
|
|
15946
|
-
return { meta, body: (m2[2] ?? "").trim() };
|
|
15947
|
-
}
|
|
15948
|
-
function loadDir(dir, source, into) {
|
|
15949
|
-
let files;
|
|
15950
|
-
try {
|
|
15951
|
-
files = fs6.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
15952
|
-
} catch {
|
|
15953
|
-
return;
|
|
15954
|
-
}
|
|
15955
|
-
for (const file of files) {
|
|
15956
|
-
try {
|
|
15957
|
-
const raw = fs6.readFileSync(path5.join(dir, file), "utf-8");
|
|
15958
|
-
const { meta, body } = parseFrontmatter(raw);
|
|
15959
|
-
const name = (meta.name || path5.basename(file, ".md")).trim().toLowerCase();
|
|
15960
|
-
if (!name)
|
|
15961
|
-
continue;
|
|
15962
|
-
if (source !== "project" && into.has(name))
|
|
15963
|
-
continue;
|
|
15964
|
-
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
15965
|
-
into.set(name, {
|
|
15966
|
-
name,
|
|
15967
|
-
description: meta.description || `Custom /${name} command`,
|
|
15968
|
-
model,
|
|
15969
|
-
mode: meta.mode || void 0,
|
|
15970
|
-
body,
|
|
15971
|
-
source
|
|
15972
|
-
});
|
|
15973
|
-
} catch {
|
|
15974
|
-
}
|
|
15975
|
-
}
|
|
15976
|
-
}
|
|
15977
|
-
function loadSlashCommands2(workDir) {
|
|
15978
|
-
const out = /* @__PURE__ */ new Map();
|
|
15979
|
-
loadDir(path5.join(workDir, ".nexrall", "commands"), "project", out);
|
|
15980
|
-
loadDir(path5.join(os5.homedir(), ".nexrall", "commands"), "global", out);
|
|
15981
|
-
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
15982
|
-
loadDir(dir, "plugin", out);
|
|
15983
|
-
for (const cmd of BUILTIN_COMMANDS) {
|
|
15984
|
-
if (!out.has(cmd.name))
|
|
15985
|
-
out.set(cmd.name, cmd);
|
|
15986
|
-
}
|
|
15987
|
-
return [...out.values()];
|
|
15988
|
-
}
|
|
15989
|
-
function findSlashCommand2(cmds, name) {
|
|
15990
|
-
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
15991
|
-
return cmds.find((c) => c.name === want);
|
|
15992
|
-
}
|
|
15993
|
-
function expandCommand2(cmd, argString, workDir) {
|
|
15994
|
-
const args = argString.trim();
|
|
15995
|
-
const positional = args ? args.split(/\s+/) : [];
|
|
15996
|
-
let out = cmd.body;
|
|
15997
|
-
out = out.replace(/!`([^`]+)`/g, (_m, c) => {
|
|
15998
|
-
try {
|
|
15999
|
-
const stdout = (0, child_process_1.execSync)(c, { cwd: workDir, encoding: "utf-8", timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] });
|
|
16000
|
-
return stdout.trim();
|
|
16001
|
-
} catch (err) {
|
|
16002
|
-
return `[command failed: ${c} \u2014 ${err.message}]`;
|
|
16003
|
-
}
|
|
16004
|
-
});
|
|
16005
|
-
out = out.replace(/(^|\s)@([^\s]+)/g, (_m, lead, rel) => {
|
|
16006
|
-
const abs = path5.isAbsolute(rel) ? rel : path5.join(workDir, rel);
|
|
16007
|
-
try {
|
|
16008
|
-
const content = fs6.readFileSync(abs, "utf-8").slice(0, 12e3);
|
|
16009
|
-
return `${lead}
|
|
16010
|
-
[File: ${rel}]
|
|
16011
|
-
\`\`\`
|
|
16012
|
-
${content}
|
|
16013
|
-
\`\`\`
|
|
16014
|
-
`;
|
|
16015
|
-
} catch {
|
|
16016
|
-
return `${lead}[missing file: ${rel}]`;
|
|
16017
|
-
}
|
|
16018
|
-
});
|
|
16019
|
-
out = out.replace(/\$(\d+)/g, (_m, n) => positional[Number(n) - 1] ?? "");
|
|
16020
|
-
const hadArgsToken = /\$ARGUMENTS/.test(out);
|
|
16021
|
-
out = out.replace(/\$ARGUMENTS/g, args);
|
|
16022
|
-
if (!hadArgsToken && !/\$\d+/.test(cmd.body) && args) {
|
|
16023
|
-
out = `${out}
|
|
16024
|
-
|
|
16025
|
-
${args}`;
|
|
16026
|
-
}
|
|
16027
|
-
return out.trim();
|
|
16028
|
-
}
|
|
16029
|
-
}
|
|
16030
|
-
});
|
|
16031
|
-
|
|
16032
16869
|
// ../core/dist/permissions/destructive.js
|
|
16033
16870
|
var require_destructive = __commonJS({
|
|
16034
16871
|
"../core/dist/permissions/destructive.js"(exports2) {
|
|
@@ -16575,6 +17412,8 @@ var require_dist2 = __commonJS({
|
|
|
16575
17412
|
__exportStar(require_crossFile(), exports2);
|
|
16576
17413
|
__exportStar(require_flaky(), exports2);
|
|
16577
17414
|
__exportStar(require_claimEvidence(), exports2);
|
|
17415
|
+
__exportStar(require_memory(), exports2);
|
|
17416
|
+
__exportStar(require_skills(), exports2);
|
|
16578
17417
|
__exportStar(require_client2(), exports2);
|
|
16579
17418
|
__exportStar(require_httpClient(), exports2);
|
|
16580
17419
|
__exportStar(require_manager(), exports2);
|
|
@@ -22360,6 +23199,25 @@ var MarkdownStreamRenderer = class {
|
|
|
22360
23199
|
for (const line of parts)
|
|
22361
23200
|
this._line(line);
|
|
22362
23201
|
}
|
|
23202
|
+
/**
|
|
23203
|
+
* Drop all buffered/partial state without emitting it.
|
|
23204
|
+
*
|
|
23205
|
+
* Used when a stream died mid-render and the same turn is being restarted: the
|
|
23206
|
+
* replacement attempt re-sends the response from the beginning, so any half-parsed
|
|
23207
|
+
* line, unterminated code fence or partially accumulated table from the dead attempt
|
|
23208
|
+
* must be discarded. Flushing instead would print a fragment and then leave the
|
|
23209
|
+
* renderer in `_inCode`/`_tbl` state that corrupts everything the retry prints.
|
|
23210
|
+
*
|
|
23211
|
+
* Note this only resets the PARSER — characters already written to stdout have
|
|
23212
|
+
* scrolled away and cannot be unprinted; the caller prints a visible restart marker
|
|
23213
|
+
* so the duplicated prefix is explained rather than silently confusing.
|
|
23214
|
+
*/
|
|
23215
|
+
reset() {
|
|
23216
|
+
this._buf = "";
|
|
23217
|
+
this._inCode = false;
|
|
23218
|
+
this._codeLang = "";
|
|
23219
|
+
this._tbl = [];
|
|
23220
|
+
}
|
|
22363
23221
|
flush() {
|
|
22364
23222
|
if (this._buf) {
|
|
22365
23223
|
this._line(this._buf);
|
|
@@ -23122,6 +23980,7 @@ async function requestPermission(req) {
|
|
|
23122
23980
|
"todo_write",
|
|
23123
23981
|
"memory_read",
|
|
23124
23982
|
"memory_write",
|
|
23983
|
+
"use_skill",
|
|
23125
23984
|
"notebook_read",
|
|
23126
23985
|
"get_diagnostics",
|
|
23127
23986
|
"go_to_definition",
|
|
@@ -23582,10 +24441,9 @@ var tryRead = (p) => {
|
|
|
23582
24441
|
};
|
|
23583
24442
|
function readNexrallMd(workDir) {
|
|
23584
24443
|
const parts = [];
|
|
23585
|
-
const memContent =
|
|
24444
|
+
const memContent = (0, import_code_core3.readAllMemory)(workDir);
|
|
23586
24445
|
if (memContent)
|
|
23587
|
-
parts.push(
|
|
23588
|
-
${memContent}`);
|
|
24446
|
+
parts.push(memContent);
|
|
23589
24447
|
const globalContent = tryRead(path3.join(os4.homedir(), ".nexrall", "nexrall.md"));
|
|
23590
24448
|
if (globalContent)
|
|
23591
24449
|
parts.push(`[Global instructions]
|
|
@@ -23685,8 +24543,12 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env2, nexrall
|
|
|
23685
24543
|
console.log(formatToolResult(lastToolName, res, durationMs));
|
|
23686
24544
|
toolStartTime = 0;
|
|
23687
24545
|
},
|
|
23688
|
-
|
|
23689
|
-
|
|
24546
|
+
// Ignore a `partial` report: it belongs to a cut-short attempt that was restarted,
|
|
24547
|
+
// and the replacement attempt reports the turn's real totals. Letting it through
|
|
24548
|
+
// would print a token count for output the user never saw.
|
|
24549
|
+
onUsage: (u, partial) => {
|
|
24550
|
+
if (!partial)
|
|
24551
|
+
lastUsage = u;
|
|
23690
24552
|
},
|
|
23691
24553
|
// A transient disconnect (network drop, machine sleep/wake, overloaded upstream)
|
|
23692
24554
|
// is retried transparently by the network layer — without this, that pause was
|
|
@@ -23700,6 +24562,27 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env2, nexrall
|
|
|
23700
24562
|
onRetryResolved: () => {
|
|
23701
24563
|
spinner.stop();
|
|
23702
24564
|
},
|
|
24565
|
+
// The stream died after part of the answer had already been printed, and the
|
|
24566
|
+
// turn is being restarted from the top. Providing this handler is what OPTS US
|
|
24567
|
+
// IN to post-render restarts at all (see AgentLoopOptions.onStreamRestart) —
|
|
24568
|
+
// without it a mid-answer disconnect kills the whole turn.
|
|
24569
|
+
//
|
|
24570
|
+
// A terminal can't unprint scrolled-away output, so instead of pretending the
|
|
24571
|
+
// fragment never happened we (a) reset the markdown parser so the dead attempt's
|
|
24572
|
+
// half-open code fence/table can't corrupt everything the retry prints, and
|
|
24573
|
+
// (b) draw an explicit marker so the user understands why the answer restarts.
|
|
24574
|
+
onStreamRestart: (reason, discardedChars) => {
|
|
24575
|
+
if (abortSignal.aborted)
|
|
24576
|
+
return;
|
|
24577
|
+
spinner.stop();
|
|
24578
|
+
mdRender.reset();
|
|
24579
|
+
if (discardedChars > 0) {
|
|
24580
|
+
console.log(
|
|
24581
|
+
"\n" + source_default.yellow(" \u21BA Connection dropped mid-answer \u2014 restarting this response.") + source_default.dim(`
|
|
24582
|
+
(${reason}. The ${discardedChars} characters above are incomplete; the full answer follows.)`) + "\n"
|
|
24583
|
+
);
|
|
24584
|
+
}
|
|
24585
|
+
},
|
|
23703
24586
|
onBalanceStatus: (balance, zero) => {
|
|
23704
24587
|
spinner.stop();
|
|
23705
24588
|
mdRender.flush();
|
|
@@ -23751,13 +24634,24 @@ async function runTurnHeadless(messages, modelAlias, workDir, _abortSignal, env2
|
|
|
23751
24634
|
toolCallCount++;
|
|
23752
24635
|
emit({ type: "tool_result", tool: name, ok: res.error === void 0, ...res.error ? { error: res.error } : {} });
|
|
23753
24636
|
},
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
|
|
24637
|
+
// Headless consumers get the partial report too — tagged, so a script can account
|
|
24638
|
+
// for the real cost of a restarted turn — but it never becomes `lastUsage`, which
|
|
24639
|
+
// represents the turn's actual output.
|
|
24640
|
+
onUsage: (u, partial) => {
|
|
24641
|
+
if (!partial)
|
|
24642
|
+
lastUsage = u;
|
|
24643
|
+
emit({ type: "usage", usage: u, ...partial ? { partial: true } : {} });
|
|
23757
24644
|
},
|
|
23758
24645
|
onRetry: (attempt, maxAttempts, reason) => {
|
|
23759
24646
|
emit({ type: "retry", attempt, max_attempts: maxAttempts, reason });
|
|
23760
24647
|
},
|
|
24648
|
+
// Headless consumers parse NDJSON, so a restart is trivially clean for them:
|
|
24649
|
+
// they simply drop every `text`/`thinking` event seen since the turn started.
|
|
24650
|
+
// Emitting it also opts headless mode into post-render restarts, so a scripted
|
|
24651
|
+
// /CI run survives a blip instead of exiting non-zero halfway through.
|
|
24652
|
+
onStreamRestart: (reason, discardedChars) => {
|
|
24653
|
+
emit({ type: "stream_restart", reason, discarded_chars: discardedChars });
|
|
24654
|
+
},
|
|
23761
24655
|
onBalanceStatus: (balance, zero) => {
|
|
23762
24656
|
emit({ type: "balance_status", balance, zero, billing_url: BILLING_URL });
|
|
23763
24657
|
},
|
|
@@ -23801,6 +24695,7 @@ function printHelp() {
|
|
|
23801
24695
|
["/rewind [id]", "List file checkpoints, or roll back to one"],
|
|
23802
24696
|
["/compact", "Summarize conversation to save tokens"],
|
|
23803
24697
|
["/init", "Generate nexrall.md for this project"],
|
|
24698
|
+
["/memory [global] [clear]", "View persistent memory (project or global); /memory clear to wipe"],
|
|
23804
24699
|
["/help", "Show this help"],
|
|
23805
24700
|
["/model [turbo|pro|ultra]", "Switch model"],
|
|
23806
24701
|
["/mode [ask|edit|plan|auto]", "Set agent mode"],
|
|
@@ -23808,7 +24703,7 @@ function printHelp() {
|
|
|
23808
24703
|
["/yolo", "Auto-approve all permissions"],
|
|
23809
24704
|
["/balance", "Show wallet balance"],
|
|
23810
24705
|
["/add <filepath>", "Add a file to conversation context"],
|
|
23811
|
-
["/
|
|
24706
|
+
["/skills", "List skills (.nexrall/skills/<name>/SKILL.md or .nexrall/commands/*.md) \u2014 the model can also auto-invoke these"],
|
|
23812
24707
|
["/plugins", "List installed plugins (.nexrall/plugins/)"],
|
|
23813
24708
|
["/update", "Update nex to the latest version"]
|
|
23814
24709
|
];
|
|
@@ -23867,9 +24762,9 @@ async function startChatSession(options) {
|
|
|
23867
24762
|
const ruleCount = permRules.allow.length + permRules.ask.length + permRules.deny.length;
|
|
23868
24763
|
if (ruleCount && !headless)
|
|
23869
24764
|
console.log(source_default.dim(` ${ruleCount} permission rule(s) loaded`));
|
|
23870
|
-
let
|
|
23871
|
-
if (
|
|
23872
|
-
console.log(source_default.dim(` ${
|
|
24765
|
+
let skills = (0, import_code_core3.loadSkills)(workDir);
|
|
24766
|
+
if (skills.length && !headless)
|
|
24767
|
+
console.log(source_default.dim(` ${skills.length} skill(s) loaded`));
|
|
23873
24768
|
if (headless && !options.prompt && !options.stdinText) {
|
|
23874
24769
|
process.stdout.write(JSON.stringify({ type: "error", error: "--output-format json/stream-json requires a one-shot prompt (or piped stdin)." }) + "\n");
|
|
23875
24770
|
process.exit(1);
|
|
@@ -23910,6 +24805,19 @@ async function startChatSession(options) {
|
|
|
23910
24805
|
const raw = first?.content[0]?.text ?? "";
|
|
23911
24806
|
sessionTitle = raw.length > 60 ? raw.slice(0, 60) + "\u2026" : raw;
|
|
23912
24807
|
};
|
|
24808
|
+
const recoverTurn = (err, current) => {
|
|
24809
|
+
const salvaged = (0, import_code_core3.salvageHistory)(err);
|
|
24810
|
+
console.error("\n" + source_default.red("Error: ") + String(err.message));
|
|
24811
|
+
if (!salvaged)
|
|
24812
|
+
return current;
|
|
24813
|
+
const rounds = err.progressCount ?? 1;
|
|
24814
|
+
console.error(
|
|
24815
|
+
source_default.dim(
|
|
24816
|
+
` Kept the ${rounds} step${rounds === 1 ? "" : "s"} completed before the interruption \u2014 send "continue" to pick up where it stopped.`
|
|
24817
|
+
)
|
|
24818
|
+
);
|
|
24819
|
+
return salvaged;
|
|
24820
|
+
};
|
|
23913
24821
|
let lastProgressSave = 0;
|
|
23914
24822
|
const saveProgress = (live) => {
|
|
23915
24823
|
const now = Date.now();
|
|
@@ -23945,10 +24853,27 @@ ${text}` : "");
|
|
|
23945
24853
|
updateTitle();
|
|
23946
24854
|
saveSession(sessionId, sessionTitle, workDir, messages);
|
|
23947
24855
|
} catch (err) {
|
|
24856
|
+
const salvaged = (0, import_code_core3.salvageHistory)(err);
|
|
24857
|
+
if (salvaged) {
|
|
24858
|
+
messages = salvaged;
|
|
24859
|
+
try {
|
|
24860
|
+
updateTitle();
|
|
24861
|
+
saveSession(sessionId, sessionTitle || "Untitled", workDir, messages);
|
|
24862
|
+
checkpoints.commitTurn();
|
|
24863
|
+
} catch {
|
|
24864
|
+
}
|
|
24865
|
+
}
|
|
23948
24866
|
if (options.outputFormat === "json" || options.outputFormat === "stream-json") {
|
|
23949
|
-
process.stdout.write(JSON.stringify({
|
|
24867
|
+
process.stdout.write(JSON.stringify({
|
|
24868
|
+
type: "error",
|
|
24869
|
+
error: String(err.message),
|
|
24870
|
+
...salvaged ? { resumable: true, messagesCompleted: salvaged.length } : {}
|
|
24871
|
+
}) + "\n");
|
|
23950
24872
|
} else {
|
|
23951
24873
|
console.error(source_default.red("\nError: ") + String(err.message));
|
|
24874
|
+
if (salvaged) {
|
|
24875
|
+
console.error(source_default.dim(" Progress was saved \u2014 run `nex --continue` to resume this session."));
|
|
24876
|
+
}
|
|
23952
24877
|
}
|
|
23953
24878
|
process.exit(1);
|
|
23954
24879
|
} finally {
|
|
@@ -24210,6 +25135,49 @@ ${dirList}`;
|
|
|
24210
25135
|
rl.prompt();
|
|
24211
25136
|
return;
|
|
24212
25137
|
}
|
|
25138
|
+
case "/memory": {
|
|
25139
|
+
const memArgs = arg.toLowerCase().split(/\s+/).filter(Boolean);
|
|
25140
|
+
const wantsGlobalOnly = memArgs.includes("global");
|
|
25141
|
+
const wantsClear = memArgs.includes("clear");
|
|
25142
|
+
const memScope = wantsGlobalOnly ? "global" : "project";
|
|
25143
|
+
if (wantsClear) {
|
|
25144
|
+
rl.pause();
|
|
25145
|
+
const rl2 = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
25146
|
+
const label = memScope === "global" ? "GLOBAL" : "this PROJECT's";
|
|
25147
|
+
const answer = await new Promise((resolve3) => rl2.question(source_default.yellow(` Clear ${label} memory? This cannot be undone. [y/n] `), (a) => {
|
|
25148
|
+
rl2.close();
|
|
25149
|
+
resolve3(a.trim());
|
|
25150
|
+
}));
|
|
25151
|
+
if (answer === "y" || answer === "yes") {
|
|
25152
|
+
(0, import_code_core3.clearMemory)(memScope, workDir);
|
|
25153
|
+
console.log(source_default.green(` ${memScope === "global" ? "Global" : "Project"} memory cleared.`));
|
|
25154
|
+
} else {
|
|
25155
|
+
console.log(source_default.dim(" Cancelled."));
|
|
25156
|
+
}
|
|
25157
|
+
rl.resume();
|
|
25158
|
+
rl.prompt();
|
|
25159
|
+
return;
|
|
25160
|
+
}
|
|
25161
|
+
console.log();
|
|
25162
|
+
if (wantsGlobalOnly) {
|
|
25163
|
+
const stats = (0, import_code_core3.memoryStats)("global");
|
|
25164
|
+
const content = (0, import_code_core3.readMemory)("global");
|
|
25165
|
+
console.log(source_default.bold(" Global memory ") + source_default.dim(`(${stats.entries} entries, ${(stats.bytes / 1024).toFixed(1)}KB) \u2014 ${stats.file}`));
|
|
25166
|
+
console.log();
|
|
25167
|
+
console.log(content || source_default.dim(" No global memories saved yet."));
|
|
25168
|
+
} else {
|
|
25169
|
+
const projStats = (0, import_code_core3.memoryStats)("project", workDir);
|
|
25170
|
+
const globalStats = (0, import_code_core3.memoryStats)("global");
|
|
25171
|
+
console.log(source_default.bold(" Memory") + source_default.dim(` \u2014 project: ${projStats.entries} entries (${(projStats.bytes / 1024).toFixed(1)}KB) \xB7 global: ${globalStats.entries} entries (${(globalStats.bytes / 1024).toFixed(1)}KB)`));
|
|
25172
|
+
console.log();
|
|
25173
|
+
const merged = (0, import_code_core3.readAllMemory)(workDir);
|
|
25174
|
+
console.log(merged || source_default.dim(" No memories saved yet."));
|
|
25175
|
+
}
|
|
25176
|
+
console.log();
|
|
25177
|
+
console.log(source_default.dim(" Tip: /memory global \xB7 /memory clear \xB7 /memory global clear"));
|
|
25178
|
+
rl.prompt();
|
|
25179
|
+
return;
|
|
25180
|
+
}
|
|
24213
25181
|
case "/help":
|
|
24214
25182
|
printHelp();
|
|
24215
25183
|
rl.prompt();
|
|
@@ -24313,20 +25281,23 @@ ${content}
|
|
|
24313
25281
|
}
|
|
24314
25282
|
console.log();
|
|
24315
25283
|
}
|
|
24316
|
-
|
|
25284
|
+
skills = (0, import_code_core3.loadSkills)(workDir);
|
|
24317
25285
|
rl.prompt();
|
|
24318
25286
|
return;
|
|
24319
25287
|
}
|
|
25288
|
+
case "/skills":
|
|
24320
25289
|
case "/commands": {
|
|
24321
|
-
|
|
24322
|
-
|
|
24323
|
-
|
|
25290
|
+
skills = (0, import_code_core3.loadSkills)(workDir);
|
|
25291
|
+
const invocable = (0, import_code_core3.userInvokableSkills)(skills);
|
|
25292
|
+
if (!invocable.length) {
|
|
25293
|
+
console.log(source_default.dim(" No skills. Add .nexrall/skills/<name>/SKILL.md (or .nexrall/commands/<name>.md) to create one."));
|
|
24324
25294
|
} else {
|
|
24325
25295
|
console.log();
|
|
24326
|
-
console.log(source_default.bold("
|
|
24327
|
-
for (const
|
|
24328
|
-
const scope =
|
|
24329
|
-
|
|
25296
|
+
console.log(source_default.bold(" Skills:"));
|
|
25297
|
+
for (const s2 of invocable) {
|
|
25298
|
+
const scope = s2.source === "project" ? "" : source_default.dim(` (${s2.source})`);
|
|
25299
|
+
const auto = s2.disableModelInvocation ? source_default.dim(" [manual only]") : "";
|
|
25300
|
+
console.log(" " + source_default.cyan(`/${s2.name}`.padEnd(20)) + source_default.dim(s2.description) + scope + auto);
|
|
24330
25301
|
}
|
|
24331
25302
|
console.log();
|
|
24332
25303
|
}
|
|
@@ -24334,14 +25305,14 @@ ${content}
|
|
|
24334
25305
|
return;
|
|
24335
25306
|
}
|
|
24336
25307
|
default: {
|
|
24337
|
-
const custom = (0, import_code_core3.
|
|
25308
|
+
const custom = (0, import_code_core3.findSkill)(skills, cmd);
|
|
24338
25309
|
if (!custom) {
|
|
24339
25310
|
console.log(source_default.red(` Unknown command: ${cmd}`));
|
|
24340
|
-
console.log(source_default.dim(" Type /help for built-ins or /
|
|
25311
|
+
console.log(source_default.dim(" Type /help for built-ins or /skills for custom ones."));
|
|
24341
25312
|
rl.prompt();
|
|
24342
25313
|
return;
|
|
24343
25314
|
}
|
|
24344
|
-
const expanded = (0, import_code_core3.
|
|
25315
|
+
const expanded = (0, import_code_core3.expandSkill)(custom, arg, workDir);
|
|
24345
25316
|
const turnModel = custom.model ?? modelAlias;
|
|
24346
25317
|
const turnMode = custom.mode ?? agentMode;
|
|
24347
25318
|
console.log(source_default.dim(` Running /${custom.name}\u2026`));
|
|
@@ -24356,7 +25327,9 @@ ${content}
|
|
|
24356
25327
|
updateTitle();
|
|
24357
25328
|
saveSession(sessionId, sessionTitle, workDir, messages);
|
|
24358
25329
|
} catch (err) {
|
|
24359
|
-
|
|
25330
|
+
messages = recoverTurn(err, messages);
|
|
25331
|
+
updateTitle();
|
|
25332
|
+
saveSession(sessionId, sessionTitle || "Untitled", workDir, messages);
|
|
24360
25333
|
} finally {
|
|
24361
25334
|
agentRunning = false;
|
|
24362
25335
|
checkpoints.commitTurn();
|
|
@@ -24378,7 +25351,9 @@ ${content}
|
|
|
24378
25351
|
updateTitle();
|
|
24379
25352
|
saveSession(sessionId, sessionTitle, workDir, messages);
|
|
24380
25353
|
} catch (err) {
|
|
24381
|
-
|
|
25354
|
+
messages = recoverTurn(err, messages);
|
|
25355
|
+
updateTitle();
|
|
25356
|
+
saveSession(sessionId, sessionTitle || "Untitled", workDir, messages);
|
|
24382
25357
|
} finally {
|
|
24383
25358
|
agentRunning = false;
|
|
24384
25359
|
checkpoints.commitTurn();
|
|
@@ -24600,7 +25575,7 @@ function pluginListCommand() {
|
|
|
24600
25575
|
|
|
24601
25576
|
// src/index.ts
|
|
24602
25577
|
var program2 = new Command();
|
|
24603
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
25578
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.17");
|
|
24604
25579
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
24605
25580
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
24606
25581
|
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) => {
|