nexrall-code 0.5.48 → 0.5.50
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 +1081 -192
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1773,13 +1773,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1773
1773
|
* @param {string} source - expected values are default/config/env/cli/implied
|
|
1774
1774
|
* @return {Command} `this` command for chaining
|
|
1775
1775
|
*/
|
|
1776
|
-
setOptionValueWithSource(key, value,
|
|
1776
|
+
setOptionValueWithSource(key, value, source2) {
|
|
1777
1777
|
if (this._storeOptionsAsProperties) {
|
|
1778
1778
|
this[key] = value;
|
|
1779
1779
|
} else {
|
|
1780
1780
|
this._optionValues[key] = value;
|
|
1781
1781
|
}
|
|
1782
|
-
this._optionValueSources[key] =
|
|
1782
|
+
this._optionValueSources[key] = source2;
|
|
1783
1783
|
return this;
|
|
1784
1784
|
}
|
|
1785
1785
|
/**
|
|
@@ -1800,13 +1800,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1800
1800
|
* @return {string}
|
|
1801
1801
|
*/
|
|
1802
1802
|
getOptionValueSourceWithGlobals(key) {
|
|
1803
|
-
let
|
|
1803
|
+
let source2;
|
|
1804
1804
|
this._getCommandAndAncestors().forEach((cmd) => {
|
|
1805
1805
|
if (cmd.getOptionValueSource(key) !== void 0) {
|
|
1806
|
-
|
|
1806
|
+
source2 = cmd.getOptionValueSource(key);
|
|
1807
1807
|
}
|
|
1808
1808
|
});
|
|
1809
|
-
return
|
|
1809
|
+
return source2;
|
|
1810
1810
|
}
|
|
1811
1811
|
/**
|
|
1812
1812
|
* Get user arguments from implied or explicit arguments.
|
|
@@ -2615,8 +2615,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2615
2615
|
const getErrorMessage = (option2) => {
|
|
2616
2616
|
const bestOption = findBestOptionFromValue(option2);
|
|
2617
2617
|
const optionKey = bestOption.attributeName();
|
|
2618
|
-
const
|
|
2619
|
-
if (
|
|
2618
|
+
const source2 = this.getOptionValueSource(optionKey);
|
|
2619
|
+
if (source2 === "env") {
|
|
2620
2620
|
return `environment variable '${bestOption.envVar}'`;
|
|
2621
2621
|
}
|
|
2622
2622
|
return `option '${bestOption.flags}'`;
|
|
@@ -5998,10 +5998,10 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
5998
5998
|
return ctor;
|
|
5999
5999
|
}
|
|
6000
6000
|
const DOMException2 = getFromGlobal() || createPolyfill();
|
|
6001
|
-
function ReadableStreamPipeTo(
|
|
6002
|
-
const reader = AcquireReadableStreamDefaultReader(
|
|
6001
|
+
function ReadableStreamPipeTo(source2, dest, preventClose, preventAbort, preventCancel, signal) {
|
|
6002
|
+
const reader = AcquireReadableStreamDefaultReader(source2);
|
|
6003
6003
|
const writer = AcquireWritableStreamDefaultWriter(dest);
|
|
6004
|
-
|
|
6004
|
+
source2._disturbed = true;
|
|
6005
6005
|
let shuttingDown = false;
|
|
6006
6006
|
let currentWrite = promiseResolvedWith(void 0);
|
|
6007
6007
|
return newPromise((resolve3, reject) => {
|
|
@@ -6020,8 +6020,8 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6020
6020
|
}
|
|
6021
6021
|
if (!preventCancel) {
|
|
6022
6022
|
actions.push(() => {
|
|
6023
|
-
if (
|
|
6024
|
-
return ReadableStreamCancel(
|
|
6023
|
+
if (source2._state === "readable") {
|
|
6024
|
+
return ReadableStreamCancel(source2, error);
|
|
6025
6025
|
}
|
|
6026
6026
|
return promiseResolvedWith(void 0);
|
|
6027
6027
|
});
|
|
@@ -6063,7 +6063,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6063
6063
|
});
|
|
6064
6064
|
});
|
|
6065
6065
|
}
|
|
6066
|
-
isOrBecomesErrored(
|
|
6066
|
+
isOrBecomesErrored(source2, reader._closedPromise, (storedError) => {
|
|
6067
6067
|
if (!preventAbort) {
|
|
6068
6068
|
shutdownWithAction(() => WritableStreamAbort(dest, storedError), true, storedError);
|
|
6069
6069
|
} else {
|
|
@@ -6073,13 +6073,13 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6073
6073
|
});
|
|
6074
6074
|
isOrBecomesErrored(dest, writer._closedPromise, (storedError) => {
|
|
6075
6075
|
if (!preventCancel) {
|
|
6076
|
-
shutdownWithAction(() => ReadableStreamCancel(
|
|
6076
|
+
shutdownWithAction(() => ReadableStreamCancel(source2, storedError), true, storedError);
|
|
6077
6077
|
} else {
|
|
6078
6078
|
shutdown(true, storedError);
|
|
6079
6079
|
}
|
|
6080
6080
|
return null;
|
|
6081
6081
|
});
|
|
6082
|
-
isOrBecomesClosed(
|
|
6082
|
+
isOrBecomesClosed(source2, reader._closedPromise, () => {
|
|
6083
6083
|
if (!preventClose) {
|
|
6084
6084
|
shutdownWithAction(() => WritableStreamDefaultWriterCloseWithErrorPropagation(writer));
|
|
6085
6085
|
} else {
|
|
@@ -6090,7 +6090,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6090
6090
|
if (WritableStreamCloseQueuedOrInFlight(dest) || dest._state === "closed") {
|
|
6091
6091
|
const destClosed = new TypeError("the destination writable stream closed before all data could be piped to it");
|
|
6092
6092
|
if (!preventCancel) {
|
|
6093
|
-
shutdownWithAction(() => ReadableStreamCancel(
|
|
6093
|
+
shutdownWithAction(() => ReadableStreamCancel(source2, destClosed), true, destClosed);
|
|
6094
6094
|
} else {
|
|
6095
6095
|
shutdown(true, destClosed);
|
|
6096
6096
|
}
|
|
@@ -6726,11 +6726,11 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6726
6726
|
function isReadableStreamLike(stream) {
|
|
6727
6727
|
return typeIsObject(stream) && typeof stream.getReader !== "undefined";
|
|
6728
6728
|
}
|
|
6729
|
-
function ReadableStreamFrom(
|
|
6730
|
-
if (isReadableStreamLike(
|
|
6731
|
-
return ReadableStreamFromDefaultReader(
|
|
6729
|
+
function ReadableStreamFrom(source2) {
|
|
6730
|
+
if (isReadableStreamLike(source2)) {
|
|
6731
|
+
return ReadableStreamFromDefaultReader(source2.getReader());
|
|
6732
6732
|
}
|
|
6733
|
-
return ReadableStreamFromIterable(
|
|
6733
|
+
return ReadableStreamFromIterable(source2);
|
|
6734
6734
|
}
|
|
6735
6735
|
function ReadableStreamFromIterable(asyncIterable) {
|
|
6736
6736
|
let stream;
|
|
@@ -6817,9 +6817,9 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
6817
6817
|
stream = CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, 0);
|
|
6818
6818
|
return stream;
|
|
6819
6819
|
}
|
|
6820
|
-
function convertUnderlyingDefaultOrByteSource(
|
|
6821
|
-
assertDictionary(
|
|
6822
|
-
const original =
|
|
6820
|
+
function convertUnderlyingDefaultOrByteSource(source2, context) {
|
|
6821
|
+
assertDictionary(source2, context);
|
|
6822
|
+
const original = source2;
|
|
6823
6823
|
const autoAllocateChunkSize = original === null || original === void 0 ? void 0 : original.autoAllocateChunkSize;
|
|
6824
6824
|
const cancel = original === null || original === void 0 ? void 0 : original.cancel;
|
|
6825
6825
|
const pull = original === null || original === void 0 ? void 0 : original.pull;
|
|
@@ -10819,6 +10819,144 @@ var require_editCompleteness = __commonJS({
|
|
|
10819
10819
|
}
|
|
10820
10820
|
});
|
|
10821
10821
|
|
|
10822
|
+
// ../core/dist/agent/securityLint.js
|
|
10823
|
+
var require_securityLint = __commonJS({
|
|
10824
|
+
"../core/dist/agent/securityLint.js"(exports) {
|
|
10825
|
+
"use strict";
|
|
10826
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10827
|
+
exports.checkSecurity = checkSecurity;
|
|
10828
|
+
exports.securityNoteText = securityNoteText;
|
|
10829
|
+
var SAMPLE_MAX = 160;
|
|
10830
|
+
function redact(line) {
|
|
10831
|
+
const masked = line.replace(/(['"`]?[\w.-]*(?:secret|password|passwd|token|api[_-]?key|apikey|auth|credential|private[_-]?key)[\w.-]*['"`]?\s*[:=]\s*)(['"`])([^'"`]{4,})\2/gi, (_m, head, q) => `${head}${q}[REDACTED]${q}`).replace(/\b(sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10,})\b/g, "[REDACTED]");
|
|
10832
|
+
return masked.length > SAMPLE_MAX ? masked.slice(0, SAMPLE_MAX) + "\u2026" : masked;
|
|
10833
|
+
}
|
|
10834
|
+
function isLikelyCommentLine(line) {
|
|
10835
|
+
return /^\s*(\/\/|\*|#|--|<!--)/.test(line);
|
|
10836
|
+
}
|
|
10837
|
+
var RULES = [
|
|
10838
|
+
// ── Hardcoded credentials ───────────────────────────────────────────────────
|
|
10839
|
+
// Provider-prefixed tokens are near-zero false positive: the prefixes are
|
|
10840
|
+
// registered formats, not something that occurs naturally in source. Checked
|
|
10841
|
+
// inside comments too — a key commented out is still a committed key.
|
|
10842
|
+
{
|
|
10843
|
+
kind: "hardcoded-secret",
|
|
10844
|
+
re: /\b(sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{30,})\b/,
|
|
10845
|
+
message: "Looks like a real API key/token committed to source. Move it to an environment variable and rotate the exposed key.",
|
|
10846
|
+
includeComments: true
|
|
10847
|
+
},
|
|
10848
|
+
// NOTE: the `private-key` check is NOT here — it needs to span multiple lines
|
|
10849
|
+
// (BEGIN header on one, base64 body on the next), which this per-line loop
|
|
10850
|
+
// cannot express. It runs separately in checkSecurity below.
|
|
10851
|
+
{
|
|
10852
|
+
kind: "hardcoded-password",
|
|
10853
|
+
// An ASSIGNMENT of a credential-ish name to a non-trivial literal.
|
|
10854
|
+
//
|
|
10855
|
+
// Tightened after measuring against the real backend, where the looser version
|
|
10856
|
+
// fired on `missingSecret:'FIREBASE_TOKEN'` — code that NAMES a secret in an
|
|
10857
|
+
// error message, the opposite of leaking one. So a value that is itself just a
|
|
10858
|
+
// SCREAMING_SNAKE identifier (an env-var name) is excluded, along with the
|
|
10859
|
+
// usual placeholder vocabulary. The value must also look like actual secret
|
|
10860
|
+
// material: mixed case or digits, not a lone lowercase word.
|
|
10861
|
+
re: /(?:password|passwd|secret|api[_-]?key|apikey|access[_-]?token)['"`]?\s*[:=]\s*['"`](?![A-Z0-9_]+['"`])(?!.*(?:\$\{|process\.env|os\.environ|example|changeme|placeholder|redacted|xxx|test|dummy|fake|sample|your[_-]?|<|\*{3}))(?=[^'"`]*[0-9A-Z])[^'"`\s]{10,}['"`]/,
|
|
10862
|
+
message: "Hardcoded credential literal. Read it from the environment/secret store instead, and rotate the exposed value.",
|
|
10863
|
+
includeComments: true
|
|
10864
|
+
},
|
|
10865
|
+
// ── Injection ───────────────────────────────────────────────────────────────
|
|
10866
|
+
{
|
|
10867
|
+
kind: "dynamic-eval",
|
|
10868
|
+
// The negative lookbehind for `.` is what makes this usable: `redisClient.eval`
|
|
10869
|
+
// (a Redis Lua script), `page.eval` (Playwright), `vm.eval` and friends are
|
|
10870
|
+
// METHOD calls on an object and have nothing to do with JavaScript's global
|
|
10871
|
+
// eval. Without it, the real backend's Redis idempotency script was flagged.
|
|
10872
|
+
// Only a bare `eval(` / `new Function(` with a non-literal argument counts.
|
|
10873
|
+
re: /(?<![.\w$])(?:eval|new\s+Function)\s*\(\s*(?!['"`][^'"`]*['"`]\s*\))[^)]*[a-zA-Z_$][\w$]*/,
|
|
10874
|
+
message: "eval / new Function on a non-literal value executes arbitrary code if that value is ever user-controlled. Use an explicit parser or a lookup table."
|
|
10875
|
+
},
|
|
10876
|
+
{
|
|
10877
|
+
kind: "sql-injection",
|
|
10878
|
+
// Only fires when the interpolated expression is plausibly REQUEST-DERIVED.
|
|
10879
|
+
//
|
|
10880
|
+
// The obvious pattern — any `${...}` inside a SQL string — was measured
|
|
10881
|
+
// against the real backend and flagged 15 of 183 files, essentially all of
|
|
10882
|
+
// them safe and idiomatic: `${sets.join(', ')}` for a dynamic UPDATE, `${field}`
|
|
10883
|
+
// for a server-chosen column, `${CONSUMPTION}` for a module constant. At that
|
|
10884
|
+
// hit rate the warning is pure noise, and noise is worse than silence because
|
|
10885
|
+
// it teaches everyone to skip the channel.
|
|
10886
|
+
//
|
|
10887
|
+
// So the interpolation must name something that plausibly came from the
|
|
10888
|
+
// outside: req/request/params/query/body/input/user/args, or a bare
|
|
10889
|
+
// `'...' + ident`. This trades recall for precision on purpose — thorough SQL
|
|
10890
|
+
// review is the security-auditor agent's job, not an inline regex's.
|
|
10891
|
+
re: /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM)\b[^;'"`]{0,160}(?:\$\{\s*(?:req|request|params?|query|body|input|user|args|ctx)\b|['"`]\s*\+\s*(?:req|request|params?|query|body|input|user|args|ctx)\b|%\s*\(\s*(?:request|params?|query|body|input|user)\b)/i,
|
|
10892
|
+
message: "SQL built by interpolating a request-derived value. Use a parameterised query ($1 / ? placeholders) \u2014 this is the classic injection sink."
|
|
10893
|
+
},
|
|
10894
|
+
{
|
|
10895
|
+
kind: "command-injection",
|
|
10896
|
+
// Shell execution with an interpolated or concatenated argument.
|
|
10897
|
+
re: /\b(?:exec|execSync|spawnSync?|system|popen|os\.system|subprocess\.(?:call|run|Popen))\s*\(\s*(?:[`'"][^`'"]*(?:\$\{|['"]\s*\+)|[a-zA-Z_$][\w$]*\s*\+)/,
|
|
10898
|
+
message: "Shell command built from a variable. Pass arguments as an array (no shell), or validate against an allowlist \u2014 a value containing ; or $() becomes command execution."
|
|
10899
|
+
},
|
|
10900
|
+
// ── Transport / verification ────────────────────────────────────────────────
|
|
10901
|
+
{
|
|
10902
|
+
kind: "tls-verification-disabled",
|
|
10903
|
+
re: /(?:rejectUnauthorized\s*:\s*false|NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*['"]?0|verify\s*=\s*False|InsecureSkipVerify\s*:\s*true)/,
|
|
10904
|
+
message: "TLS certificate verification is disabled, which removes protection against man-in-the-middle attacks. Trust a specific CA instead if the cert is self-signed."
|
|
10905
|
+
}
|
|
10906
|
+
];
|
|
10907
|
+
function checkSecurity(content, max = 5) {
|
|
10908
|
+
if (!content)
|
|
10909
|
+
return [];
|
|
10910
|
+
const findings = [];
|
|
10911
|
+
const lines = content.split(/\r?\n/);
|
|
10912
|
+
const pemIdx = lines.findIndex((l) => /-----BEGIN\s+(?:RSA|EC|DSA|OPENSSH|PGP)?\s*PRIVATE KEY-----/.test(l));
|
|
10913
|
+
if (pemIdx !== -1) {
|
|
10914
|
+
const following = lines.slice(pemIdx, pemIdx + 4).join("\n");
|
|
10915
|
+
if (/[A-Za-z0-9+/]{40,}/.test(following.replace(/-----[^-]+-----/g, ""))) {
|
|
10916
|
+
findings.push({
|
|
10917
|
+
kind: "private-key",
|
|
10918
|
+
message: "A private key with real key material is being written into source. Store it outside the repo (secret manager / env var) and rotate it.",
|
|
10919
|
+
line: pemIdx + 1,
|
|
10920
|
+
sample: "-----BEGIN PRIVATE KEY----- [REDACTED]"
|
|
10921
|
+
});
|
|
10922
|
+
}
|
|
10923
|
+
}
|
|
10924
|
+
const seenKinds = /* @__PURE__ */ new Set();
|
|
10925
|
+
for (let i2 = 0; i2 < lines.length && findings.length < max; i2++) {
|
|
10926
|
+
const line = lines[i2];
|
|
10927
|
+
if (!line || line.length > 2e3)
|
|
10928
|
+
continue;
|
|
10929
|
+
const commentish = isLikelyCommentLine(line);
|
|
10930
|
+
for (const rule of RULES) {
|
|
10931
|
+
if (seenKinds.has(rule.kind))
|
|
10932
|
+
continue;
|
|
10933
|
+
if (commentish && !rule.includeComments)
|
|
10934
|
+
continue;
|
|
10935
|
+
if (!rule.re.test(line))
|
|
10936
|
+
continue;
|
|
10937
|
+
seenKinds.add(rule.kind);
|
|
10938
|
+
findings.push({ kind: rule.kind, message: rule.message, line: i2 + 1, sample: redact(line.trim()) });
|
|
10939
|
+
break;
|
|
10940
|
+
}
|
|
10941
|
+
}
|
|
10942
|
+
return findings;
|
|
10943
|
+
}
|
|
10944
|
+
function securityNoteText(findings) {
|
|
10945
|
+
if (process.env.NEXRALL_SECURITY_LINT === "off")
|
|
10946
|
+
return "";
|
|
10947
|
+
if (!findings.length)
|
|
10948
|
+
return "";
|
|
10949
|
+
const lines = findings.map((f3) => ` \u2022 line ${f3.line} [${f3.kind}]: ${f3.message}
|
|
10950
|
+
${f3.sample}`);
|
|
10951
|
+
return `
|
|
10952
|
+
|
|
10953
|
+
\u26A0 SECURITY REVIEW (${findings.length} finding${findings.length > 1 ? "s" : ""}) \u2014 the write succeeded; check these before moving on:
|
|
10954
|
+
` + lines.join("\n") + `
|
|
10955
|
+
If a finding is a false positive (test fixture, placeholder, intentionally dynamic), say so and continue \u2014 do NOT rewrite correct code to silence it.`;
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
});
|
|
10959
|
+
|
|
10822
10960
|
// ../core/dist/agent/memory.js
|
|
10823
10961
|
var require_memory = __commonJS({
|
|
10824
10962
|
"../core/dist/agent/memory.js"(exports) {
|
|
@@ -11063,6 +11201,8 @@ var require_plugins = __commonJS({
|
|
|
11063
11201
|
};
|
|
11064
11202
|
}();
|
|
11065
11203
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11204
|
+
exports.FILE_CANDIDATES = void 0;
|
|
11205
|
+
exports.resolvePluginFile = resolvePluginFile;
|
|
11066
11206
|
exports.loadPlugins = loadPlugins3;
|
|
11067
11207
|
exports.pluginAssetDirs = pluginAssetDirs;
|
|
11068
11208
|
exports.pluginHooks = pluginHooks;
|
|
@@ -11070,9 +11210,28 @@ var require_plugins = __commonJS({
|
|
|
11070
11210
|
var fs9 = __importStar(__require("fs"));
|
|
11071
11211
|
var path6 = __importStar(__require("path"));
|
|
11072
11212
|
var os6 = __importStar(__require("os"));
|
|
11213
|
+
exports.FILE_CANDIDATES = {
|
|
11214
|
+
manifest: ["plugin.json", path6.join(".claude-plugin", "plugin.json")],
|
|
11215
|
+
hooks: ["hooks.json", path6.join("hooks", "hooks.json")],
|
|
11216
|
+
mcp: ["mcp.json", ".mcp.json"]
|
|
11217
|
+
};
|
|
11218
|
+
function resolvePluginFile(dir, kind) {
|
|
11219
|
+
for (const rel of exports.FILE_CANDIDATES[kind]) {
|
|
11220
|
+
const full = path6.join(dir, rel);
|
|
11221
|
+
try {
|
|
11222
|
+
if (fs9.statSync(full).isFile())
|
|
11223
|
+
return full;
|
|
11224
|
+
} catch {
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11227
|
+
return null;
|
|
11228
|
+
}
|
|
11073
11229
|
function readMeta(dir) {
|
|
11230
|
+
const file = resolvePluginFile(dir, "manifest");
|
|
11231
|
+
if (!file)
|
|
11232
|
+
return {};
|
|
11074
11233
|
try {
|
|
11075
|
-
const raw = fs9.readFileSync(
|
|
11234
|
+
const raw = fs9.readFileSync(file, "utf-8");
|
|
11076
11235
|
const j = JSON.parse(raw);
|
|
11077
11236
|
return {
|
|
11078
11237
|
name: typeof j.name === "string" ? j.name : void 0,
|
|
@@ -11125,8 +11284,11 @@ var require_plugins = __commonJS({
|
|
|
11125
11284
|
function pluginHooks(workDir) {
|
|
11126
11285
|
const merged = {};
|
|
11127
11286
|
for (const p of loadPlugins3(workDir)) {
|
|
11287
|
+
const file = resolvePluginFile(p.dir, "hooks");
|
|
11288
|
+
if (!file)
|
|
11289
|
+
continue;
|
|
11128
11290
|
try {
|
|
11129
|
-
const raw = fs9.readFileSync(
|
|
11291
|
+
const raw = fs9.readFileSync(file, "utf-8");
|
|
11130
11292
|
const j = JSON.parse(raw);
|
|
11131
11293
|
const hooks = j.hooks ?? j;
|
|
11132
11294
|
for (const [phase, entries] of Object.entries(hooks)) {
|
|
@@ -11142,8 +11304,11 @@ var require_plugins = __commonJS({
|
|
|
11142
11304
|
function pluginMcpServers(workDir) {
|
|
11143
11305
|
const merged = {};
|
|
11144
11306
|
for (const p of loadPlugins3(workDir)) {
|
|
11307
|
+
const file = resolvePluginFile(p.dir, "mcp");
|
|
11308
|
+
if (!file)
|
|
11309
|
+
continue;
|
|
11145
11310
|
try {
|
|
11146
|
-
const raw = fs9.readFileSync(
|
|
11311
|
+
const raw = fs9.readFileSync(file, "utf-8");
|
|
11147
11312
|
const j = JSON.parse(raw);
|
|
11148
11313
|
const servers = j.mcpServers ?? j;
|
|
11149
11314
|
for (const [name, cfg] of Object.entries(servers)) {
|
|
@@ -11262,7 +11427,7 @@ var require_loader = __commonJS({
|
|
|
11262
11427
|
}
|
|
11263
11428
|
return { meta, body: (m2[2] ?? "").trim() };
|
|
11264
11429
|
}
|
|
11265
|
-
function loadDir(dir,
|
|
11430
|
+
function loadDir(dir, source2, into) {
|
|
11266
11431
|
let files;
|
|
11267
11432
|
try {
|
|
11268
11433
|
files = fs9.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
@@ -11276,7 +11441,7 @@ var require_loader = __commonJS({
|
|
|
11276
11441
|
const name = (meta.name || path6.basename(file, ".md")).trim().toLowerCase();
|
|
11277
11442
|
if (!name)
|
|
11278
11443
|
continue;
|
|
11279
|
-
if (
|
|
11444
|
+
if (source2 !== "project" && into.has(name))
|
|
11280
11445
|
continue;
|
|
11281
11446
|
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
11282
11447
|
into.set(name, {
|
|
@@ -11285,7 +11450,7 @@ var require_loader = __commonJS({
|
|
|
11285
11450
|
model,
|
|
11286
11451
|
mode: meta.mode || void 0,
|
|
11287
11452
|
body,
|
|
11288
|
-
source
|
|
11453
|
+
source: source2
|
|
11289
11454
|
});
|
|
11290
11455
|
} catch {
|
|
11291
11456
|
}
|
|
@@ -11431,7 +11596,7 @@ var require_skills = __commonJS({
|
|
|
11431
11596
|
return false;
|
|
11432
11597
|
return fallback;
|
|
11433
11598
|
}
|
|
11434
|
-
function toSkill(meta, body, name,
|
|
11599
|
+
function toSkill(meta, body, name, source2, dir) {
|
|
11435
11600
|
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
11436
11601
|
return {
|
|
11437
11602
|
name,
|
|
@@ -11439,13 +11604,13 @@ var require_skills = __commonJS({
|
|
|
11439
11604
|
model,
|
|
11440
11605
|
mode: meta.mode || void 0,
|
|
11441
11606
|
body,
|
|
11442
|
-
source,
|
|
11607
|
+
source: source2,
|
|
11443
11608
|
dir,
|
|
11444
11609
|
disableModelInvocation: parseBool(meta["disable-model-invocation"], false),
|
|
11445
11610
|
userInvocable: parseBool(meta["user-invocable"], true)
|
|
11446
11611
|
};
|
|
11447
11612
|
}
|
|
11448
|
-
function loadFlatCommandDir(dir,
|
|
11613
|
+
function loadFlatCommandDir(dir, source2, into) {
|
|
11449
11614
|
let files;
|
|
11450
11615
|
try {
|
|
11451
11616
|
files = fs9.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
@@ -11459,14 +11624,14 @@ var require_skills = __commonJS({
|
|
|
11459
11624
|
const name = (meta.name || path6.basename(file, ".md")).trim().toLowerCase();
|
|
11460
11625
|
if (!name)
|
|
11461
11626
|
continue;
|
|
11462
|
-
if (
|
|
11627
|
+
if (source2 !== "project" && into.has(name))
|
|
11463
11628
|
continue;
|
|
11464
|
-
into.set(name, toSkill(meta, body, name,
|
|
11629
|
+
into.set(name, toSkill(meta, body, name, source2));
|
|
11465
11630
|
} catch {
|
|
11466
11631
|
}
|
|
11467
11632
|
}
|
|
11468
11633
|
}
|
|
11469
|
-
function loadSkillDir(root,
|
|
11634
|
+
function loadSkillDir(root, source2, into) {
|
|
11470
11635
|
let entries;
|
|
11471
11636
|
try {
|
|
11472
11637
|
entries = fs9.readdirSync(root, { withFileTypes: true });
|
|
@@ -11490,9 +11655,9 @@ var require_skills = __commonJS({
|
|
|
11490
11655
|
const name = (meta.name || entry.name).trim().toLowerCase();
|
|
11491
11656
|
if (!name)
|
|
11492
11657
|
continue;
|
|
11493
|
-
if (
|
|
11658
|
+
if (source2 !== "project" && into.has(name))
|
|
11494
11659
|
continue;
|
|
11495
|
-
into.set(name, toSkill(meta, body, name,
|
|
11660
|
+
into.set(name, toSkill(meta, body, name, source2, skillDir));
|
|
11496
11661
|
} catch {
|
|
11497
11662
|
}
|
|
11498
11663
|
}
|
|
@@ -12026,12 +12191,12 @@ var require_symbols = __commonJS({
|
|
|
12026
12191
|
return new Set(EXT_TO_LANG.keys());
|
|
12027
12192
|
}
|
|
12028
12193
|
var MAX_FILE_BYTES = 1024 * 1024;
|
|
12029
|
-
function extractSymbols(filePath,
|
|
12194
|
+
function extractSymbols(filePath, source2) {
|
|
12030
12195
|
const lang = EXT_TO_LANG.get(path6.extname(filePath).toLowerCase());
|
|
12031
12196
|
if (!lang)
|
|
12032
12197
|
return [];
|
|
12033
12198
|
const hits = [];
|
|
12034
|
-
const lines =
|
|
12199
|
+
const lines = source2.split("\n");
|
|
12035
12200
|
let inBlockComment = false;
|
|
12036
12201
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
12037
12202
|
const trimmed = lines[i2].trim();
|
|
@@ -12068,21 +12233,21 @@ var require_symbols = __commonJS({
|
|
|
12068
12233
|
if (!rel)
|
|
12069
12234
|
return { error: "path is required" };
|
|
12070
12235
|
const abs = resolveIn(workDir, rel);
|
|
12071
|
-
let
|
|
12236
|
+
let source2;
|
|
12072
12237
|
try {
|
|
12073
12238
|
const st = fs9.statSync(abs);
|
|
12074
12239
|
if (!st.isFile())
|
|
12075
12240
|
return { error: `Not a file: ${rel}` };
|
|
12076
12241
|
if (st.size > MAX_FILE_BYTES)
|
|
12077
12242
|
return { error: `File too large for symbol scan (${Math.round(st.size / 1024)} KB > 1024 KB)` };
|
|
12078
|
-
|
|
12243
|
+
source2 = fs9.readFileSync(abs, "utf-8");
|
|
12079
12244
|
} catch (err) {
|
|
12080
12245
|
return { error: `Cannot read ${rel}: ${err.message}` };
|
|
12081
12246
|
}
|
|
12082
12247
|
if (!EXT_TO_LANG.has(path6.extname(abs).toLowerCase())) {
|
|
12083
12248
|
return { output: `No symbol support for ${path6.extname(abs) || "this file type"} (regex-based scanner). Use search_files instead.` };
|
|
12084
12249
|
}
|
|
12085
|
-
const hits = extractSymbols(abs,
|
|
12250
|
+
const hits = extractSymbols(abs, source2);
|
|
12086
12251
|
if (!hits.length)
|
|
12087
12252
|
return { output: `No symbols found in ${rel}.` };
|
|
12088
12253
|
const lines = hits.map((h2) => `${h2.kind.padEnd(10)} ${h2.name} (line ${h2.line})`);
|
|
@@ -12124,10 +12289,10 @@ ${lines.join("\n")}` };
|
|
|
12124
12289
|
const st = fs9.statSync(full);
|
|
12125
12290
|
if (st.size > MAX_FILE_BYTES)
|
|
12126
12291
|
continue;
|
|
12127
|
-
const
|
|
12128
|
-
if (!
|
|
12292
|
+
const source2 = fs9.readFileSync(full, "utf-8");
|
|
12293
|
+
if (!source2.toLowerCase().includes(q))
|
|
12129
12294
|
continue;
|
|
12130
|
-
for (const h2 of extractSymbols(full,
|
|
12295
|
+
for (const h2 of extractSymbols(full, source2)) {
|
|
12131
12296
|
if (h2.name.toLowerCase().includes(q)) {
|
|
12132
12297
|
results.push({ ...h2, file: path6.relative(root, full) });
|
|
12133
12298
|
if (results.length >= MAX_RESULTS)
|
|
@@ -12314,18 +12479,18 @@ var require_tsLangService = __commonJS({
|
|
|
12314
12479
|
return false;
|
|
12315
12480
|
}
|
|
12316
12481
|
}
|
|
12317
|
-
function posToOffset(
|
|
12318
|
-
const lines =
|
|
12482
|
+
function posToOffset(source2, line, character) {
|
|
12483
|
+
const lines = source2.split("\n");
|
|
12319
12484
|
let offset = 0;
|
|
12320
12485
|
for (let i2 = 0; i2 < line - 1 && i2 < lines.length; i2++)
|
|
12321
12486
|
offset += lines[i2].length + 1;
|
|
12322
12487
|
return offset + Math.max(0, character - 1);
|
|
12323
12488
|
}
|
|
12324
|
-
function offsetToPos(
|
|
12489
|
+
function offsetToPos(source2, offset) {
|
|
12325
12490
|
let line = 1;
|
|
12326
12491
|
let last = 0;
|
|
12327
|
-
for (let i2 = 0; i2 < offset && i2 <
|
|
12328
|
-
if (
|
|
12492
|
+
for (let i2 = 0; i2 < offset && i2 < source2.length; i2++) {
|
|
12493
|
+
if (source2[i2] === "\n") {
|
|
12329
12494
|
line++;
|
|
12330
12495
|
last = i2 + 1;
|
|
12331
12496
|
}
|
|
@@ -12342,8 +12507,8 @@ var require_tsLangService = __commonJS({
|
|
|
12342
12507
|
return null;
|
|
12343
12508
|
if (!ensureFile(svc, deps.filePath))
|
|
12344
12509
|
return { error: `Cannot read ${deps.filePath}` };
|
|
12345
|
-
const
|
|
12346
|
-
const offset = posToOffset(
|
|
12510
|
+
const source2 = svc.snapshots.get(deps.filePath) ?? "";
|
|
12511
|
+
const offset = posToOffset(source2, deps.line, deps.character);
|
|
12347
12512
|
let defs;
|
|
12348
12513
|
try {
|
|
12349
12514
|
defs = svc.service.getDefinitionAtPosition(deps.filePath, offset);
|
|
@@ -12366,8 +12531,8 @@ ${out.join("\n")}` };
|
|
|
12366
12531
|
return null;
|
|
12367
12532
|
if (!ensureFile(svc, deps.filePath))
|
|
12368
12533
|
return { error: `Cannot read ${deps.filePath}` };
|
|
12369
|
-
const
|
|
12370
|
-
const offset = posToOffset(
|
|
12534
|
+
const source2 = svc.snapshots.get(deps.filePath) ?? "";
|
|
12535
|
+
const offset = posToOffset(source2, deps.line, deps.character);
|
|
12371
12536
|
let refs;
|
|
12372
12537
|
try {
|
|
12373
12538
|
refs = svc.service.getReferencesAtPosition(deps.filePath, offset);
|
|
@@ -12394,8 +12559,8 @@ ${lines.join("\n")}` };
|
|
|
12394
12559
|
return null;
|
|
12395
12560
|
if (!ensureFile(svc, deps.filePath))
|
|
12396
12561
|
return { error: `Cannot read ${deps.filePath}` };
|
|
12397
|
-
const
|
|
12398
|
-
const offset = posToOffset(
|
|
12562
|
+
const source2 = svc.snapshots.get(deps.filePath) ?? "";
|
|
12563
|
+
const offset = posToOffset(source2, deps.line, deps.character);
|
|
12399
12564
|
let info;
|
|
12400
12565
|
try {
|
|
12401
12566
|
info = svc.service.getQuickInfoAtPosition(deps.filePath, offset);
|
|
@@ -12482,6 +12647,7 @@ var require_executor = __commonJS({
|
|
|
12482
12647
|
var sandbox_1 = require_sandbox();
|
|
12483
12648
|
var auth_1 = require_auth();
|
|
12484
12649
|
var editCompleteness_1 = require_editCompleteness();
|
|
12650
|
+
var securityLint_1 = require_securityLint();
|
|
12485
12651
|
var memory_1 = require_memory();
|
|
12486
12652
|
var skills_1 = require_skills();
|
|
12487
12653
|
var testIntegrity_1 = require_testIntegrity();
|
|
@@ -12737,15 +12903,16 @@ Resolve the conflict (remove <<<<<<< / ======= / >>>>>>> markers and keep the in
|
|
|
12737
12903
|
atomicWrite(resolved, content, existingMode);
|
|
12738
12904
|
const bytes = Buffer.byteLength(content, "utf-8");
|
|
12739
12905
|
const lines = content.split("\n").length;
|
|
12906
|
+
const sec = (0, securityLint_1.securityNoteText)((0, securityLint_1.checkSecurity)(content));
|
|
12740
12907
|
if (isNew) {
|
|
12741
|
-
return { output: `Created ${resolved} (${lines} lines, ${bytes} bytes)` };
|
|
12908
|
+
return { output: `Created ${resolved} (${lines} lines, ${bytes} bytes)${sec}` };
|
|
12742
12909
|
}
|
|
12743
12910
|
const xfile = crossFileBreakageWarning(resolved, normalizeLF(priorContent), normalizeLF(content), workDir ?? process.cwd());
|
|
12744
12911
|
let tiMarker = "";
|
|
12745
12912
|
const ti = (0, testIntegrity_1.analyzeTestEdit)(resolved, normalizeLF(priorContent), normalizeLF(content));
|
|
12746
12913
|
if (ti.suspicious)
|
|
12747
12914
|
tiMarker = (0, testIntegrity_1.encodeTestIntegrityMarker)(ti.findings);
|
|
12748
|
-
return { output: `Overwrote ${resolved} (${lines} lines, ${bytes} bytes)${xfile}${tiMarker}` };
|
|
12915
|
+
return { output: `Overwrote ${resolved} (${lines} lines, ${bytes} bytes)${xfile}${sec}${tiMarker}` };
|
|
12749
12916
|
} catch (err) {
|
|
12750
12917
|
return { error: err.message };
|
|
12751
12918
|
}
|
|
@@ -13388,14 +13555,14 @@ ${globalCapMatches(output)}` : "";
|
|
|
13388
13555
|
}
|
|
13389
13556
|
}
|
|
13390
13557
|
async function moveFile(input, workDir) {
|
|
13391
|
-
const
|
|
13558
|
+
const source2 = typeof input.source === "string" ? input.source : "";
|
|
13392
13559
|
const dest = typeof input.dest === "string" ? input.dest : "";
|
|
13393
|
-
if (!
|
|
13560
|
+
if (!source2)
|
|
13394
13561
|
return { error: "Missing required parameter: source" };
|
|
13395
13562
|
if (!dest)
|
|
13396
13563
|
return { error: "Missing required parameter: dest" };
|
|
13397
13564
|
try {
|
|
13398
|
-
const resolvedSource = resolvePath(
|
|
13565
|
+
const resolvedSource = resolvePath(source2, workDir);
|
|
13399
13566
|
const resolvedDest = resolvePath(dest, workDir);
|
|
13400
13567
|
fs9.mkdirSync(path6.dirname(resolvedDest), { recursive: true });
|
|
13401
13568
|
try {
|
|
@@ -13531,22 +13698,23 @@ ${globalCapMatches(output)}` : "";
|
|
|
13531
13698
|
const linesBefore = origNorm.split("\n").length;
|
|
13532
13699
|
const linesAfter = updated.split("\n").length;
|
|
13533
13700
|
const xfile = crossFileBreakageWarning(resolved, origNorm, normalizeLF(updated), workDir ?? process.cwd());
|
|
13701
|
+
const sec = (0, securityLint_1.securityNoteText)((0, securityLint_1.checkSecurity)(newNorm));
|
|
13534
13702
|
return { output: `Edited ${resolved} (${linesBefore} \u2192 ${linesAfter} lines)
|
|
13535
13703
|
|
|
13536
|
-
${diff3}${xfile}` };
|
|
13704
|
+
${diff3}${xfile}${sec}` };
|
|
13537
13705
|
} catch (err) {
|
|
13538
13706
|
return { error: err.message };
|
|
13539
13707
|
}
|
|
13540
13708
|
}
|
|
13541
13709
|
async function copyFile(input, workDir) {
|
|
13542
|
-
const
|
|
13710
|
+
const source2 = typeof input.source === "string" ? input.source : "";
|
|
13543
13711
|
const destination = typeof input.destination === "string" ? input.destination : "";
|
|
13544
|
-
if (!
|
|
13712
|
+
if (!source2)
|
|
13545
13713
|
return { error: "Missing required parameter: source" };
|
|
13546
13714
|
if (!destination)
|
|
13547
13715
|
return { error: "Missing required parameter: destination" };
|
|
13548
13716
|
try {
|
|
13549
|
-
const resolvedSrc = resolvePath(
|
|
13717
|
+
const resolvedSrc = resolvePath(source2, workDir);
|
|
13550
13718
|
const resolvedDest = resolvePath(destination, workDir);
|
|
13551
13719
|
if (fs9.existsSync(resolvedDest) && fs9.statSync(resolvedDest).isDirectory()) {
|
|
13552
13720
|
return { error: `Destination is a directory: ${resolvedDest}. Provide a full file path including the filename.` };
|
|
@@ -13801,6 +13969,7 @@ ${body}${truncNote}
|
|
|
13801
13969
|
let content = normalizeLF(rawFile);
|
|
13802
13970
|
const originalNorm = content;
|
|
13803
13971
|
const diffs = [];
|
|
13972
|
+
const insertedText = [];
|
|
13804
13973
|
for (let i2 = 0; i2 < edits.length; i2++) {
|
|
13805
13974
|
const edit = edits[i2];
|
|
13806
13975
|
const oldStr = normalizeLF(typeof edit.old_string === "string" ? edit.old_string : "");
|
|
@@ -13815,16 +13984,18 @@ ${body}${truncNote}
|
|
|
13815
13984
|
return { error: `Edit #${i2 + 1}: old_string appears ${count} times \u2014 it must be unique. Add more surrounding context.` };
|
|
13816
13985
|
}
|
|
13817
13986
|
diffs.push(buildDiff(filePath, oldStr, newStr, content));
|
|
13987
|
+
insertedText.push(newStr);
|
|
13818
13988
|
content = literalReplace(content, oldStr, newStr);
|
|
13819
13989
|
}
|
|
13820
13990
|
const finalContent = wasCRLF ? content.replace(/\n/g, "\r\n") : content;
|
|
13821
13991
|
atomicWritePreservingMode(resolved, finalContent, pre.mode);
|
|
13822
13992
|
const xfile = crossFileBreakageWarning(resolved, originalNorm, content, workDir ?? process.cwd());
|
|
13993
|
+
const sec = (0, securityLint_1.securityNoteText)((0, securityLint_1.checkSecurity)(insertedText.join("\n")));
|
|
13823
13994
|
return {
|
|
13824
13995
|
output: `Applied ${edits.length} edit(s) to ${resolved}:
|
|
13825
13996
|
` + diffs.map((d, i2) => `
|
|
13826
13997
|
--- edit #${i2 + 1} ---
|
|
13827
|
-
${d}`).join("\n") + xfile
|
|
13998
|
+
${d}`).join("\n") + xfile + sec
|
|
13828
13999
|
};
|
|
13829
14000
|
} catch (err) {
|
|
13830
14001
|
return { error: err.message };
|
|
@@ -13989,7 +14160,7 @@ ${lines.join("\n")}`;
|
|
|
13989
14160
|
const editType = typeof input.edit_type === "string" ? input.edit_type : "";
|
|
13990
14161
|
const rawIdx = input.cell_index;
|
|
13991
14162
|
const cellIndex = typeof rawIdx === "number" ? Math.floor(rawIdx) : typeof rawIdx === "string" && rawIdx.trim() !== "" ? parseInt(rawIdx, 10) : -1;
|
|
13992
|
-
const
|
|
14163
|
+
const source2 = typeof input.source === "string" ? input.source : null;
|
|
13993
14164
|
const cellType = typeof input.cell_type === "string" ? input.cell_type : "code";
|
|
13994
14165
|
if (!filePath)
|
|
13995
14166
|
return { error: "Missing required parameter: path" };
|
|
@@ -14008,10 +14179,10 @@ ${lines.join("\n")}`;
|
|
|
14008
14179
|
if (editType === "replace") {
|
|
14009
14180
|
if (cellIndex < 0 || cellIndex >= n)
|
|
14010
14181
|
return { error: `cell_index ${cellIndex} out of range (0\u2013${n - 1})` };
|
|
14011
|
-
if (
|
|
14182
|
+
if (source2 === null)
|
|
14012
14183
|
return { error: "Missing required parameter: source (required for replace)" };
|
|
14013
14184
|
const cell = nb.cells[cellIndex];
|
|
14014
|
-
cell.source =
|
|
14185
|
+
cell.source = source2;
|
|
14015
14186
|
if (cell.cell_type === "code") {
|
|
14016
14187
|
cell.outputs = [];
|
|
14017
14188
|
cell.execution_count = null;
|
|
@@ -14026,12 +14197,12 @@ ${lines.join("\n")}`;
|
|
|
14026
14197
|
save();
|
|
14027
14198
|
return { output: `Deleted cell ${cellIndex} [${removed.cell_type}] from ${resolved}. Notebook now has ${nb.cells.length} cells.` };
|
|
14028
14199
|
}
|
|
14029
|
-
if (
|
|
14200
|
+
if (source2 === null)
|
|
14030
14201
|
return { error: "Missing required parameter: source (required for insert)" };
|
|
14031
14202
|
const insertAt = cellIndex >= 0 ? Math.min(cellIndex + 1, n) : n;
|
|
14032
14203
|
const newCell = {
|
|
14033
14204
|
cell_type: cellType,
|
|
14034
|
-
source,
|
|
14205
|
+
source: source2,
|
|
14035
14206
|
metadata: {},
|
|
14036
14207
|
...cellType === "code" ? { execution_count: null, outputs: [] } : {}
|
|
14037
14208
|
};
|
|
@@ -14398,18 +14569,62 @@ var require_agentTypes = __commonJS({
|
|
|
14398
14569
|
};
|
|
14399
14570
|
}();
|
|
14400
14571
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14401
|
-
exports.loadAgentTypes =
|
|
14572
|
+
exports.loadAgentTypes = loadAgentTypes;
|
|
14573
|
+
exports.loadAgentTypesWithWarnings = loadAgentTypesWithWarnings2;
|
|
14574
|
+
exports.knownToolNames = knownToolNames;
|
|
14575
|
+
exports.builtinAgents = builtinAgents;
|
|
14402
14576
|
exports.summariseAgents = summariseAgents;
|
|
14403
14577
|
exports.findAgentType = findAgentType;
|
|
14404
14578
|
var fs9 = __importStar(__require("fs"));
|
|
14405
14579
|
var path6 = __importStar(__require("path"));
|
|
14406
14580
|
var os6 = __importStar(__require("os"));
|
|
14407
14581
|
var index_1 = require_plugins();
|
|
14582
|
+
var READ_ONLY_TOOLS = [
|
|
14583
|
+
// Universal
|
|
14584
|
+
"read_file",
|
|
14585
|
+
"search_files",
|
|
14586
|
+
"glob",
|
|
14587
|
+
"list_directory",
|
|
14588
|
+
"bash",
|
|
14589
|
+
"bash_output",
|
|
14590
|
+
// kill_shell belongs next to bash_output: an agent that can start a background
|
|
14591
|
+
// process and poll it but never stop it leaks that process past its own
|
|
14592
|
+
// lifetime. Stopping a shell you started is not a write to the repo.
|
|
14593
|
+
"kill_shell",
|
|
14594
|
+
"notebook_read",
|
|
14595
|
+
"todo_write",
|
|
14596
|
+
"todo_read",
|
|
14597
|
+
// Skills are reusable prompt playbooks, and loop.ts advertises the skills
|
|
14598
|
+
// catalogue to sub-agents at EVERY depth — so withholding the tool that loads
|
|
14599
|
+
// one meant showing every sub-agent a menu it could not order from.
|
|
14600
|
+
"use_skill",
|
|
14601
|
+
// VS Code language server (ignored on the CLI)
|
|
14602
|
+
"get_symbols",
|
|
14603
|
+
"get_workspace_symbols",
|
|
14604
|
+
"find_references",
|
|
14605
|
+
"go_to_definition",
|
|
14606
|
+
"get_hover",
|
|
14607
|
+
"get_diagnostics"
|
|
14608
|
+
];
|
|
14609
|
+
var RESEARCH_TOOLS = [...READ_ONLY_TOOLS, "fetch_url"];
|
|
14610
|
+
var WRITE_TOOLS = [
|
|
14611
|
+
...READ_ONLY_TOOLS,
|
|
14612
|
+
"write_file",
|
|
14613
|
+
"edit_file",
|
|
14614
|
+
"multi_edit",
|
|
14615
|
+
"create_directory",
|
|
14616
|
+
"move_file",
|
|
14617
|
+
"copy_file",
|
|
14618
|
+
// notebook_edit is a write tool like any other, and allowsTestOnlyWrite already
|
|
14619
|
+
// knows its shape (`source` is cell CONTENT, not a path). Omitting it just meant
|
|
14620
|
+
// test-writer silently could not touch notebooks.
|
|
14621
|
+
"notebook_edit"
|
|
14622
|
+
];
|
|
14408
14623
|
var BUILTIN_AGENTS = [
|
|
14409
14624
|
{
|
|
14410
14625
|
name: "reviewer",
|
|
14411
14626
|
description: "Read-only code reviewer \u2014 finds correctness bugs, edge cases, and security issues in a diff or file set. Cannot modify files.",
|
|
14412
|
-
tools:
|
|
14627
|
+
tools: READ_ONLY_TOOLS,
|
|
14413
14628
|
source: "builtin",
|
|
14414
14629
|
prompt: [
|
|
14415
14630
|
"You are a meticulous senior code reviewer. You NEVER modify files \u2014 you only read, search, and report.",
|
|
@@ -14425,78 +14640,377 @@ var require_agentTypes = __commonJS({
|
|
|
14425
14640
|
"Report format: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F7E2} Suggestion, each with file:line and a concrete fix,",
|
|
14426
14641
|
"then a final verdict (APPROVE or REQUEST CHANGES) with a one-paragraph rationale."
|
|
14427
14642
|
].join("\n")
|
|
14643
|
+
},
|
|
14644
|
+
// Promoted from the security-audit plugin to a builtin.
|
|
14645
|
+
//
|
|
14646
|
+
// Leaving it plugin-only was indefensible next to `reviewer` being builtin:
|
|
14647
|
+
// reviewer's own prompt already tells it to look for security issues, so
|
|
14648
|
+
// security IS treated as default work — yet the specialist agent for it was
|
|
14649
|
+
// invisible unless the user happened to know the plugin existed. For an agent
|
|
14650
|
+
// that WRITES code, "you only get a security review if you knew to install
|
|
14651
|
+
// something" is the wrong default.
|
|
14652
|
+
{
|
|
14653
|
+
name: "security-auditor",
|
|
14654
|
+
description: "Read-only security auditor \u2014 hunts injection, authz, secrets, and validation flaws in a path or diff. Cannot modify files.",
|
|
14655
|
+
tools: READ_ONLY_TOOLS,
|
|
14656
|
+
model: "pro",
|
|
14657
|
+
source: "builtin",
|
|
14658
|
+
prompt: [
|
|
14659
|
+
"You are a security auditor. You find real, exploitable flaws \u2014 not style issues.",
|
|
14660
|
+
"",
|
|
14661
|
+
"Method:",
|
|
14662
|
+
"1. Map the attack surface FIRST: entry points (HTTP routes, message handlers, CLI args, file/network",
|
|
14663
|
+
" input, deserialization), then trace user-controlled data inward to where it is used.",
|
|
14664
|
+
"2. For each finding: file:line, the flaw class, a one-line exploit scenario, and the concrete fix.",
|
|
14665
|
+
"3. Grade severity honestly: Critical = remote compromise or data breach; High = auth bypass/IDOR;",
|
|
14666
|
+
" Medium = needs unusual preconditions; Low = hardening.",
|
|
14667
|
+
"",
|
|
14668
|
+
"Classes worth the most attention, in order: injection (SQL/command/template/prototype), broken",
|
|
14669
|
+
"authz (missing ownership checks, IDOR, trusting client-supplied ids), secrets committed to source,",
|
|
14670
|
+
"path traversal, SSRF, unsafe deserialization, missing rate limits on expensive or auth endpoints,",
|
|
14671
|
+
"and crypto misuse (hand-rolled comparison, predictable randomness).",
|
|
14672
|
+
"",
|
|
14673
|
+
"Hard rules:",
|
|
14674
|
+
"- READ-ONLY: never modify, create or delete files. bash only for read-only inspection.",
|
|
14675
|
+
"- NEVER print a discovered secret's value. Report its location and advise rotation.",
|
|
14676
|
+
"- Distinguish EXPLOITABLE from theoretical, and say which one each finding is.",
|
|
14677
|
+
'- "No issues found in scope X" is a valid, useful result. Do not pad the report to look thorough.'
|
|
14678
|
+
].join("\n")
|
|
14679
|
+
},
|
|
14680
|
+
// The gap Claude Code fills with its built-in `Explore`: read-heavy codebase
|
|
14681
|
+
// search that would otherwise flood the parent's context. Defaults to the
|
|
14682
|
+
// cheapest model on purpose — "find every caller of X" has no need of a
|
|
14683
|
+
// frontier model, and this is the agent most likely to be spawned in bulk.
|
|
14684
|
+
{
|
|
14685
|
+
name: "explorer",
|
|
14686
|
+
description: "Fast read-only codebase explorer \u2014 locates files, symbols, and call sites and reports concise findings. Use to keep bulk searching out of the main context. Cannot modify files.",
|
|
14687
|
+
tools: READ_ONLY_TOOLS,
|
|
14688
|
+
model: "turbo",
|
|
14689
|
+
source: "builtin",
|
|
14690
|
+
prompt: [
|
|
14691
|
+
"You map code. You NEVER modify anything.",
|
|
14692
|
+
"",
|
|
14693
|
+
"Method:",
|
|
14694
|
+
"1. Prefer structural search over text search where available (get_workspace_symbols, find_references,",
|
|
14695
|
+
" go_to_definition); fall back to search_files/glob otherwise.",
|
|
14696
|
+
"2. Read only the sections you need \u2014 use read_file with offset/limit on large files instead of",
|
|
14697
|
+
" pulling in thousands of lines.",
|
|
14698
|
+
"3. Follow the real call graph rather than guessing from names.",
|
|
14699
|
+
"",
|
|
14700
|
+
"Your ONLY output is a compact report: the file:line locations that matter, how they relate, and the",
|
|
14701
|
+
"direct answer to the question you were given. This exists to keep bulk search OUT of the parent's",
|
|
14702
|
+
"context, so do not paste large file contents back \u2014 cite locations and summarise. Say plainly when",
|
|
14703
|
+
'something does not exist; a confident wrong answer is far worse than "not found".'
|
|
14704
|
+
].join("\n")
|
|
14705
|
+
},
|
|
14706
|
+
// Matches Claude Code's built-in `Plan`: research a change and return a
|
|
14707
|
+
// strategy, deliberately WITHOUT write access so "make a plan" can never
|
|
14708
|
+
// quietly become "start editing".
|
|
14709
|
+
{
|
|
14710
|
+
name: "planner",
|
|
14711
|
+
description: "Read-only planning agent \u2014 researches a change and returns a concrete step-by-step implementation plan with risks and affected files. Cannot modify files.",
|
|
14712
|
+
tools: RESEARCH_TOOLS,
|
|
14713
|
+
model: "pro",
|
|
14714
|
+
source: "builtin",
|
|
14715
|
+
prompt: [
|
|
14716
|
+
"You produce implementation plans. You NEVER modify files \u2014 planning and doing are separate steps,",
|
|
14717
|
+
'and this agent exists so "plan it" cannot silently turn into "change it".',
|
|
14718
|
+
"",
|
|
14719
|
+
"Method:",
|
|
14720
|
+
"1. Read the actual code before proposing anything. No plan may rest on an assumed API shape.",
|
|
14721
|
+
"2. Find every affected call site (find_references / search_files) and list them.",
|
|
14722
|
+
"3. Order the steps so the tree stays working after each one \u2014 types, then implementation, then",
|
|
14723
|
+
" tests, then exports/registration.",
|
|
14724
|
+
"",
|
|
14725
|
+
"Output:",
|
|
14726
|
+
"- Goal, in one sentence.",
|
|
14727
|
+
"- Numbered steps, each with the exact files touched and what changes in them.",
|
|
14728
|
+
"- Risks + the specific thing that could break, and how it would be detected.",
|
|
14729
|
+
"- How to verify (the exact test/build command for THIS project, taken from package.json/Makefile).",
|
|
14730
|
+
"- Anything genuinely ambiguous, stated as an open question rather than a silent assumption."
|
|
14731
|
+
].join("\n")
|
|
14732
|
+
},
|
|
14733
|
+
// Promoted from the test-gen plugin. Needs write access — it produces test
|
|
14734
|
+
// files — but is deliberately forbidden from touching source, because "make the
|
|
14735
|
+
// tests pass" is the single most common way an agent destroys signal.
|
|
14736
|
+
{
|
|
14737
|
+
name: "test-writer",
|
|
14738
|
+
description: "Writes tests that follow the project's existing conventions. May create/edit TEST files only \u2014 never production source.",
|
|
14739
|
+
tools: WRITE_TOOLS,
|
|
14740
|
+
// Enforced, not merely requested: the permission gate refuses a write whose
|
|
14741
|
+
// path is not a test file. Without this the allowlist would grant edit_file
|
|
14742
|
+
// for every path and the rule below would be a suggestion the model is free
|
|
14743
|
+
// to rationalise its way past.
|
|
14744
|
+
testFilesOnly: true,
|
|
14745
|
+
source: "builtin",
|
|
14746
|
+
prompt: [
|
|
14747
|
+
"You write tests. You may create and edit TEST files only.",
|
|
14748
|
+
"",
|
|
14749
|
+
"Hard rules \u2014 these are the ways test-writing agents destroy value, so they are non-negotiable:",
|
|
14750
|
+
"- NEVER modify production source to make a test pass. If the code looks wrong, REPORT it and stop.",
|
|
14751
|
+
"- NEVER weaken, delete or skip an existing assertion or test.",
|
|
14752
|
+
"- A test that cannot fail is worse than no test. Every test must be able to fail for one clear reason.",
|
|
14753
|
+
"",
|
|
14754
|
+
"Method:",
|
|
14755
|
+
"1. Read the existing tests FIRST and copy their conventions exactly \u2014 runner, file naming, layout,",
|
|
14756
|
+
" assertion style, fixture/helper patterns. Never introduce a new framework.",
|
|
14757
|
+
"2. Test observable behaviour and the contract, not private internals.",
|
|
14758
|
+
"3. Cover the boring-but-real cases: empty input, null/undefined, unicode and non-BMP characters,",
|
|
14759
|
+
" boundaries, error paths, concurrency where it applies.",
|
|
14760
|
+
"4. No sleeps or wall-clock dependence \u2014 those produce the flaky tests that get deleted later.",
|
|
14761
|
+
"5. RUN the tests you wrote and report the real output. Never claim a test passes without running it."
|
|
14762
|
+
].join("\n")
|
|
14763
|
+
},
|
|
14764
|
+
// The DevOps gap — answered with a READ-ONLY advisor, not an operator.
|
|
14765
|
+
//
|
|
14766
|
+
// A "DevOps agent" with write/apply access is a genuinely different risk class
|
|
14767
|
+
// from the others here: its mistakes are `kubectl delete`, a bad `terraform
|
|
14768
|
+
// apply`, a broken deploy pipeline — often not revertible and affecting
|
|
14769
|
+
// production rather than a working tree. So this one diagnoses and proposes a
|
|
14770
|
+
// diff; a human applies it. That asymmetry is the whole design.
|
|
14771
|
+
{
|
|
14772
|
+
name: "devops-advisor",
|
|
14773
|
+
description: "Read-only CI/CD, container, and infrastructure advisor \u2014 diagnoses pipelines, Dockerfiles, and k8s manifests and proposes concrete fixes as a diff. Never applies changes.",
|
|
14774
|
+
tools: RESEARCH_TOOLS,
|
|
14775
|
+
model: "pro",
|
|
14776
|
+
source: "builtin",
|
|
14777
|
+
prompt: [
|
|
14778
|
+
"You are an infrastructure and delivery advisor. You DIAGNOSE and PROPOSE. You never apply changes.",
|
|
14779
|
+
"",
|
|
14780
|
+
"Hard rules:",
|
|
14781
|
+
"- READ-ONLY, and stricter than the other read-only agents: bash is for INSPECTION only",
|
|
14782
|
+
" (git log/diff, cat, grep, `kubectl get/describe`, `docker images`, `terraform plan`).",
|
|
14783
|
+
" NEVER run anything that mutates infrastructure \u2014 no apply/delete/scale/rollout/restart/push,",
|
|
14784
|
+
" no `terraform apply`, no `helm upgrade`. If a fix needs such a command, WRITE IT OUT for a human.",
|
|
14785
|
+
"- Never print secret values from env files, k8s Secrets or CI variables. Reference them by name.",
|
|
14786
|
+
"",
|
|
14787
|
+
"Method:",
|
|
14788
|
+
"1. Read what actually exists \u2014 workflow files, Dockerfiles, manifests, kustomize overlays, the",
|
|
14789
|
+
' deploy scripts \u2014 before drawing any conclusion. Never reason from what a stack "usually" looks like.',
|
|
14790
|
+
"2. Follow the real path a change takes to production, and name the step that is broken or missing.",
|
|
14791
|
+
"3. Check the failure modes that bite hardest: CI path filters that skip files a workload actually",
|
|
14792
|
+
" needs, image tags that do not match what is deployed, missing health probes, absent resource",
|
|
14793
|
+
" limits, secrets baked into images, ports/timeouts inconsistent between proxy and app, and",
|
|
14794
|
+
" migrations that must run before the new image is live.",
|
|
14795
|
+
"",
|
|
14796
|
+
"Output: the diagnosis, the evidence (file:line or command output), the proposed change as a diff or",
|
|
14797
|
+
"exact file content, and the command a human should run to apply and verify it."
|
|
14798
|
+
].join("\n")
|
|
14428
14799
|
}
|
|
14429
14800
|
];
|
|
14801
|
+
var KNOWN_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
14802
|
+
"read_file",
|
|
14803
|
+
"write_file",
|
|
14804
|
+
"edit_file",
|
|
14805
|
+
"multi_edit",
|
|
14806
|
+
"list_directory",
|
|
14807
|
+
"create_directory",
|
|
14808
|
+
"move_file",
|
|
14809
|
+
"copy_file",
|
|
14810
|
+
"delete_file",
|
|
14811
|
+
"search_files",
|
|
14812
|
+
"glob",
|
|
14813
|
+
"bash",
|
|
14814
|
+
"bash_output",
|
|
14815
|
+
"kill_shell",
|
|
14816
|
+
"notebook_read",
|
|
14817
|
+
"notebook_edit",
|
|
14818
|
+
"todo_write",
|
|
14819
|
+
"todo_read",
|
|
14820
|
+
"memory_write",
|
|
14821
|
+
"memory_read",
|
|
14822
|
+
"use_skill",
|
|
14823
|
+
"fetch_url",
|
|
14824
|
+
"web_search",
|
|
14825
|
+
"generate_image",
|
|
14826
|
+
"stock_photo",
|
|
14827
|
+
"open_in_browser",
|
|
14828
|
+
"task",
|
|
14829
|
+
"get_symbols",
|
|
14830
|
+
"get_workspace_symbols",
|
|
14831
|
+
"find_references",
|
|
14832
|
+
"go_to_definition",
|
|
14833
|
+
"get_hover",
|
|
14834
|
+
"get_diagnostics"
|
|
14835
|
+
]);
|
|
14836
|
+
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
14837
|
+
"name",
|
|
14838
|
+
"description",
|
|
14839
|
+
"tools",
|
|
14840
|
+
"model",
|
|
14841
|
+
"test_files_only",
|
|
14842
|
+
"testfilesonly"
|
|
14843
|
+
]);
|
|
14844
|
+
var VALID_MODELS = ["turbo", "pro", "ultra"];
|
|
14430
14845
|
function parseFrontmatter(raw) {
|
|
14431
14846
|
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
14432
14847
|
if (!m2)
|
|
14433
|
-
return { meta: {}, body: raw.trim() };
|
|
14848
|
+
return { meta: {}, body: raw.trim(), ok: false };
|
|
14434
14849
|
const meta = {};
|
|
14435
14850
|
for (const line of m2[1].split(/\r?\n/)) {
|
|
14436
14851
|
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
14437
14852
|
if (kv)
|
|
14438
14853
|
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
|
|
14439
14854
|
}
|
|
14440
|
-
return { meta, body: (m2[2] ?? "").trim() };
|
|
14855
|
+
return { meta, body: (m2[2] ?? "").trim(), ok: true };
|
|
14441
14856
|
}
|
|
14442
14857
|
function parseModel(v) {
|
|
14443
14858
|
const s2 = (v ?? "").toLowerCase();
|
|
14444
14859
|
return s2 === "turbo" || s2 === "pro" || s2 === "ultra" ? s2 : void 0;
|
|
14445
14860
|
}
|
|
14861
|
+
function parseBool(v) {
|
|
14862
|
+
return /^(true|yes|1|on)$/i.test((v ?? "").trim());
|
|
14863
|
+
}
|
|
14446
14864
|
function parseToolList(v) {
|
|
14447
14865
|
if (!v)
|
|
14448
14866
|
return void 0;
|
|
14449
14867
|
const tools = v.replace(/^\[|\]$/g, "").split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
|
|
14450
14868
|
return tools.length ? tools : void 0;
|
|
14451
14869
|
}
|
|
14452
|
-
function loadDir(dir,
|
|
14870
|
+
function loadDir(dir, source2, into, warnings) {
|
|
14453
14871
|
let entries;
|
|
14454
14872
|
try {
|
|
14455
|
-
entries = fs9.readdirSync(dir
|
|
14873
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
14456
14874
|
} catch {
|
|
14457
14875
|
return;
|
|
14458
14876
|
}
|
|
14459
|
-
for (const
|
|
14877
|
+
for (const entry of entries) {
|
|
14878
|
+
const full = path6.join(dir, entry.name);
|
|
14879
|
+
if (entry.isDirectory()) {
|
|
14880
|
+
loadDir(full, source2, into, warnings);
|
|
14881
|
+
continue;
|
|
14882
|
+
}
|
|
14883
|
+
if (!entry.name.endsWith(".md"))
|
|
14884
|
+
continue;
|
|
14885
|
+
let raw;
|
|
14460
14886
|
try {
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
|
|
14465
|
-
|
|
14466
|
-
|
|
14467
|
-
|
|
14468
|
-
|
|
14469
|
-
|
|
14470
|
-
|
|
14471
|
-
|
|
14472
|
-
|
|
14473
|
-
|
|
14474
|
-
|
|
14887
|
+
raw = fs9.readFileSync(full, "utf-8");
|
|
14888
|
+
} catch (err) {
|
|
14889
|
+
warnings.push({ file: full, agent: path6.basename(entry.name, ".md"), message: `could not be read (${err.message}) \u2014 this agent was skipped` });
|
|
14890
|
+
continue;
|
|
14891
|
+
}
|
|
14892
|
+
const { meta, body, ok } = parseFrontmatter(raw);
|
|
14893
|
+
const name = (meta.name || path6.basename(entry.name, ".md")).trim();
|
|
14894
|
+
if (!name)
|
|
14895
|
+
continue;
|
|
14896
|
+
if (source2 !== "project" && into.has(name))
|
|
14897
|
+
continue;
|
|
14898
|
+
const tools = parseToolList(meta.tools);
|
|
14899
|
+
let effectiveTools = tools;
|
|
14900
|
+
if (!ok) {
|
|
14901
|
+
effectiveTools = READ_ONLY_TOOLS;
|
|
14902
|
+
warnings.push({
|
|
14903
|
+
file: full,
|
|
14904
|
+
agent: name,
|
|
14905
|
+
message: "has no valid YAML frontmatter (a `---` block must be the first thing in the file), so no tool allowlist could be read. Treating it as READ-ONLY. Add frontmatter with a `tools:` line to grant more."
|
|
14475
14906
|
});
|
|
14476
|
-
}
|
|
14907
|
+
} else {
|
|
14908
|
+
if (!meta.description) {
|
|
14909
|
+
warnings.push({
|
|
14910
|
+
file: full,
|
|
14911
|
+
agent: name,
|
|
14912
|
+
message: "has no `description:` \u2014 that text is the ONLY thing the model uses to decide when to delegate to this agent, so it will rarely be picked."
|
|
14913
|
+
});
|
|
14914
|
+
}
|
|
14915
|
+
if (meta.model !== void 0 && parseModel(meta.model) === void 0) {
|
|
14916
|
+
warnings.push({
|
|
14917
|
+
file: full,
|
|
14918
|
+
agent: name,
|
|
14919
|
+
message: `has model: "${meta.model}", which is not valid \u2014 use one of ${VALID_MODELS.join(", ")}, or omit the line to inherit the current session's model.`
|
|
14920
|
+
});
|
|
14921
|
+
}
|
|
14922
|
+
const unknown = (tools ?? []).filter((t2) => !KNOWN_TOOL_NAMES.has(t2) && !t2.includes("__"));
|
|
14923
|
+
if (unknown.length) {
|
|
14924
|
+
warnings.push({
|
|
14925
|
+
file: full,
|
|
14926
|
+
agent: name,
|
|
14927
|
+
message: `lists unknown tool name(s): ${unknown.join(", ")}. An allowlist only grants, so these silently do nothing and the agent cannot use them. Tool names are lower_snake_case (read_file, search_files, glob, bash).`
|
|
14928
|
+
});
|
|
14929
|
+
}
|
|
14930
|
+
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
14931
|
+
if (strayKeys.length) {
|
|
14932
|
+
warnings.push({
|
|
14933
|
+
file: full,
|
|
14934
|
+
agent: name,
|
|
14935
|
+
message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.`
|
|
14936
|
+
});
|
|
14937
|
+
}
|
|
14938
|
+
if (!body) {
|
|
14939
|
+
warnings.push({
|
|
14940
|
+
file: full,
|
|
14941
|
+
agent: name,
|
|
14942
|
+
message: "has an empty body \u2014 the text below the frontmatter IS the agent's system prompt, so it currently has no instructions."
|
|
14943
|
+
});
|
|
14944
|
+
}
|
|
14477
14945
|
}
|
|
14946
|
+
into.set(name, {
|
|
14947
|
+
name,
|
|
14948
|
+
description: meta.description || `Custom ${name} agent`,
|
|
14949
|
+
tools: effectiveTools,
|
|
14950
|
+
model: parseModel(meta.model),
|
|
14951
|
+
prompt: body,
|
|
14952
|
+
source: source2,
|
|
14953
|
+
// Exposed to user/plugin definitions too — `test_files_only: true` (or
|
|
14954
|
+
// `testFilesOnly`) lets anyone build a test-writing agent that genuinely
|
|
14955
|
+
// cannot touch production source, rather than only the builtin getting
|
|
14956
|
+
// that guarantee.
|
|
14957
|
+
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {}
|
|
14958
|
+
});
|
|
14478
14959
|
}
|
|
14479
14960
|
}
|
|
14480
|
-
function
|
|
14961
|
+
function loadAgentTypes(workDir) {
|
|
14962
|
+
return loadAgentTypesWithWarnings2(workDir).types;
|
|
14963
|
+
}
|
|
14964
|
+
function loadAgentTypesWithWarnings2(workDir) {
|
|
14481
14965
|
const out = /* @__PURE__ */ new Map();
|
|
14482
|
-
|
|
14483
|
-
loadDir(path6.join(
|
|
14966
|
+
const warnings = [];
|
|
14967
|
+
loadDir(path6.join(workDir, ".nexrall", "agents"), "project", out, warnings);
|
|
14968
|
+
loadDir(path6.join(os6.homedir(), ".nexrall", "agents"), "global", out, warnings);
|
|
14484
14969
|
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "agents"))
|
|
14485
|
-
loadDir(dir, "plugin", out);
|
|
14970
|
+
loadDir(dir, "plugin", out, warnings);
|
|
14486
14971
|
for (const agent of BUILTIN_AGENTS) {
|
|
14487
14972
|
if (!out.has(agent.name))
|
|
14488
14973
|
out.set(agent.name, agent);
|
|
14489
14974
|
}
|
|
14490
|
-
|
|
14975
|
+
const builtinOrder = new Map(BUILTIN_AGENTS.map((a, i2) => [a.name, i2]));
|
|
14976
|
+
const types3 = [...out.values()].sort((a, b) => {
|
|
14977
|
+
const ai = builtinOrder.get(a.name);
|
|
14978
|
+
const bi = builtinOrder.get(b.name);
|
|
14979
|
+
if (ai !== void 0 && bi !== void 0)
|
|
14980
|
+
return ai - bi;
|
|
14981
|
+
if (ai !== void 0)
|
|
14982
|
+
return -1;
|
|
14983
|
+
if (bi !== void 0)
|
|
14984
|
+
return 1;
|
|
14985
|
+
return a.name.localeCompare(b.name);
|
|
14986
|
+
});
|
|
14987
|
+
return { types: types3, warnings };
|
|
14988
|
+
}
|
|
14989
|
+
function knownToolNames() {
|
|
14990
|
+
return [...KNOWN_TOOL_NAMES].sort();
|
|
14991
|
+
}
|
|
14992
|
+
function builtinAgents() {
|
|
14993
|
+
return BUILTIN_AGENTS;
|
|
14491
14994
|
}
|
|
14492
14995
|
function summariseAgents(types3) {
|
|
14493
14996
|
if (!types3.length)
|
|
14494
14997
|
return "";
|
|
14495
14998
|
return types3.map((t2) => {
|
|
14496
|
-
const
|
|
14497
|
-
|
|
14999
|
+
const canWrite = !t2.tools || t2.tools.some((x2) => WRITE_TOOL_HINTS.has(x2));
|
|
15000
|
+
const access = t2.testFilesOnly ? "writes TEST files only" : canWrite ? "can modify files" : "read-only";
|
|
15001
|
+
const model = t2.model ? `, ${t2.model} model` : "";
|
|
15002
|
+
return `- ${t2.name} (${access}${model}): ${t2.description}`;
|
|
14498
15003
|
}).join("\n");
|
|
14499
15004
|
}
|
|
15005
|
+
var WRITE_TOOL_HINTS = /* @__PURE__ */ new Set([
|
|
15006
|
+
"write_file",
|
|
15007
|
+
"edit_file",
|
|
15008
|
+
"multi_edit",
|
|
15009
|
+
"notebook_edit",
|
|
15010
|
+
"delete_file",
|
|
15011
|
+
"move_file",
|
|
15012
|
+
"copy_file"
|
|
15013
|
+
]);
|
|
14500
15014
|
function findAgentType(types3, name) {
|
|
14501
15015
|
if (!name)
|
|
14502
15016
|
return void 0;
|
|
@@ -14896,7 +15410,7 @@ var require_loop = __commonJS({
|
|
|
14896
15410
|
};
|
|
14897
15411
|
}();
|
|
14898
15412
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14899
|
-
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = void 0;
|
|
15413
|
+
exports.VERIFY_CMD_RE = exports.WRITE_TOOL_NAMES = exports.ToolNotAllowedError = void 0;
|
|
14900
15414
|
exports.resolveMaxIterations = resolveMaxIterations;
|
|
14901
15415
|
exports.createLimiter = createLimiter;
|
|
14902
15416
|
exports.extractSubTaskText = extractSubTaskText;
|
|
@@ -14905,6 +15419,7 @@ var require_loop = __commonJS({
|
|
|
14905
15419
|
exports.contextWindowFor = contextWindowFor2;
|
|
14906
15420
|
exports.compactionThresholds = compactionThresholds2;
|
|
14907
15421
|
exports.estimateBodyBytes = estimateBodyBytes2;
|
|
15422
|
+
exports.allowsTestOnlyWrite = allowsTestOnlyWrite;
|
|
14908
15423
|
exports.findSafeCutIndex = findSafeCutIndex;
|
|
14909
15424
|
exports.transcriptOf = transcriptOf;
|
|
14910
15425
|
exports.createLedger = createLedger;
|
|
@@ -15172,6 +15687,13 @@ var require_loop = __commonJS({
|
|
|
15172
15687
|
var _subTaskCounter = 0;
|
|
15173
15688
|
var SUBTASK_TIMEOUT_MS = Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) > 0 ? Number(process.env.NEXRALL_SUBTASK_TIMEOUT_MS) : 10 * 60 * 1e3;
|
|
15174
15689
|
var SUBTASK_MAX = 48e3;
|
|
15690
|
+
var ToolNotAllowedError = class extends Error {
|
|
15691
|
+
constructor(message) {
|
|
15692
|
+
super(message);
|
|
15693
|
+
this.name = "ToolNotAllowedError";
|
|
15694
|
+
}
|
|
15695
|
+
};
|
|
15696
|
+
exports.ToolNotAllowedError = ToolNotAllowedError;
|
|
15175
15697
|
function sliceSafeEnd(s2, max) {
|
|
15176
15698
|
if (s2.length <= max)
|
|
15177
15699
|
return s2;
|
|
@@ -15235,10 +15757,18 @@ ${tail}`;
|
|
|
15235
15757
|
return { error: "Sub-agents cannot spawn further sub-agents. Do this work directly, or report back so the main agent can delegate it." };
|
|
15236
15758
|
}
|
|
15237
15759
|
const requestedType = typeof input.subagent_type === "string" ? input.subagent_type : "";
|
|
15238
|
-
|
|
15760
|
+
let agent = (0, agentTypes_1.findAgentType)(agentTypes, requestedType);
|
|
15761
|
+
let knownTypes = agentTypes;
|
|
15762
|
+
if (requestedType && !agent) {
|
|
15763
|
+
knownTypes = (0, agentTypes_1.loadAgentTypes)(options.workDir);
|
|
15764
|
+
agent = (0, agentTypes_1.findAgentType)(knownTypes, requestedType);
|
|
15765
|
+
}
|
|
15239
15766
|
if (requestedType && !agent) {
|
|
15240
|
-
const known =
|
|
15241
|
-
return {
|
|
15767
|
+
const known = knownTypes.map((a) => a.name).join(", ") || "(none defined)";
|
|
15768
|
+
return {
|
|
15769
|
+
error: `Unknown subagent_type "${requestedType}". Available types: ${known}.
|
|
15770
|
+
If you just created .nexrall/agents/` + requestedType + ".md, make sure the write finished in an EARLIER tool call than this one \u2014 a file written in the same batch may not be on disk yet."
|
|
15771
|
+
};
|
|
15242
15772
|
}
|
|
15243
15773
|
const subNexrallMd = agent ? `# Sub-agent role: ${agent.name}
|
|
15244
15774
|
${agent.prompt}` + (options.nexrallMd ? `
|
|
@@ -15248,8 +15778,12 @@ ${agent.prompt}` + (options.nexrallMd ? `
|
|
|
15248
15778
|
${options.nexrallMd}` : "") : options.nexrallMd;
|
|
15249
15779
|
const allowed = agent?.tools ? new Set(agent.tools) : null;
|
|
15250
15780
|
const gatedPermission = async (req) => {
|
|
15251
|
-
if (allowed && !allowed.has(req.tool))
|
|
15252
|
-
|
|
15781
|
+
if (allowed && !allowed.has(req.tool)) {
|
|
15782
|
+
throw new ToolNotAllowedError(`The "${agent.name}" sub-agent is not allowed to use \`${req.tool}\` \u2014 it is not in that agent's tool allowlist. This is a restriction of the agent definition, NOT a user decision: do not ask for approval, use one of the tools you do have, or report back that the task needs a different agent.`);
|
|
15783
|
+
}
|
|
15784
|
+
if (agent?.testFilesOnly && !allowsTestOnlyWrite(req.tool, req.input)) {
|
|
15785
|
+
throw new ToolNotAllowedError(`The "${agent.name}" sub-agent may only write to TEST files, so \`${req.tool}\` was refused for this path. Do not try to work around it: if production code must change, say so in your report instead.`);
|
|
15786
|
+
}
|
|
15253
15787
|
return options.requestPermission(req);
|
|
15254
15788
|
};
|
|
15255
15789
|
const subMessages = [
|
|
@@ -15394,6 +15928,15 @@ ${partial}` : "",
|
|
|
15394
15928
|
return true;
|
|
15395
15929
|
}
|
|
15396
15930
|
exports.WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["write_file", "edit_file", "multi_edit", "delete_file", "move_file", "copy_file", "notebook_edit"]);
|
|
15931
|
+
function allowsTestOnlyWrite(tool, input) {
|
|
15932
|
+
if (!exports.WRITE_TOOL_NAMES.has(tool))
|
|
15933
|
+
return true;
|
|
15934
|
+
const pathKeys = tool === "notebook_edit" ? ["path"] : ["path", "source", "dest", "destination"];
|
|
15935
|
+
const candidates = pathKeys.map((k) => input?.[k]).filter((v) => typeof v === "string" && v.length > 0);
|
|
15936
|
+
if (candidates.length === 0)
|
|
15937
|
+
return false;
|
|
15938
|
+
return candidates.every((p) => (0, testIntegrity_1.isTestFile)(p));
|
|
15939
|
+
}
|
|
15397
15940
|
exports.VERIFY_CMD_RE = /\b(npm|yarn|pnpm)\s+(run\s+)?(test|build|lint|typecheck|tsc)\b|\bpytest\b|\bgo\s+(test|vet|build)\b|\btsc\b|\beslint\b|\bcargo\s+(test|build|check)\b/i;
|
|
15398
15941
|
function findSafeCutIndex(messages, maxIdx) {
|
|
15399
15942
|
for (let i2 = Math.min(maxIdx, messages.length - 1); i2 >= 2; i2--) {
|
|
@@ -15926,13 +16469,16 @@ Continue the work from here.` }] });
|
|
|
15926
16469
|
}
|
|
15927
16470
|
const description = humanDescription(name, input);
|
|
15928
16471
|
let permitted;
|
|
16472
|
+
let deniedReason = null;
|
|
15929
16473
|
try {
|
|
15930
16474
|
permitted = await options.requestPermission({ tool: name, input, description });
|
|
15931
|
-
} catch {
|
|
16475
|
+
} catch (err) {
|
|
15932
16476
|
permitted = false;
|
|
16477
|
+
if (err instanceof ToolNotAllowedError)
|
|
16478
|
+
deniedReason = err.message;
|
|
15933
16479
|
}
|
|
15934
16480
|
if (!permitted) {
|
|
15935
|
-
result = { error: "Permission denied by user" };
|
|
16481
|
+
result = { error: deniedReason ?? "Permission denied by user" };
|
|
15936
16482
|
} else if (name === "task") {
|
|
15937
16483
|
result = depth === 0 ? await _subTaskLimit(() => runSubTask(input, options, agentTypes)) : await runSubTask(input, options, agentTypes);
|
|
15938
16484
|
} else {
|
|
@@ -17373,6 +17919,175 @@ var require_destructive = __commonJS({
|
|
|
17373
17919
|
}
|
|
17374
17920
|
});
|
|
17375
17921
|
|
|
17922
|
+
// ../core/dist/plugins/sources.js
|
|
17923
|
+
var require_sources = __commonJS({
|
|
17924
|
+
"../core/dist/plugins/sources.js"(exports) {
|
|
17925
|
+
"use strict";
|
|
17926
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
17927
|
+
if (k2 === void 0)
|
|
17928
|
+
k2 = k;
|
|
17929
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
17930
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
17931
|
+
desc = { enumerable: true, get: function() {
|
|
17932
|
+
return m2[k];
|
|
17933
|
+
} };
|
|
17934
|
+
}
|
|
17935
|
+
Object.defineProperty(o, k2, desc);
|
|
17936
|
+
} : function(o, m2, k, k2) {
|
|
17937
|
+
if (k2 === void 0)
|
|
17938
|
+
k2 = k;
|
|
17939
|
+
o[k2] = m2[k];
|
|
17940
|
+
});
|
|
17941
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
17942
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17943
|
+
} : function(o, v) {
|
|
17944
|
+
o["default"] = v;
|
|
17945
|
+
});
|
|
17946
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
17947
|
+
var ownKeys = function(o) {
|
|
17948
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
17949
|
+
var ar = [];
|
|
17950
|
+
for (var k in o2)
|
|
17951
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
17952
|
+
ar[ar.length] = k;
|
|
17953
|
+
return ar;
|
|
17954
|
+
};
|
|
17955
|
+
return ownKeys(o);
|
|
17956
|
+
};
|
|
17957
|
+
return function(mod) {
|
|
17958
|
+
if (mod && mod.__esModule)
|
|
17959
|
+
return mod;
|
|
17960
|
+
var result = {};
|
|
17961
|
+
if (mod != null) {
|
|
17962
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
17963
|
+
if (k[i2] !== "default")
|
|
17964
|
+
__createBinding(result, mod, k[i2]);
|
|
17965
|
+
}
|
|
17966
|
+
__setModuleDefault(result, mod);
|
|
17967
|
+
return result;
|
|
17968
|
+
};
|
|
17969
|
+
}();
|
|
17970
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17971
|
+
exports.loadDeclaredSources = loadDeclaredSources2;
|
|
17972
|
+
exports.allowedSourcePatterns = allowedSourcePatterns2;
|
|
17973
|
+
exports.isSourceAllowed = isSourceAllowed2;
|
|
17974
|
+
exports.resolveSourceAlias = resolveSourceAlias2;
|
|
17975
|
+
exports.writeDeclaredSource = writeDeclaredSource2;
|
|
17976
|
+
exports.removeDeclaredSource = removeDeclaredSource2;
|
|
17977
|
+
exports.sourcesFileFor = sourcesFileFor2;
|
|
17978
|
+
var fs9 = __importStar(__require("fs"));
|
|
17979
|
+
var path6 = __importStar(__require("path"));
|
|
17980
|
+
var os6 = __importStar(__require("os"));
|
|
17981
|
+
function managedSettingsPath() {
|
|
17982
|
+
if (process.platform === "darwin")
|
|
17983
|
+
return "/Library/Application Support/Nexrall/managed-settings.json";
|
|
17984
|
+
if (process.platform === "win32")
|
|
17985
|
+
return path6.join(process.env.PROGRAMDATA ?? "C:\\ProgramData", "Nexrall", "managed-settings.json");
|
|
17986
|
+
return "/etc/nexrall/managed-settings.json";
|
|
17987
|
+
}
|
|
17988
|
+
function readJson(file) {
|
|
17989
|
+
try {
|
|
17990
|
+
return JSON.parse(fs9.readFileSync(file, "utf-8"));
|
|
17991
|
+
} catch {
|
|
17992
|
+
return null;
|
|
17993
|
+
}
|
|
17994
|
+
}
|
|
17995
|
+
var ALIAS_RE = /^[a-z0-9][\w.-]{0,63}$/i;
|
|
17996
|
+
function readSpec(value) {
|
|
17997
|
+
if (typeof value === "string")
|
|
17998
|
+
return value.trim() || null;
|
|
17999
|
+
if (value && typeof value === "object") {
|
|
18000
|
+
const v = value;
|
|
18001
|
+
const inner = v.source ?? v.repo ?? v.url;
|
|
18002
|
+
if (typeof inner === "string")
|
|
18003
|
+
return inner.trim() || null;
|
|
18004
|
+
if (inner && typeof inner === "object") {
|
|
18005
|
+
const deep = inner.repo ?? inner.url;
|
|
18006
|
+
if (typeof deep === "string")
|
|
18007
|
+
return deep.trim() || null;
|
|
18008
|
+
}
|
|
18009
|
+
}
|
|
18010
|
+
return null;
|
|
18011
|
+
}
|
|
18012
|
+
function collect(file, tier, into) {
|
|
18013
|
+
const obj = readJson(file);
|
|
18014
|
+
const raw = obj?.pluginSources;
|
|
18015
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
18016
|
+
return;
|
|
18017
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
18018
|
+
if (!ALIAS_RE.test(name))
|
|
18019
|
+
continue;
|
|
18020
|
+
const spec = readSpec(value);
|
|
18021
|
+
if (!spec)
|
|
18022
|
+
continue;
|
|
18023
|
+
if (into.has(name))
|
|
18024
|
+
continue;
|
|
18025
|
+
into.set(name, { name, spec, tier, file });
|
|
18026
|
+
}
|
|
18027
|
+
}
|
|
18028
|
+
function loadDeclaredSources2(workDir, opts = {}) {
|
|
18029
|
+
const out = /* @__PURE__ */ new Map();
|
|
18030
|
+
collect(managedSettingsPath(), "managed", out);
|
|
18031
|
+
collect(path6.join(os6.homedir(), ".nexrall", "settings.json"), "user", out);
|
|
18032
|
+
if (opts.includeProject) {
|
|
18033
|
+
collect(path6.join(workDir, ".nexrall", "settings.json"), "project", out);
|
|
18034
|
+
collect(path6.join(workDir, ".nexrall", "settings.local.json"), "project", out);
|
|
18035
|
+
}
|
|
18036
|
+
return [...out.values()];
|
|
18037
|
+
}
|
|
18038
|
+
function allowedSourcePatterns2() {
|
|
18039
|
+
const obj = readJson(managedSettingsPath());
|
|
18040
|
+
const v = obj?.allowedPluginSources;
|
|
18041
|
+
if (!Array.isArray(v))
|
|
18042
|
+
return null;
|
|
18043
|
+
const pats = v.filter((x2) => typeof x2 === "string" && x2.trim().length > 0);
|
|
18044
|
+
return pats.length ? pats : null;
|
|
18045
|
+
}
|
|
18046
|
+
function isSourceAllowed2(spec, patterns = allowedSourcePatterns2()) {
|
|
18047
|
+
if (!patterns)
|
|
18048
|
+
return true;
|
|
18049
|
+
const cleaned = spec.trim().replace(/^https?:\/\/(?:www\.)?github\.com\//i, "").replace(/\.git$/, "");
|
|
18050
|
+
const base = cleaned.split("#")[0].split("/").slice(0, 2).join("/").toLowerCase();
|
|
18051
|
+
return patterns.some((p) => {
|
|
18052
|
+
const pat = p.trim().toLowerCase().replace(/^https?:\/\/(?:www\.)?github\.com\//i, "");
|
|
18053
|
+
if (pat === "*")
|
|
18054
|
+
return true;
|
|
18055
|
+
if (pat.endsWith("/*"))
|
|
18056
|
+
return base.startsWith(pat.slice(0, -1));
|
|
18057
|
+
return base === pat.split("#")[0].split("/").slice(0, 2).join("/");
|
|
18058
|
+
});
|
|
18059
|
+
}
|
|
18060
|
+
function resolveSourceAlias2(alias, workDir, opts = {}) {
|
|
18061
|
+
const want = alias.trim().toLowerCase();
|
|
18062
|
+
return loadDeclaredSources2(workDir, opts).find((s2) => s2.name.toLowerCase() === want) ?? null;
|
|
18063
|
+
}
|
|
18064
|
+
function writeDeclaredSource2(file, name, spec) {
|
|
18065
|
+
if (!ALIAS_RE.test(name)) {
|
|
18066
|
+
throw new Error(`Invalid source name "${name}". Use letters, numbers, dot, dash or underscore.`);
|
|
18067
|
+
}
|
|
18068
|
+
const obj = readJson(file) ?? {};
|
|
18069
|
+
const existing = obj.pluginSources && typeof obj.pluginSources === "object" && !Array.isArray(obj.pluginSources) ? obj.pluginSources : {};
|
|
18070
|
+
obj.pluginSources = { ...existing, [name]: spec };
|
|
18071
|
+
fs9.mkdirSync(path6.dirname(file), { recursive: true });
|
|
18072
|
+
fs9.writeFileSync(file, JSON.stringify(obj, null, 2) + "\n", "utf-8");
|
|
18073
|
+
}
|
|
18074
|
+
function removeDeclaredSource2(file, name) {
|
|
18075
|
+
const obj = readJson(file);
|
|
18076
|
+
if (!obj?.pluginSources || typeof obj.pluginSources !== "object")
|
|
18077
|
+
return false;
|
|
18078
|
+
const sources = obj.pluginSources;
|
|
18079
|
+
if (!(name in sources))
|
|
18080
|
+
return false;
|
|
18081
|
+
delete sources[name];
|
|
18082
|
+
fs9.writeFileSync(file, JSON.stringify(obj, null, 2) + "\n", "utf-8");
|
|
18083
|
+
return true;
|
|
18084
|
+
}
|
|
18085
|
+
function sourcesFileFor2(scope, workDir) {
|
|
18086
|
+
return scope === "user" ? path6.join(os6.homedir(), ".nexrall", "settings.json") : path6.join(workDir, ".nexrall", "settings.json");
|
|
18087
|
+
}
|
|
18088
|
+
}
|
|
18089
|
+
});
|
|
18090
|
+
|
|
17376
18091
|
// ../core/dist/plugins/installer.js
|
|
17377
18092
|
var require_installer = __commonJS({
|
|
17378
18093
|
"../core/dist/plugins/installer.js"(exports) {
|
|
@@ -17425,8 +18140,8 @@ var require_installer = __commonJS({
|
|
|
17425
18140
|
exports.searchRegistry = searchRegistry2;
|
|
17426
18141
|
exports.getRegistryPlugin = getRegistryPlugin2;
|
|
17427
18142
|
exports.reportInstall = reportInstall2;
|
|
17428
|
-
exports.parsePluginSource =
|
|
17429
|
-
exports.inspectPluginDir =
|
|
18143
|
+
exports.parsePluginSource = parsePluginSource2;
|
|
18144
|
+
exports.inspectPluginDir = inspectPluginDir2;
|
|
17430
18145
|
exports.installPlugin = installPlugin2;
|
|
17431
18146
|
exports.readReceipt = readReceipt2;
|
|
17432
18147
|
exports.removePlugin = removePlugin2;
|
|
@@ -17436,6 +18151,8 @@ var require_installer = __commonJS({
|
|
|
17436
18151
|
var os6 = __importStar(__require("os"));
|
|
17437
18152
|
var child_process_1 = __require("child_process");
|
|
17438
18153
|
var client_1 = require_client();
|
|
18154
|
+
var index_1 = require_plugins();
|
|
18155
|
+
var sources_1 = require_sources();
|
|
17439
18156
|
var RECEIPT_FILE = "_install.json";
|
|
17440
18157
|
async function searchRegistry2(query, limit = 50) {
|
|
17441
18158
|
const u = new URL(`${client_1.API_BASE}/api/code/plugins`);
|
|
@@ -17465,7 +18182,7 @@ var require_installer = __commonJS({
|
|
|
17465
18182
|
fetch(`${client_1.API_BASE}/api/code/plugins/${encodeURIComponent(name)}/installed`, { method: "POST" }).catch(() => {
|
|
17466
18183
|
});
|
|
17467
18184
|
}
|
|
17468
|
-
function
|
|
18185
|
+
function parsePluginSource2(spec) {
|
|
17469
18186
|
const raw = spec.trim();
|
|
17470
18187
|
if (!raw)
|
|
17471
18188
|
throw new Error("Empty plugin source.");
|
|
@@ -17512,29 +18229,45 @@ var require_installer = __commonJS({
|
|
|
17512
18229
|
return [];
|
|
17513
18230
|
}
|
|
17514
18231
|
}
|
|
17515
|
-
function
|
|
18232
|
+
function inspectPluginDir2(dir) {
|
|
17516
18233
|
let meta = {};
|
|
17517
|
-
|
|
17518
|
-
|
|
17519
|
-
|
|
17520
|
-
|
|
17521
|
-
|
|
17522
|
-
|
|
17523
|
-
|
|
17524
|
-
|
|
18234
|
+
const manifest = (0, index_1.resolvePluginFile)(dir, "manifest");
|
|
18235
|
+
if (manifest) {
|
|
18236
|
+
try {
|
|
18237
|
+
const j = JSON.parse(fs9.readFileSync(manifest, "utf-8"));
|
|
18238
|
+
meta = {
|
|
18239
|
+
name: typeof j.name === "string" ? j.name : void 0,
|
|
18240
|
+
version: typeof j.version === "string" ? j.version : void 0,
|
|
18241
|
+
description: typeof j.description === "string" ? j.description : void 0
|
|
18242
|
+
};
|
|
18243
|
+
} catch {
|
|
18244
|
+
}
|
|
17525
18245
|
}
|
|
17526
18246
|
return {
|
|
17527
18247
|
commands: listMd(path6.join(dir, "commands")),
|
|
17528
18248
|
agents: listMd(path6.join(dir, "agents")),
|
|
17529
|
-
hasHooks:
|
|
17530
|
-
hasMcp:
|
|
18249
|
+
hasHooks: (0, index_1.resolvePluginFile)(dir, "hooks") !== null,
|
|
18250
|
+
hasMcp: (0, index_1.resolvePluginFile)(dir, "mcp") !== null,
|
|
17531
18251
|
meta
|
|
17532
18252
|
};
|
|
17533
18253
|
}
|
|
17534
18254
|
function looksLikePlugin(dir) {
|
|
17535
|
-
const i2 =
|
|
18255
|
+
const i2 = inspectPluginDir2(dir);
|
|
17536
18256
|
return i2.commands.length > 0 || i2.agents.length > 0 || i2.hasHooks || i2.hasMcp || i2.meta.name !== void 0;
|
|
17537
18257
|
}
|
|
18258
|
+
async function resolveCommitSha(repo, ref) {
|
|
18259
|
+
try {
|
|
18260
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/commits/${encodeURIComponent(ref || "HEAD")}`, {
|
|
18261
|
+
headers: { Accept: "application/vnd.github.sha", "User-Agent": "nexrall-code" }
|
|
18262
|
+
});
|
|
18263
|
+
if (!res.ok)
|
|
18264
|
+
return null;
|
|
18265
|
+
const sha = (await res.text()).trim();
|
|
18266
|
+
return /^[0-9a-f]{40}$/i.test(sha) ? sha : null;
|
|
18267
|
+
} catch {
|
|
18268
|
+
return null;
|
|
18269
|
+
}
|
|
18270
|
+
}
|
|
17538
18271
|
async function downloadTarball(repo, ref, destFile) {
|
|
17539
18272
|
const r2 = ref || "HEAD";
|
|
17540
18273
|
const url = `https://codeload.github.com/${repo}/tar.gz/${encodeURIComponent(r2)}`;
|
|
@@ -17585,28 +18318,33 @@ var require_installer = __commonJS({
|
|
|
17585
18318
|
return scope === "project" ? path6.join(workDir || process.cwd(), ".nexrall", "plugins") : path6.join(os6.homedir(), ".nexrall", "plugins");
|
|
17586
18319
|
}
|
|
17587
18320
|
async function installPlugin2(spec, opts = {}) {
|
|
17588
|
-
const
|
|
18321
|
+
const source2 = parsePluginSource2(spec);
|
|
17589
18322
|
const scope = opts.scope ?? "global";
|
|
18323
|
+
const patterns = (0, sources_1.allowedSourcePatterns)();
|
|
18324
|
+
if (patterns && source2.kind === "github" && !(0, sources_1.isSourceAllowed)(source2.raw, patterns)) {
|
|
18325
|
+
throw new Error(`Blocked by your organisation's policy: plugins may only be installed from ${patterns.join(", ")}.`);
|
|
18326
|
+
}
|
|
17590
18327
|
const tmpBase = fs9.mkdtempSync(path6.join(os6.tmpdir(), "nexrall-plugin-"));
|
|
17591
18328
|
try {
|
|
17592
18329
|
let repoRoot;
|
|
17593
|
-
if (
|
|
17594
|
-
if (!fs9.existsSync(
|
|
17595
|
-
throw new Error(`Local plugin path not found: ${
|
|
18330
|
+
if (source2.kind === "local") {
|
|
18331
|
+
if (!fs9.existsSync(source2.repo) || !fs9.statSync(source2.repo).isDirectory()) {
|
|
18332
|
+
throw new Error(`Local plugin path not found: ${source2.repo}`);
|
|
17596
18333
|
}
|
|
17597
|
-
repoRoot =
|
|
18334
|
+
repoRoot = source2.repo;
|
|
17598
18335
|
} else {
|
|
17599
18336
|
const tarFile = path6.join(tmpBase, "plugin.tar.gz");
|
|
17600
|
-
await downloadTarball(
|
|
18337
|
+
await downloadTarball(source2.repo, source2.ref, tarFile);
|
|
17601
18338
|
repoRoot = extractTarball(tarFile, path6.join(tmpBase, "x"));
|
|
17602
18339
|
}
|
|
17603
|
-
|
|
18340
|
+
const sha = source2.kind === "github" ? await resolveCommitSha(source2.repo, source2.ref) : null;
|
|
18341
|
+
let pluginRoot = source2.subdir ? path6.join(repoRoot, source2.subdir) : repoRoot;
|
|
17604
18342
|
pluginRoot = path6.resolve(pluginRoot);
|
|
17605
18343
|
if (!path6.resolve(pluginRoot).startsWith(path6.resolve(repoRoot))) {
|
|
17606
18344
|
throw new Error("Plugin subdirectory escapes the repository \u2014 refusing.");
|
|
17607
18345
|
}
|
|
17608
18346
|
if (!fs9.existsSync(pluginRoot) || !fs9.statSync(pluginRoot).isDirectory()) {
|
|
17609
|
-
throw new Error(`Subdirectory not found in repo: ${
|
|
18347
|
+
throw new Error(`Subdirectory not found in repo: ${source2.subdir}`);
|
|
17610
18348
|
}
|
|
17611
18349
|
if (!looksLikePlugin(pluginRoot)) {
|
|
17612
18350
|
const candidates = ["plugin", ".nexrall-plugin"].map((c) => path6.join(pluginRoot, c));
|
|
@@ -17617,12 +18355,16 @@ var require_installer = __commonJS({
|
|
|
17617
18355
|
throw new Error("No plugin found: expected plugin.json, commands/, agents/, hooks.json or mcp.json at the source root.");
|
|
17618
18356
|
}
|
|
17619
18357
|
}
|
|
17620
|
-
const inspection =
|
|
17621
|
-
const name = opts.name || inspection.meta.name || (
|
|
18358
|
+
const inspection = inspectPluginDir2(pluginRoot);
|
|
18359
|
+
const name = opts.name || inspection.meta.name || (source2.subdir ? path6.basename(source2.subdir) : void 0) || (source2.kind === "github" ? source2.repo.split("/")[1] : path6.basename(source2.repo));
|
|
17622
18360
|
if (!/^[\w.-]+$/.test(name))
|
|
17623
18361
|
throw new Error(`Invalid plugin name "${name}".`);
|
|
17624
18362
|
if (opts.confirm) {
|
|
17625
|
-
const ok = await opts.confirm(inspection, name
|
|
18363
|
+
const ok = await opts.confirm(inspection, name, {
|
|
18364
|
+
sha: sha ?? void 0,
|
|
18365
|
+
previousSha: opts._previousSha,
|
|
18366
|
+
isUpdate: Boolean(opts._previousSha) || Boolean(opts.force)
|
|
18367
|
+
});
|
|
17626
18368
|
if (!ok)
|
|
17627
18369
|
throw new Error("Installation cancelled.");
|
|
17628
18370
|
}
|
|
@@ -17635,14 +18377,15 @@ var require_installer = __commonJS({
|
|
|
17635
18377
|
}
|
|
17636
18378
|
copyPluginDir(pluginRoot, dest);
|
|
17637
18379
|
const receipt = {
|
|
17638
|
-
source:
|
|
17639
|
-
kind:
|
|
17640
|
-
ref:
|
|
17641
|
-
subdir:
|
|
17642
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18380
|
+
source: source2.raw,
|
|
18381
|
+
kind: source2.kind,
|
|
18382
|
+
ref: source2.ref,
|
|
18383
|
+
subdir: source2.subdir,
|
|
18384
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18385
|
+
...sha ? { sha } : {}
|
|
17643
18386
|
};
|
|
17644
18387
|
fs9.writeFileSync(path6.join(dest, RECEIPT_FILE), JSON.stringify(receipt, null, 2));
|
|
17645
|
-
return { name, dir: dest, scope, inspection };
|
|
18388
|
+
return { name, dir: dest, scope, inspection, sha: sha ?? void 0, previousSha: opts._previousSha };
|
|
17646
18389
|
} finally {
|
|
17647
18390
|
fs9.rmSync(tmpBase, { recursive: true, force: true });
|
|
17648
18391
|
}
|
|
@@ -17681,7 +18424,14 @@ var require_installer = __commonJS({
|
|
|
17681
18424
|
if (receipt.kind === "local") {
|
|
17682
18425
|
throw new Error(`Plugin "${name}" was installed from a local path \u2014 re-run 'nex plugin install ${receipt.source} --force'.`);
|
|
17683
18426
|
}
|
|
17684
|
-
return installPlugin2(receipt.source, {
|
|
18427
|
+
return installPlugin2(receipt.source, {
|
|
18428
|
+
scope: s2,
|
|
18429
|
+
workDir: opts.workDir,
|
|
18430
|
+
name,
|
|
18431
|
+
force: true,
|
|
18432
|
+
confirm: opts.confirm,
|
|
18433
|
+
_previousSha: receipt.sha
|
|
18434
|
+
});
|
|
17685
18435
|
}
|
|
17686
18436
|
throw new Error(`Plugin "${name}" is not installed.`);
|
|
17687
18437
|
}
|
|
@@ -17720,6 +18470,7 @@ var require_dist2 = __commonJS({
|
|
|
17720
18470
|
__exportStar(require_loop(), exports);
|
|
17721
18471
|
__exportStar(require_testIntegrity(), exports);
|
|
17722
18472
|
__exportStar(require_editCompleteness(), exports);
|
|
18473
|
+
__exportStar(require_securityLint(), exports);
|
|
17723
18474
|
__exportStar(require_crossFile(), exports);
|
|
17724
18475
|
__exportStar(require_flaky(), exports);
|
|
17725
18476
|
__exportStar(require_claimEvidence(), exports);
|
|
@@ -17735,6 +18486,7 @@ var require_dist2 = __commonJS({
|
|
|
17735
18486
|
__exportStar(require_destructive(), exports);
|
|
17736
18487
|
__exportStar(require_plugins(), exports);
|
|
17737
18488
|
__exportStar(require_installer(), exports);
|
|
18489
|
+
__exportStar(require_sources(), exports);
|
|
17738
18490
|
}
|
|
17739
18491
|
});
|
|
17740
18492
|
|
|
@@ -20550,23 +21302,23 @@ var require_react_reconciler_production = __commonJS({
|
|
|
20550
21302
|
return "\nError generating stack: " + x2.message + "\n" + x2.stack;
|
|
20551
21303
|
}
|
|
20552
21304
|
}
|
|
20553
|
-
function createCapturedValueAtFiber(value,
|
|
21305
|
+
function createCapturedValueAtFiber(value, source2) {
|
|
20554
21306
|
if ("object" === typeof value && null !== value) {
|
|
20555
21307
|
var existing = CapturedStacks.get(value);
|
|
20556
21308
|
if (void 0 !== existing)
|
|
20557
21309
|
return existing;
|
|
20558
|
-
|
|
21310
|
+
source2 = {
|
|
20559
21311
|
value,
|
|
20560
|
-
source,
|
|
20561
|
-
stack: getStackByFiberInDevAndProd(
|
|
21312
|
+
source: source2,
|
|
21313
|
+
stack: getStackByFiberInDevAndProd(source2)
|
|
20562
21314
|
};
|
|
20563
|
-
CapturedStacks.set(value,
|
|
20564
|
-
return
|
|
21315
|
+
CapturedStacks.set(value, source2);
|
|
21316
|
+
return source2;
|
|
20565
21317
|
}
|
|
20566
21318
|
return {
|
|
20567
21319
|
value,
|
|
20568
|
-
source,
|
|
20569
|
-
stack: getStackByFiberInDevAndProd(
|
|
21320
|
+
source: source2,
|
|
21321
|
+
stack: getStackByFiberInDevAndProd(source2)
|
|
20570
21322
|
};
|
|
20571
21323
|
}
|
|
20572
21324
|
function pushTreeFork(workInProgress2, totalChildren) {
|
|
@@ -29738,23 +30490,23 @@ var require_react_reconciler_development = __commonJS({
|
|
|
29738
30490
|
function describeFunctionComponentFrameWithoutLineNumber(fn) {
|
|
29739
30491
|
return (fn = fn ? fn.displayName || fn.name : "") ? describeBuiltInComponentFrame(fn) : "";
|
|
29740
30492
|
}
|
|
29741
|
-
function createCapturedValueAtFiber(value,
|
|
30493
|
+
function createCapturedValueAtFiber(value, source2) {
|
|
29742
30494
|
if ("object" === typeof value && null !== value) {
|
|
29743
30495
|
var existing = CapturedStacks.get(value);
|
|
29744
30496
|
if (void 0 !== existing)
|
|
29745
30497
|
return existing;
|
|
29746
|
-
|
|
30498
|
+
source2 = {
|
|
29747
30499
|
value,
|
|
29748
|
-
source,
|
|
29749
|
-
stack: getStackByFiberInDevAndProd(
|
|
30500
|
+
source: source2,
|
|
30501
|
+
stack: getStackByFiberInDevAndProd(source2)
|
|
29750
30502
|
};
|
|
29751
|
-
CapturedStacks.set(value,
|
|
29752
|
-
return
|
|
30503
|
+
CapturedStacks.set(value, source2);
|
|
30504
|
+
return source2;
|
|
29753
30505
|
}
|
|
29754
30506
|
return {
|
|
29755
30507
|
value,
|
|
29756
|
-
source,
|
|
29757
|
-
stack: getStackByFiberInDevAndProd(
|
|
30508
|
+
source: source2,
|
|
30509
|
+
stack: getStackByFiberInDevAndProd(source2)
|
|
29758
30510
|
};
|
|
29759
30511
|
}
|
|
29760
30512
|
function pushTreeFork(workInProgress2, totalChildren) {
|
|
@@ -42312,9 +43064,9 @@ var require_buffer_util = __commonJS({
|
|
|
42312
43064
|
}
|
|
42313
43065
|
return target;
|
|
42314
43066
|
}
|
|
42315
|
-
function _mask(
|
|
43067
|
+
function _mask(source2, mask, output, offset, length) {
|
|
42316
43068
|
for (let i2 = 0; i2 < length; i2++) {
|
|
42317
|
-
output[offset + i2] =
|
|
43069
|
+
output[offset + i2] = source2[i2] ^ mask[i2 & 3];
|
|
42318
43070
|
}
|
|
42319
43071
|
}
|
|
42320
43072
|
function _unmask(buffer, mask) {
|
|
@@ -42353,11 +43105,11 @@ var require_buffer_util = __commonJS({
|
|
|
42353
43105
|
if (!process.env.WS_NO_BUFFER_UTIL) {
|
|
42354
43106
|
try {
|
|
42355
43107
|
const bufferUtil = __require("bufferutil");
|
|
42356
|
-
module.exports.mask = function(
|
|
43108
|
+
module.exports.mask = function(source2, mask, output, offset, length) {
|
|
42357
43109
|
if (length < 48)
|
|
42358
|
-
_mask(
|
|
43110
|
+
_mask(source2, mask, output, offset, length);
|
|
42359
43111
|
else
|
|
42360
|
-
bufferUtil.mask(
|
|
43112
|
+
bufferUtil.mask(source2, mask, output, offset, length);
|
|
42361
43113
|
};
|
|
42362
43114
|
module.exports.unmask = function(buffer, mask) {
|
|
42363
43115
|
if (buffer.length < 32)
|
|
@@ -49074,16 +49826,16 @@ var require_dist3 = __commonJS({
|
|
|
49074
49826
|
}
|
|
49075
49827
|
function _objectSpread(target) {
|
|
49076
49828
|
for (var i2 = 1; i2 < arguments.length; i2++) {
|
|
49077
|
-
var
|
|
49829
|
+
var source2 = arguments[i2] != null ? arguments[i2] : {};
|
|
49078
49830
|
if (i2 % 2) {
|
|
49079
|
-
ownKeys(Object(
|
|
49080
|
-
_defineProperty(target, key,
|
|
49831
|
+
ownKeys(Object(source2), true).forEach(function(key) {
|
|
49832
|
+
_defineProperty(target, key, source2[key]);
|
|
49081
49833
|
});
|
|
49082
49834
|
} else if (Object.getOwnPropertyDescriptors) {
|
|
49083
|
-
Object.defineProperties(target, Object.getOwnPropertyDescriptors(
|
|
49835
|
+
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source2));
|
|
49084
49836
|
} else {
|
|
49085
|
-
ownKeys(Object(
|
|
49086
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(
|
|
49837
|
+
ownKeys(Object(source2)).forEach(function(key) {
|
|
49838
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source2, key));
|
|
49087
49839
|
});
|
|
49088
49840
|
}
|
|
49089
49841
|
}
|
|
@@ -53803,6 +54555,16 @@ function trustGrantedByEnv() {
|
|
|
53803
54555
|
const v = process.env[TRUST_ENV_VAR];
|
|
53804
54556
|
return v === "1" || v === "true";
|
|
53805
54557
|
}
|
|
54558
|
+
function declaresPluginSources(dir) {
|
|
54559
|
+
try {
|
|
54560
|
+
const raw = fs5.readFileSync(path3.join(dir, ".nexrall", "settings.json"), "utf-8");
|
|
54561
|
+
const obj = JSON.parse(raw);
|
|
54562
|
+
const s2 = obj.pluginSources;
|
|
54563
|
+
return Boolean(s2 && typeof s2 === "object" && !Array.isArray(s2) && Object.keys(s2).length > 0);
|
|
54564
|
+
} catch {
|
|
54565
|
+
return false;
|
|
54566
|
+
}
|
|
54567
|
+
}
|
|
53806
54568
|
function detectTrustSignals(dir) {
|
|
53807
54569
|
const signals = [];
|
|
53808
54570
|
const exists = (...p) => {
|
|
@@ -53822,7 +54584,9 @@ function detectTrustSignals(dir) {
|
|
|
53822
54584
|
signals.push(".nexrall/permissions.json \u2014 pre-approved tool permissions");
|
|
53823
54585
|
}
|
|
53824
54586
|
if (exists(".nexrall", "settings.json")) {
|
|
53825
|
-
signals.push(
|
|
54587
|
+
signals.push(
|
|
54588
|
+
declaresPluginSources(dir) ? ".nexrall/settings.json \u2014 project settings, and declares plugin sources (aliases for remote plugin repos)" : ".nexrall/settings.json \u2014 project settings and permission rules"
|
|
54589
|
+
);
|
|
53826
54590
|
}
|
|
53827
54591
|
if (exists("nexrall.md")) {
|
|
53828
54592
|
signals.push("nexrall.md \u2014 project instructions added to the agent\u2019s prompt");
|
|
@@ -60331,15 +61095,15 @@ var generateLineNumbers = (line, around) => {
|
|
|
60331
61095
|
}
|
|
60332
61096
|
return lineNumbers;
|
|
60333
61097
|
};
|
|
60334
|
-
var codeExcerpt = (
|
|
61098
|
+
var codeExcerpt = (source2, line, options = {}) => {
|
|
60335
61099
|
var _a;
|
|
60336
|
-
if (typeof
|
|
61100
|
+
if (typeof source2 !== "string") {
|
|
60337
61101
|
throw new TypeError("Source code is missing.");
|
|
60338
61102
|
}
|
|
60339
61103
|
if (!line || line < 1) {
|
|
60340
61104
|
throw new TypeError("Line number must start from `1`.");
|
|
60341
61105
|
}
|
|
60342
|
-
const lines = dist_default3(
|
|
61106
|
+
const lines = dist_default3(source2).split(/\r?\n/);
|
|
60343
61107
|
if (line > lines.length) {
|
|
60344
61108
|
return;
|
|
60345
61109
|
}
|
|
@@ -62794,7 +63558,7 @@ var InkReadlineAdapter = class extends EventEmitter3 {
|
|
|
62794
63558
|
};
|
|
62795
63559
|
|
|
62796
63560
|
// src/commands/chat.ts
|
|
62797
|
-
var CLI_VERSION = "0.5.
|
|
63561
|
+
var CLI_VERSION = "0.5.50";
|
|
62798
63562
|
var MODEL_LABELS = {
|
|
62799
63563
|
turbo: "Nexrall Turbo",
|
|
62800
63564
|
pro: "Nexrall Pro",
|
|
@@ -63012,15 +63776,23 @@ function formatContextUsage(messages, modelAlias) {
|
|
|
63012
63776
|
return lines.join("\n");
|
|
63013
63777
|
}
|
|
63014
63778
|
function formatAgentsList(workDir) {
|
|
63015
|
-
const types3 = (0, import_code_core3.
|
|
63779
|
+
const { types: types3, warnings } = (0, import_code_core3.loadAgentTypesWithWarnings)(workDir);
|
|
63016
63780
|
if (!types3.length)
|
|
63017
63781
|
return source_default.dim(" No agent types found (this should not happen \u2014 builtins always register).");
|
|
63018
63782
|
const lines = types3.map((t2) => {
|
|
63019
|
-
const tools = t2.tools ? source_default.dim(` \u2014 tools: ${t2.tools.join(", ")}`) : source_default.dim(" \u2014
|
|
63783
|
+
const tools = t2.tools ? source_default.dim(` \u2014 tools: ${t2.tools.join(", ")}`) : source_default.dim(" \u2014 ALL tools (no allowlist)");
|
|
63020
63784
|
const model = t2.model ? source_default.dim(` \u2014 model: ${t2.model}`) : "";
|
|
63021
63785
|
const scope = source_default.dim(` (${t2.source})`);
|
|
63022
63786
|
return " " + source_default.cyan(t2.name.padEnd(16)) + source_default.white(t2.description) + scope + tools + model;
|
|
63023
63787
|
});
|
|
63788
|
+
if (warnings.length) {
|
|
63789
|
+
lines.push("");
|
|
63790
|
+
lines.push(" " + source_default.yellow.bold(`\u26A0 ${warnings.length} problem${warnings.length > 1 ? "s" : ""} in your agent definitions:`));
|
|
63791
|
+
for (const w of warnings) {
|
|
63792
|
+
lines.push(" " + source_default.yellow(`\u2022 ${w.agent}`) + source_default.dim(` \u2014 ${w.message}`));
|
|
63793
|
+
lines.push(" " + source_default.dim(w.file));
|
|
63794
|
+
}
|
|
63795
|
+
}
|
|
63024
63796
|
return lines.join("\n");
|
|
63025
63797
|
}
|
|
63026
63798
|
async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, mode, effort, checkpointManager, onProgress) {
|
|
@@ -64137,11 +64909,23 @@ ${caption}` : `[Attached: ${rel}]`;
|
|
|
64137
64909
|
// src/commands/plugin.ts
|
|
64138
64910
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
64139
64911
|
var import_code_core4 = __toESM(require_dist2(), 1);
|
|
64140
|
-
function describeInspection(i2, name) {
|
|
64912
|
+
function describeInspection(i2, name, ctx) {
|
|
64141
64913
|
console.log();
|
|
64142
64914
|
console.log(source_default.bold(` Plugin: ${name}`) + (i2.meta.version ? source_default.dim(` v${i2.meta.version}`) : ""));
|
|
64143
64915
|
if (i2.meta.description)
|
|
64144
64916
|
console.log(source_default.dim(` ${i2.meta.description}`));
|
|
64917
|
+
if (ctx?.sha) {
|
|
64918
|
+
const short = (s2) => s2.slice(0, 7);
|
|
64919
|
+
if (ctx.previousSha && ctx.previousSha !== ctx.sha) {
|
|
64920
|
+
console.log(source_default.dim(" commit: ") + source_default.yellow(`${short(ctx.previousSha)} \u2192 ${short(ctx.sha)}`));
|
|
64921
|
+
} else if (ctx.previousSha === ctx.sha) {
|
|
64922
|
+
console.log(source_default.dim(` commit: ${short(ctx.sha)} (already up to date)`));
|
|
64923
|
+
} else {
|
|
64924
|
+
console.log(source_default.dim(` commit: ${short(ctx.sha)}`));
|
|
64925
|
+
}
|
|
64926
|
+
} else if (ctx?.isUpdate) {
|
|
64927
|
+
console.log(source_default.dim(" commit: unknown (could not reach the GitHub API)"));
|
|
64928
|
+
}
|
|
64145
64929
|
if (i2.commands.length)
|
|
64146
64930
|
console.log(` ${source_default.cyan("commands:")} ${i2.commands.map((c) => "/" + c).join(", ")}`);
|
|
64147
64931
|
if (i2.agents.length)
|
|
@@ -64151,10 +64935,16 @@ function describeInspection(i2, name) {
|
|
|
64151
64935
|
if (i2.hasMcp)
|
|
64152
64936
|
console.log(` ${source_default.yellow("mcp:")} yes`);
|
|
64153
64937
|
}
|
|
64154
|
-
async function confirmInstall(i2, name, autoYes) {
|
|
64155
|
-
describeInspection(i2, name);
|
|
64938
|
+
async function confirmInstall(i2, name, autoYes, ctx) {
|
|
64939
|
+
describeInspection(i2, name, ctx);
|
|
64156
64940
|
const dangerous = i2.hasHooks || i2.hasMcp;
|
|
64157
64941
|
if (dangerous) {
|
|
64942
|
+
if (ctx?.isUpdate) {
|
|
64943
|
+
console.log();
|
|
64944
|
+
console.log(source_default.red.bold(" \u26A0 THIS UPDATE SHIPS CODE EXECUTION"));
|
|
64945
|
+
console.log(source_default.red(" A version of this plugin you already trust is being replaced by one"));
|
|
64946
|
+
console.log(source_default.red(" that runs commands. Review the diff before continuing."));
|
|
64947
|
+
}
|
|
64158
64948
|
console.log();
|
|
64159
64949
|
console.log(source_default.red.bold(" \u26A0 SECURITY WARNING"));
|
|
64160
64950
|
if (i2.hasHooks) {
|
|
@@ -64172,6 +64962,12 @@ async function confirmInstall(i2, name, autoYes) {
|
|
|
64172
64962
|
if (autoYes && dangerous) {
|
|
64173
64963
|
console.log(source_default.yellow("\n --yes does not apply to plugins with hooks/MCP; confirmation required."));
|
|
64174
64964
|
}
|
|
64965
|
+
if (!process.stdin.isTTY) {
|
|
64966
|
+
console.log();
|
|
64967
|
+
console.log(source_default.red(" \u2717 Cannot ask for confirmation: stdin is not a terminal."));
|
|
64968
|
+
console.log(source_default.dim(dangerous ? " This plugin ships hooks/MCP, which always require an interactive confirmation." : " Re-run with --yes to install non-interactively."));
|
|
64969
|
+
return false;
|
|
64970
|
+
}
|
|
64175
64971
|
console.log();
|
|
64176
64972
|
const { ok } = await (0, import_prompts.default)({
|
|
64177
64973
|
type: "confirm",
|
|
@@ -64184,35 +64980,43 @@ async function confirmInstall(i2, name, autoYes) {
|
|
|
64184
64980
|
async function pluginInstallCommand(spec, opts) {
|
|
64185
64981
|
const scope = opts.project ? "project" : "global";
|
|
64186
64982
|
try {
|
|
64187
|
-
let
|
|
64983
|
+
let source2 = spec;
|
|
64188
64984
|
let registryName;
|
|
64189
64985
|
const isShortName = /^[\w.-]+$/.test(spec.trim()) && !spec.includes("/");
|
|
64190
64986
|
if (isShortName) {
|
|
64987
|
+
const alias = (0, import_code_core4.resolveSourceAlias)(spec.trim(), process.cwd(), { includeProject: true });
|
|
64988
|
+
if (alias) {
|
|
64989
|
+
source2 = alias.spec;
|
|
64990
|
+
console.log(source_default.dim(` Source: ${alias.name} (${alias.tier}) \u2190 ${alias.spec}`));
|
|
64991
|
+
}
|
|
64992
|
+
}
|
|
64993
|
+
if (isShortName && source2 === spec) {
|
|
64191
64994
|
const entry = await (0, import_code_core4.getRegistryPlugin)(spec.trim()).catch(() => null);
|
|
64192
64995
|
if (entry) {
|
|
64193
|
-
|
|
64996
|
+
source2 = entry.source;
|
|
64194
64997
|
registryName = entry.name;
|
|
64195
64998
|
const badge = entry.official ? source_default.magenta(" [official]") : entry.verified ? source_default.green(" [verified]") : "";
|
|
64196
64999
|
console.log(source_default.dim(` Registry: ${entry.display_name}${badge} \u2190 ${entry.source}`));
|
|
64197
65000
|
} else {
|
|
64198
|
-
console.error(source_default.red(` \u2717 "${spec}" not
|
|
64199
|
-
console.error(source_default.dim(" Search:
|
|
64200
|
-
console.error(source_default.dim("
|
|
65001
|
+
console.error(source_default.red(` \u2717 "${spec}" is not a declared source or a registry plugin.`));
|
|
65002
|
+
console.error(source_default.dim(" Search: nex plugin search <keyword>"));
|
|
65003
|
+
console.error(source_default.dim(" From GitHub: nex plugin install owner/repo"));
|
|
65004
|
+
console.error(source_default.dim(" Declare it: nex plugin source add " + spec.trim() + " owner/repo"));
|
|
64201
65005
|
process.exitCode = 1;
|
|
64202
65006
|
return;
|
|
64203
65007
|
}
|
|
64204
65008
|
}
|
|
64205
|
-
const res = await (0, import_code_core4.installPlugin)(
|
|
65009
|
+
const res = await (0, import_code_core4.installPlugin)(source2, {
|
|
64206
65010
|
scope,
|
|
64207
65011
|
workDir: process.cwd(),
|
|
64208
65012
|
name: opts.name ?? registryName,
|
|
64209
65013
|
force: opts.force,
|
|
64210
|
-
confirm: (i2, name) => confirmInstall(i2, name, opts.yes === true)
|
|
65014
|
+
confirm: (i2, name, ctx) => confirmInstall(i2, name, opts.yes === true, ctx)
|
|
64211
65015
|
});
|
|
64212
65016
|
if (registryName)
|
|
64213
65017
|
(0, import_code_core4.reportInstall)(registryName);
|
|
64214
65018
|
console.log();
|
|
64215
|
-
console.log(source_default.green(` \u2713 Installed "${res.name}" (${res.scope}) \u2192 ${res.dir}`));
|
|
65019
|
+
console.log(source_default.green(` \u2713 Installed "${res.name}" (${res.scope}) \u2192 ${res.dir}`) + (res.sha ? source_default.dim(` @ ${res.sha.slice(0, 7)}`) : ""));
|
|
64216
65020
|
if (res.inspection.commands.length) {
|
|
64217
65021
|
console.log(source_default.dim(` Try: ${res.inspection.commands.map((c) => "/" + c).join(", ")}`));
|
|
64218
65022
|
}
|
|
@@ -64246,9 +65050,10 @@ async function pluginUpdateCommand(name, opts) {
|
|
|
64246
65050
|
try {
|
|
64247
65051
|
const res = await (0, import_code_core4.updatePlugin)(t2, {
|
|
64248
65052
|
workDir,
|
|
64249
|
-
confirm: (i2, n) => confirmInstall(i2, n, opts.yes === true)
|
|
65053
|
+
confirm: (i2, n, ctx) => confirmInstall(i2, n, opts.yes === true, ctx)
|
|
64250
65054
|
});
|
|
64251
|
-
|
|
65055
|
+
const moved = res.sha && res.previousSha && res.sha !== res.previousSha ? source_default.dim(` ${res.previousSha.slice(0, 7)} \u2192 ${res.sha.slice(0, 7)}`) : res.sha && res.previousSha === res.sha ? source_default.dim(" (unchanged)") : "";
|
|
65056
|
+
console.log(source_default.green(` \u2713 Updated "${res.name}"`) + moved);
|
|
64252
65057
|
} catch (err) {
|
|
64253
65058
|
console.error(source_default.red(` \u2717 ${t2}: ${err.message}`));
|
|
64254
65059
|
process.exitCode = 1;
|
|
@@ -64330,24 +65135,98 @@ function pluginListCommand() {
|
|
|
64330
65135
|
for (const p of plugins) {
|
|
64331
65136
|
const receipt = (0, import_code_core4.readReceipt)(p.dir);
|
|
64332
65137
|
const src = receipt ? source_default.dim(` \u2190 ${receipt.source}`) : source_default.dim(" (manual)");
|
|
65138
|
+
const at = receipt?.sha ? source_default.dim(` @ ${receipt.sha.slice(0, 7)}`) : "";
|
|
65139
|
+
const insp = (0, import_code_core4.inspectPluginDir)(p.dir);
|
|
65140
|
+
const runs = insp.hasHooks || insp.hasMcp ? source_default.yellow(` \u26A0 ${[insp.hasHooks && "hooks", insp.hasMcp && "mcp"].filter(Boolean).join("+")}`) : "";
|
|
64333
65141
|
console.log(
|
|
64334
|
-
" " + source_default.cyan(p.name.padEnd(24)) + source_default.dim(`${(p.version ?? "").padEnd(10)}${p.scope.padEnd(9)}`) + src
|
|
65142
|
+
" " + source_default.cyan(p.name.padEnd(24)) + source_default.dim(`${(p.version ?? "").padEnd(10)}${p.scope.padEnd(9)}`) + src + at + runs
|
|
64335
65143
|
);
|
|
64336
65144
|
}
|
|
64337
65145
|
console.log();
|
|
64338
65146
|
}
|
|
65147
|
+
function pluginSourceListCommand() {
|
|
65148
|
+
const workDir = process.cwd();
|
|
65149
|
+
const sources = (0, import_code_core4.loadDeclaredSources)(workDir, { includeProject: true });
|
|
65150
|
+
const patterns = (0, import_code_core4.allowedSourcePatterns)();
|
|
65151
|
+
if (patterns) {
|
|
65152
|
+
console.log();
|
|
65153
|
+
console.log(source_default.bold(" Organisation policy: ") + source_default.dim(`installs restricted to ${patterns.join(", ")}`));
|
|
65154
|
+
}
|
|
65155
|
+
if (!sources.length) {
|
|
65156
|
+
console.log(source_default.dim("\n No plugin sources declared."));
|
|
65157
|
+
console.log(source_default.dim(" Add one: nex plugin source add <alias> owner/repo"));
|
|
65158
|
+
console.log();
|
|
65159
|
+
return;
|
|
65160
|
+
}
|
|
65161
|
+
console.log();
|
|
65162
|
+
console.log(source_default.bold(" Declared plugin sources:"));
|
|
65163
|
+
for (const s2 of sources) {
|
|
65164
|
+
const tier = s2.tier === "project" ? source_default.yellow(s2.tier.padEnd(9)) : source_default.dim(s2.tier.padEnd(9));
|
|
65165
|
+
const blocked = !(0, import_code_core4.isSourceAllowed)(s2.spec, patterns) ? source_default.red(" \u2717 blocked by policy") : "";
|
|
65166
|
+
console.log(" " + source_default.cyan(s2.name.padEnd(20)) + tier + source_default.white(s2.spec) + blocked);
|
|
65167
|
+
}
|
|
65168
|
+
console.log();
|
|
65169
|
+
console.log(source_default.dim(" Install from one: nex plugin install <alias>"));
|
|
65170
|
+
console.log(source_default.dim(" Declaring a source does not install anything."));
|
|
65171
|
+
console.log();
|
|
65172
|
+
}
|
|
65173
|
+
function pluginSourceAddCommand(name, spec, opts) {
|
|
65174
|
+
try {
|
|
65175
|
+
(0, import_code_core4.parsePluginSource)(spec);
|
|
65176
|
+
const patterns = (0, import_code_core4.allowedSourcePatterns)();
|
|
65177
|
+
if (patterns && !(0, import_code_core4.isSourceAllowed)(spec, patterns)) {
|
|
65178
|
+
console.error(source_default.red(` \u2717 "${spec}" is not permitted by your organisation's policy.`));
|
|
65179
|
+
console.error(source_default.dim(` Allowed: ${patterns.join(", ")}`));
|
|
65180
|
+
process.exitCode = 1;
|
|
65181
|
+
return;
|
|
65182
|
+
}
|
|
65183
|
+
const scope = opts.project ? "project" : "user";
|
|
65184
|
+
const file = (0, import_code_core4.sourcesFileFor)(scope, process.cwd());
|
|
65185
|
+
(0, import_code_core4.writeDeclaredSource)(file, name, spec);
|
|
65186
|
+
console.log();
|
|
65187
|
+
console.log(source_default.green(` \u2713 Declared "${name}" \u2192 ${spec}`) + source_default.dim(` (${scope})`));
|
|
65188
|
+
console.log(source_default.dim(` ${file}`));
|
|
65189
|
+
console.log(source_default.dim(` Nothing installed yet \u2014 run: nex plugin install ${name}`));
|
|
65190
|
+
console.log();
|
|
65191
|
+
} catch (err) {
|
|
65192
|
+
console.error(source_default.red(` \u2717 ${err.message}`));
|
|
65193
|
+
process.exitCode = 1;
|
|
65194
|
+
}
|
|
65195
|
+
}
|
|
65196
|
+
function pluginSourceRemoveCommand(name, opts) {
|
|
65197
|
+
const workDir = process.cwd();
|
|
65198
|
+
const scope = opts.project ? "project" : "user";
|
|
65199
|
+
const file = (0, import_code_core4.sourcesFileFor)(scope, workDir);
|
|
65200
|
+
if ((0, import_code_core4.removeDeclaredSource)(file, name)) {
|
|
65201
|
+
console.log(source_default.green(` \u2713 Removed source "${name}" (${scope})`));
|
|
65202
|
+
console.log(source_default.dim(" Plugins already installed from it are untouched \u2014 remove them with nex plugin remove <name>"));
|
|
65203
|
+
return;
|
|
65204
|
+
}
|
|
65205
|
+
const found = (0, import_code_core4.resolveSourceAlias)(name, workDir, { includeProject: true });
|
|
65206
|
+
if (found) {
|
|
65207
|
+
console.error(source_default.red(` \u2717 "${name}" is declared in the ${found.tier} tier, not ${scope}.`));
|
|
65208
|
+
console.error(source_default.dim(` ${found.file}`));
|
|
65209
|
+
if (found.tier === "project")
|
|
65210
|
+
console.error(source_default.dim(" Retry with --project"));
|
|
65211
|
+
if (found.tier === "managed")
|
|
65212
|
+
console.error(source_default.dim(" Managed sources are set by your organisation and cannot be removed here."));
|
|
65213
|
+
} else {
|
|
65214
|
+
console.error(source_default.red(` \u2717 No source named "${name}".`));
|
|
65215
|
+
}
|
|
65216
|
+
process.exitCode = 1;
|
|
65217
|
+
}
|
|
64339
65218
|
|
|
64340
65219
|
// src/index.ts
|
|
64341
65220
|
var program2 = new Command();
|
|
64342
|
-
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.
|
|
65221
|
+
program2.name("nex").description("Nexrall Code \u2014 AI coding assistant (powered by Nexrall)").version("0.5.50").enablePositionalOptions();
|
|
64343
65222
|
program2.command("auth").description("Login to your Nexrall account").action(authCommand);
|
|
64344
65223
|
program2.command("logout").description("Log out of your Nexrall account").action(logoutCommand);
|
|
64345
65224
|
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) => {
|
|
64346
65225
|
await updateCommand({ check: opts.check, yes: opts.yes });
|
|
64347
65226
|
});
|
|
64348
65227
|
var plugin = program2.command("plugin").description("Manage plugins (install from GitHub, list, update, remove)");
|
|
64349
|
-
plugin.command("install <source>").description("Install a plugin: registry name, owner/repo(#ref)(/subdir), GitHub URL, or local path").option("--project", "Install into ./.nexrall/plugins (default: ~/.nexrall/plugins)").option("--name <name>", "Override the installed plugin name").option("-f, --force", "Overwrite if already installed").option("-y, --yes", "Skip confirmation (still prompts for plugins with hooks/MCP)").action(async (
|
|
64350
|
-
await pluginInstallCommand(
|
|
65228
|
+
plugin.command("install <source>").description("Install a plugin: registry name, owner/repo(#ref)(/subdir), GitHub URL, or local path").option("--project", "Install into ./.nexrall/plugins (default: ~/.nexrall/plugins)").option("--name <name>", "Override the installed plugin name").option("-f, --force", "Overwrite if already installed").option("-y, --yes", "Skip confirmation (still prompts for plugins with hooks/MCP)").action(async (source2, opts) => {
|
|
65229
|
+
await pluginInstallCommand(source2, opts);
|
|
64351
65230
|
});
|
|
64352
65231
|
plugin.command("remove <name>").alias("uninstall").description("Remove an installed plugin").option("--project", "Only look in ./.nexrall/plugins").action((name, opts) => {
|
|
64353
65232
|
pluginRemoveCommand(name, opts);
|
|
@@ -64364,6 +65243,16 @@ plugin.command("search [query]").description("Search the Nexrall plugin registry
|
|
|
64364
65243
|
plugin.command("info <name>").description("Show registry details for a plugin").action(async (name) => {
|
|
64365
65244
|
await pluginInfoCommand(name);
|
|
64366
65245
|
});
|
|
65246
|
+
var source = plugin.command("source").description("Declare where plugins may be installed from (aliases for plugin repos)");
|
|
65247
|
+
source.command("list").alias("ls").description("List declared plugin sources").action(() => {
|
|
65248
|
+
pluginSourceListCommand();
|
|
65249
|
+
});
|
|
65250
|
+
source.command("add <name> <spec>").description("Declare a source: nex plugin source add acme acme/plugins").option("--project", "Write to ./.nexrall/settings.json so the team shares it (default: ~/.nexrall/settings.json)").action((name, spec, opts) => {
|
|
65251
|
+
pluginSourceAddCommand(name, spec, opts);
|
|
65252
|
+
});
|
|
65253
|
+
source.command("remove <name>").alias("rm").description("Remove a declared source (installed plugins are untouched)").option("--project", "Remove from ./.nexrall/settings.json").action((name, opts) => {
|
|
65254
|
+
pluginSourceRemoveCommand(name, opts);
|
|
65255
|
+
});
|
|
64367
65256
|
program2.command("sessions").description("List or delete saved chat sessions").option("-d, --delete <id>", "Delete a session by id").option("--clear-all", "Delete ALL saved sessions").action((opts) => {
|
|
64368
65257
|
if (opts.clearAll) {
|
|
64369
65258
|
const sessions2 = listSessions();
|