nexrall-code 0.5.94 → 0.5.96
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 +784 -646
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11654,17 +11654,697 @@ var require_frontmatter = __commonJS({
|
|
|
11654
11654
|
"use strict";
|
|
11655
11655
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11656
11656
|
exports2.parseFrontmatter = parseFrontmatter;
|
|
11657
|
+
var TOP_LEVEL_KEY = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/;
|
|
11658
|
+
var BLOCK_SCALAR = /^[|>][+-]?$/;
|
|
11659
|
+
function leadingIndent(line) {
|
|
11660
|
+
const m2 = /^[ \t]*/.exec(line);
|
|
11661
|
+
return m2 ? m2[0].length : 0;
|
|
11662
|
+
}
|
|
11663
|
+
function unquote(v) {
|
|
11664
|
+
return v.trim().replace(/^["']|["']$/g, "");
|
|
11665
|
+
}
|
|
11657
11666
|
function parseFrontmatter(raw) {
|
|
11658
11667
|
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
11659
11668
|
if (!m2)
|
|
11660
|
-
return { meta: {}, body: raw.trim() };
|
|
11669
|
+
return { meta: {}, body: raw.trim(), ok: false };
|
|
11670
|
+
const lines = m2[1].split(/\r?\n/);
|
|
11661
11671
|
const meta = {};
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11672
|
+
let nested;
|
|
11673
|
+
let i2 = 0;
|
|
11674
|
+
while (i2 < lines.length) {
|
|
11675
|
+
const line = lines[i2];
|
|
11676
|
+
if (!line.trim() || leadingIndent(line) > 0) {
|
|
11677
|
+
i2++;
|
|
11678
|
+
continue;
|
|
11679
|
+
}
|
|
11680
|
+
const kv = TOP_LEVEL_KEY.exec(line.trim());
|
|
11681
|
+
if (!kv) {
|
|
11682
|
+
i2++;
|
|
11683
|
+
continue;
|
|
11684
|
+
}
|
|
11685
|
+
const key = kv[1].toLowerCase();
|
|
11686
|
+
const value = kv[2].trim();
|
|
11687
|
+
const continuation = [];
|
|
11688
|
+
let j = i2 + 1;
|
|
11689
|
+
while (j < lines.length && (leadingIndent(lines[j]) > 0 || !lines[j].trim())) {
|
|
11690
|
+
continuation.push(lines[j]);
|
|
11691
|
+
j++;
|
|
11692
|
+
}
|
|
11693
|
+
while (continuation.length && !continuation[continuation.length - 1].trim())
|
|
11694
|
+
continuation.pop();
|
|
11695
|
+
if (BLOCK_SCALAR.test(value)) {
|
|
11696
|
+
const folded = value.startsWith(">");
|
|
11697
|
+
const base = continuation.length ? leadingIndent(continuation.find((l2) => l2.trim()) ?? continuation[0]) : 0;
|
|
11698
|
+
const dedented = continuation.map((l2) => l2.trim() ? l2.slice(Math.min(base, leadingIndent(l2))) : "");
|
|
11699
|
+
meta[key] = folded ? dedented.join(" ").replace(/\s+/g, " ").trim() : dedented.join("\n").trim();
|
|
11700
|
+
} else if (value === "") {
|
|
11701
|
+
const looksNested = continuation.length > 0 && continuation.every((l2) => !l2.trim() || TOP_LEVEL_KEY.test(l2.trim()));
|
|
11702
|
+
if (looksNested && continuation.some((l2) => l2.trim())) {
|
|
11703
|
+
const map = {};
|
|
11704
|
+
for (const l2 of continuation) {
|
|
11705
|
+
const sub = TOP_LEVEL_KEY.exec(l2.trim());
|
|
11706
|
+
if (sub)
|
|
11707
|
+
map[sub[1].toLowerCase()] = unquote(sub[2]);
|
|
11708
|
+
}
|
|
11709
|
+
nested = nested ?? {};
|
|
11710
|
+
nested[key] = map;
|
|
11711
|
+
} else if (continuation.length) {
|
|
11712
|
+
meta[key] = [value, ...continuation.map((l2) => l2.trim())].filter(Boolean).join(" ").trim();
|
|
11713
|
+
} else {
|
|
11714
|
+
meta[key] = "";
|
|
11715
|
+
}
|
|
11716
|
+
} else {
|
|
11717
|
+
const looksNested = continuation.length > 0 && continuation.every((l2) => !l2.trim() || TOP_LEVEL_KEY.test(l2.trim()));
|
|
11718
|
+
if (continuation.length && !looksNested) {
|
|
11719
|
+
meta[key] = unquote([value, ...continuation.map((l2) => l2.trim())].join(" "));
|
|
11720
|
+
} else if (continuation.length && looksNested) {
|
|
11721
|
+
meta[key] = unquote(value);
|
|
11722
|
+
const map = {};
|
|
11723
|
+
for (const l2 of continuation) {
|
|
11724
|
+
const sub = TOP_LEVEL_KEY.exec(l2.trim());
|
|
11725
|
+
if (sub)
|
|
11726
|
+
map[sub[1].toLowerCase()] = unquote(sub[2]);
|
|
11727
|
+
}
|
|
11728
|
+
nested = nested ?? {};
|
|
11729
|
+
nested[key] = map;
|
|
11730
|
+
} else {
|
|
11731
|
+
meta[key] = unquote(value);
|
|
11732
|
+
}
|
|
11733
|
+
}
|
|
11734
|
+
i2 = j;
|
|
11735
|
+
}
|
|
11736
|
+
return { meta, body: (m2[2] ?? "").trim(), nested, ok: true };
|
|
11737
|
+
}
|
|
11738
|
+
}
|
|
11739
|
+
});
|
|
11740
|
+
|
|
11741
|
+
// ../core/dist/agent/agentTypes.js
|
|
11742
|
+
var require_agentTypes = __commonJS({
|
|
11743
|
+
"../core/dist/agent/agentTypes.js"(exports2) {
|
|
11744
|
+
"use strict";
|
|
11745
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
11746
|
+
if (k2 === void 0)
|
|
11747
|
+
k2 = k;
|
|
11748
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
11749
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
11750
|
+
desc = { enumerable: true, get: function() {
|
|
11751
|
+
return m2[k];
|
|
11752
|
+
} };
|
|
11753
|
+
}
|
|
11754
|
+
Object.defineProperty(o, k2, desc);
|
|
11755
|
+
} : function(o, m2, k, k2) {
|
|
11756
|
+
if (k2 === void 0)
|
|
11757
|
+
k2 = k;
|
|
11758
|
+
o[k2] = m2[k];
|
|
11759
|
+
});
|
|
11760
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
11761
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11762
|
+
} : function(o, v) {
|
|
11763
|
+
o["default"] = v;
|
|
11764
|
+
});
|
|
11765
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
11766
|
+
var ownKeys = function(o) {
|
|
11767
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
11768
|
+
var ar = [];
|
|
11769
|
+
for (var k in o2)
|
|
11770
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
11771
|
+
ar[ar.length] = k;
|
|
11772
|
+
return ar;
|
|
11773
|
+
};
|
|
11774
|
+
return ownKeys(o);
|
|
11775
|
+
};
|
|
11776
|
+
return function(mod) {
|
|
11777
|
+
if (mod && mod.__esModule)
|
|
11778
|
+
return mod;
|
|
11779
|
+
var result = {};
|
|
11780
|
+
if (mod != null) {
|
|
11781
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
11782
|
+
if (k[i2] !== "default")
|
|
11783
|
+
__createBinding(result, mod, k[i2]);
|
|
11784
|
+
}
|
|
11785
|
+
__setModuleDefault(result, mod);
|
|
11786
|
+
return result;
|
|
11787
|
+
};
|
|
11788
|
+
}();
|
|
11789
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11790
|
+
exports2.MODEL_TIER_ALIASES = exports2.VALID_MODELS = void 0;
|
|
11791
|
+
exports2.parseModel = parseModel;
|
|
11792
|
+
exports2.loadAgentTypes = loadAgentTypes;
|
|
11793
|
+
exports2.loadAgentTypesWithWarnings = loadAgentTypesWithWarnings2;
|
|
11794
|
+
exports2.knownToolNames = knownToolNames;
|
|
11795
|
+
exports2.builtinAgents = builtinAgents;
|
|
11796
|
+
exports2.summariseAgents = summariseAgents;
|
|
11797
|
+
exports2.findAgentType = findAgentType;
|
|
11798
|
+
var fs9 = __importStar(__require("fs"));
|
|
11799
|
+
var path7 = __importStar(__require("path"));
|
|
11800
|
+
var os6 = __importStar(__require("os"));
|
|
11801
|
+
var index_1 = require_plugins();
|
|
11802
|
+
var frontmatter_1 = require_frontmatter();
|
|
11803
|
+
var VALID_MEMORY_SCOPES = ["project", "user", "local"];
|
|
11804
|
+
function parseMemoryScope(v) {
|
|
11805
|
+
const s2 = (v ?? "").trim().toLowerCase();
|
|
11806
|
+
return VALID_MEMORY_SCOPES.includes(s2) ? s2 : void 0;
|
|
11807
|
+
}
|
|
11808
|
+
var READ_ONLY_TOOLS = [
|
|
11809
|
+
// Universal
|
|
11810
|
+
"read_file",
|
|
11811
|
+
"search_files",
|
|
11812
|
+
"glob",
|
|
11813
|
+
"list_directory",
|
|
11814
|
+
"bash",
|
|
11815
|
+
"bash_output",
|
|
11816
|
+
// kill_shell belongs next to bash_output: an agent that can start a background
|
|
11817
|
+
// process and poll it but never stop it leaks that process past its own
|
|
11818
|
+
// lifetime. Stopping a shell you started is not a write to the repo.
|
|
11819
|
+
"kill_shell",
|
|
11820
|
+
"notebook_read",
|
|
11821
|
+
"todo_write",
|
|
11822
|
+
"todo_read",
|
|
11823
|
+
// memory_READ, deliberately without memory_write.
|
|
11824
|
+
//
|
|
11825
|
+
// Reading is free capability: a sub-agent that knows "tests run with X" or "never
|
|
11826
|
+
// edit Z directly" does better work, and withholding it meant every sub-agent
|
|
11827
|
+
// rediscovered project conventions from scratch. Writing is a different thing
|
|
11828
|
+
// entirely — memory is durable, cross-session, SHARED state, while sub-agents run
|
|
11829
|
+
// up to 4-wide in parallel with their reasoning hidden from the user. A wrong fact
|
|
11830
|
+
// written there is invisible and permanent, and it competes for a capped, shared
|
|
11831
|
+
// budget that gets periodically condensed.
|
|
11832
|
+
//
|
|
11833
|
+
// So the parent owns writes: a sub-agent that learns something durable says so in
|
|
11834
|
+
// its report, and the main agent — the one actually talking to the user — decides.
|
|
11835
|
+
// A sub-agent that needs its own persistent notes gets the per-agent store instead
|
|
11836
|
+
// (see `memory:` in the frontmatter), which is scoped to itself and cannot pollute
|
|
11837
|
+
// the shared file.
|
|
11838
|
+
"memory_read",
|
|
11839
|
+
// Skills are reusable prompt playbooks, and loop.ts advertises the skills
|
|
11840
|
+
// catalogue to sub-agents at EVERY depth — so withholding the tool that loads
|
|
11841
|
+
// one meant showing every sub-agent a menu it could not order from.
|
|
11842
|
+
"use_skill",
|
|
11843
|
+
// VS Code language server (ignored on the CLI)
|
|
11844
|
+
"get_symbols",
|
|
11845
|
+
"get_workspace_symbols",
|
|
11846
|
+
"find_references",
|
|
11847
|
+
"go_to_definition",
|
|
11848
|
+
"get_hover",
|
|
11849
|
+
"get_diagnostics"
|
|
11850
|
+
];
|
|
11851
|
+
var RESEARCH_TOOLS = [...READ_ONLY_TOOLS, "fetch_url"];
|
|
11852
|
+
var WRITE_TOOLS = [
|
|
11853
|
+
...READ_ONLY_TOOLS,
|
|
11854
|
+
"write_file",
|
|
11855
|
+
"edit_file",
|
|
11856
|
+
"multi_edit",
|
|
11857
|
+
"create_directory",
|
|
11858
|
+
"move_file",
|
|
11859
|
+
"copy_file",
|
|
11860
|
+
// notebook_edit is a write tool like any other, and allowsTestOnlyWrite already
|
|
11861
|
+
// knows its shape (`source` is cell CONTENT, not a path). Omitting it just meant
|
|
11862
|
+
// test-writer silently could not touch notebooks.
|
|
11863
|
+
"notebook_edit",
|
|
11864
|
+
// Office document generation — same write-access tier as write_file, just a
|
|
11865
|
+
// structured content shape instead of raw text (see tools/executor.ts).
|
|
11866
|
+
"write_docx",
|
|
11867
|
+
"write_xlsx",
|
|
11868
|
+
"write_pptx"
|
|
11869
|
+
];
|
|
11870
|
+
var BUILTIN_AGENTS = [
|
|
11871
|
+
{
|
|
11872
|
+
// The catch-all, matching Claude Code's `general-purpose`.
|
|
11873
|
+
//
|
|
11874
|
+
// This capability already existed — omitting `subagent_type` gives an unrestricted
|
|
11875
|
+
// sub-agent — but it had no NAME, and that had two consequences worth fixing:
|
|
11876
|
+
//
|
|
11877
|
+
// 1. `permissions.deny: ["task(...)"]` matches on the agent name, so the ONE
|
|
11878
|
+
// sub-agent that can write files and run bash was the one variant a project
|
|
11879
|
+
// could not disable individually. Only a blanket `deny: ["task"]` reached it.
|
|
11880
|
+
// 2. The model had to infer that leaving the field blank was even an option, so
|
|
11881
|
+
// it would sometimes pick a specialist that fitted badly (an unrestricted
|
|
11882
|
+
// explorer) rather than the general worker it actually wanted.
|
|
11883
|
+
//
|
|
11884
|
+
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
11885
|
+
// inheriting whatever the session permits. That is the same power an unnamed
|
|
11886
|
+
// sub-task always had; naming it changes only who can see and deny it.
|
|
11887
|
+
//
|
|
11888
|
+
// It also makes this the ONLY builtin that can nest, since every specialist omits
|
|
11889
|
+
// `task` on purpose. So the tree this enables is up to `maxSubagentDepth` generations
|
|
11890
|
+
// of unrestricted agents — accepted deliberately, because the alternative (a catch-all
|
|
11891
|
+
// that cannot delegate) removes the one agent suited to orchestration.
|
|
11892
|
+
//
|
|
11893
|
+
// The safety properties that bound it: plan mode is inherited, the user's permission
|
|
11894
|
+
// gate runs on every call, a child's allowlist is INTERSECTED with its parent's so a
|
|
11895
|
+
// restricted ancestor cannot be escaped through this agent, `test_files_only` is
|
|
11896
|
+
// likewise sticky, and both the depth ceiling and the session budget still apply.
|
|
11897
|
+
name: "general-purpose",
|
|
11898
|
+
description: "General-purpose worker for a multi-step task that needs BOTH exploration and changes (edit files, run commands) and that no specialist above fits. Inherits the session model and full tool access, so prefer a narrower agent when one matches.",
|
|
11899
|
+
prompt: "You are a general-purpose engineering sub-agent. Work the task end to end: explore what you need, make the changes, and verify them with the project's own build/test commands.\n\nRules:\n- Mirror existing conventions; make the smallest correct change.\n- Verify before you claim success. If you could not verify, say so explicitly.\n- Your FINAL MESSAGE is the only thing that reaches the main agent: state what you changed (with file paths), what you ran and its outcome, and anything you deliberately left undone.",
|
|
11900
|
+
source: "builtin"
|
|
11901
|
+
},
|
|
11902
|
+
{
|
|
11903
|
+
name: "reviewer",
|
|
11904
|
+
description: "Read-only code reviewer \u2014 finds correctness bugs, edge cases, and security issues in a diff or file set. Cannot modify files.",
|
|
11905
|
+
tools: READ_ONLY_TOOLS,
|
|
11906
|
+
source: "builtin",
|
|
11907
|
+
prompt: [
|
|
11908
|
+
"You are a meticulous senior code reviewer. You NEVER modify files \u2014 you only read, search, and report.",
|
|
11909
|
+
"",
|
|
11910
|
+
"Method:",
|
|
11911
|
+
"1. Read the full context around every change you are asked to review; never judge a hunk in isolation.",
|
|
11912
|
+
"2. Hunt specifically for: correctness bugs, unhandled edge cases (empty/null/unicode/concurrency/timezone),",
|
|
11913
|
+
" security issues (injection, path traversal, secrets in code, unsafe deserialization), breaking API",
|
|
11914
|
+
" changes (search for callers first), silent behaviour changes, and swallowed errors.",
|
|
11915
|
+
"3. Verify test coverage: are the changed paths tested? Were assertions weakened or tests deleted?",
|
|
11916
|
+
"4. Only use bash for read-only commands (git diff/log/show, grep, test runs). Never run mutating commands.",
|
|
11917
|
+
"",
|
|
11918
|
+
"Report format: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F7E2} Suggestion, each with file:line and a concrete fix,",
|
|
11919
|
+
"then a final verdict (APPROVE or REQUEST CHANGES) with a one-paragraph rationale."
|
|
11920
|
+
].join("\n")
|
|
11921
|
+
},
|
|
11922
|
+
// Promoted from the security-audit plugin to a builtin.
|
|
11923
|
+
//
|
|
11924
|
+
// Leaving it plugin-only was indefensible next to `reviewer` being builtin:
|
|
11925
|
+
// reviewer's own prompt already tells it to look for security issues, so
|
|
11926
|
+
// security IS treated as default work — yet the specialist agent for it was
|
|
11927
|
+
// invisible unless the user happened to know the plugin existed. For an agent
|
|
11928
|
+
// that WRITES code, "you only get a security review if you knew to install
|
|
11929
|
+
// something" is the wrong default.
|
|
11930
|
+
{
|
|
11931
|
+
name: "security-auditor",
|
|
11932
|
+
description: "Read-only security auditor \u2014 hunts injection, authz, secrets, and validation flaws in a path or diff. Cannot modify files.",
|
|
11933
|
+
tools: READ_ONLY_TOOLS,
|
|
11934
|
+
// No `model` override — inherits the session's, same as general-purpose.
|
|
11935
|
+
// A previous version forced 'pro' (Claude Opus 5) here, which silently
|
|
11936
|
+
// billed Anthropic even for a session running entirely on OpenAI/DeepSeek/
|
|
11937
|
+
// Qwen. Consistency with the main conversation's provider matters more
|
|
11938
|
+
// than defaulting every specialist to a fixed tier.
|
|
11939
|
+
source: "builtin",
|
|
11940
|
+
prompt: [
|
|
11941
|
+
"You are a security auditor. You find real, exploitable flaws \u2014 not style issues.",
|
|
11942
|
+
"",
|
|
11943
|
+
"Method:",
|
|
11944
|
+
"1. Map the attack surface FIRST: entry points (HTTP routes, message handlers, CLI args, file/network",
|
|
11945
|
+
" input, deserialization), then trace user-controlled data inward to where it is used.",
|
|
11946
|
+
"2. For each finding: file:line, the flaw class, a one-line exploit scenario, and the concrete fix.",
|
|
11947
|
+
"3. Grade severity honestly: Critical = remote compromise or data breach; High = auth bypass/IDOR;",
|
|
11948
|
+
" Medium = needs unusual preconditions; Low = hardening.",
|
|
11949
|
+
"",
|
|
11950
|
+
"Classes worth the most attention, in order: injection (SQL/command/template/prototype), broken",
|
|
11951
|
+
"authz (missing ownership checks, IDOR, trusting client-supplied ids), secrets committed to source,",
|
|
11952
|
+
"path traversal, SSRF, unsafe deserialization, missing rate limits on expensive or auth endpoints,",
|
|
11953
|
+
"and crypto misuse (hand-rolled comparison, predictable randomness).",
|
|
11954
|
+
"",
|
|
11955
|
+
"Hard rules:",
|
|
11956
|
+
"- READ-ONLY: never modify, create or delete files. bash only for read-only inspection.",
|
|
11957
|
+
"- NEVER print a discovered secret's value. Report its location and advise rotation.",
|
|
11958
|
+
"- Distinguish EXPLOITABLE from theoretical, and say which one each finding is.",
|
|
11959
|
+
'- "No issues found in scope X" is a valid, useful result. Do not pad the report to look thorough.'
|
|
11960
|
+
].join("\n")
|
|
11961
|
+
},
|
|
11962
|
+
// The gap Claude Code fills with its built-in `Explore`: read-heavy codebase
|
|
11963
|
+
// search that would otherwise flood the parent's context. Defaults to the
|
|
11964
|
+
// cheapest model on purpose — "find every caller of X" has no need of a
|
|
11965
|
+
// frontier model, and this is the agent most likely to be spawned in bulk.
|
|
11966
|
+
{
|
|
11967
|
+
name: "explorer",
|
|
11968
|
+
// Lean prompt: this agent exists to keep bulk searching cheap, and it reports
|
|
11969
|
+
// findings for the MAIN agent to interpret with full project context.
|
|
11970
|
+
lightPrompt: true,
|
|
11971
|
+
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.",
|
|
11972
|
+
tools: READ_ONLY_TOOLS,
|
|
11973
|
+
// No `model` override — inherits the session's. A previous version forced
|
|
11974
|
+
// 'turbo' (Claude Sonnet 5) on the theory that bulk search doesn't need a
|
|
11975
|
+
// frontier model, but that silently billed Anthropic regardless of which
|
|
11976
|
+
// provider the user actually selected for the conversation.
|
|
11977
|
+
source: "builtin",
|
|
11978
|
+
prompt: [
|
|
11979
|
+
"You map code. You NEVER modify anything.",
|
|
11980
|
+
"",
|
|
11981
|
+
"Method:",
|
|
11982
|
+
"1. Prefer structural search over text search where available (get_workspace_symbols, find_references,",
|
|
11983
|
+
" go_to_definition); fall back to search_files/glob otherwise.",
|
|
11984
|
+
"2. Read only the sections you need \u2014 use read_file with offset/limit on large files instead of",
|
|
11985
|
+
" pulling in thousands of lines.",
|
|
11986
|
+
"3. Follow the real call graph rather than guessing from names.",
|
|
11987
|
+
"",
|
|
11988
|
+
"Your ONLY output is a compact report: the file:line locations that matter, how they relate, and the",
|
|
11989
|
+
"direct answer to the question you were given. This exists to keep bulk search OUT of the parent's",
|
|
11990
|
+
"context, so do not paste large file contents back \u2014 cite locations and summarise. Say plainly when",
|
|
11991
|
+
'something does not exist; a confident wrong answer is far worse than "not found".'
|
|
11992
|
+
].join("\n")
|
|
11993
|
+
},
|
|
11994
|
+
// Matches Claude Code's built-in `Plan`: research a change and return a
|
|
11995
|
+
// strategy, deliberately WITHOUT write access so "make a plan" can never
|
|
11996
|
+
// quietly become "start editing".
|
|
11997
|
+
{
|
|
11998
|
+
name: "planner",
|
|
11999
|
+
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.",
|
|
12000
|
+
tools: RESEARCH_TOOLS,
|
|
12001
|
+
// No `model` override — inherits the session's, for the same reason as
|
|
12002
|
+
// explorer/security-auditor above: a fixed alias silently billed Anthropic
|
|
12003
|
+
// regardless of the user's chosen provider.
|
|
12004
|
+
source: "builtin",
|
|
12005
|
+
prompt: [
|
|
12006
|
+
"You produce implementation plans. You NEVER modify files \u2014 planning and doing are separate steps,",
|
|
12007
|
+
'and this agent exists so "plan it" cannot silently turn into "change it".',
|
|
12008
|
+
"",
|
|
12009
|
+
"Method:",
|
|
12010
|
+
"1. Read the actual code before proposing anything. No plan may rest on an assumed API shape.",
|
|
12011
|
+
"2. Find every affected call site (find_references / search_files) and list them.",
|
|
12012
|
+
"3. Order the steps so the tree stays working after each one \u2014 types, then implementation, then",
|
|
12013
|
+
" tests, then exports/registration.",
|
|
12014
|
+
"",
|
|
12015
|
+
"Output:",
|
|
12016
|
+
"- Goal, in one sentence.",
|
|
12017
|
+
"- Numbered steps, each with the exact files touched and what changes in them.",
|
|
12018
|
+
"- Risks + the specific thing that could break, and how it would be detected.",
|
|
12019
|
+
"- How to verify (the exact test/build command for THIS project, taken from package.json/Makefile).",
|
|
12020
|
+
"- Anything genuinely ambiguous, stated as an open question rather than a silent assumption."
|
|
12021
|
+
].join("\n")
|
|
12022
|
+
},
|
|
12023
|
+
// Promoted from the test-gen plugin. Needs write access — it produces test
|
|
12024
|
+
// files — but is deliberately forbidden from touching source, because "make the
|
|
12025
|
+
// tests pass" is the single most common way an agent destroys signal.
|
|
12026
|
+
{
|
|
12027
|
+
name: "test-writer",
|
|
12028
|
+
description: "Writes tests that follow the project's existing conventions. May create/edit TEST files only \u2014 never production source.",
|
|
12029
|
+
tools: WRITE_TOOLS,
|
|
12030
|
+
// Enforced, not merely requested: the permission gate refuses a write whose
|
|
12031
|
+
// path is not a test file. Without this the allowlist would grant edit_file
|
|
12032
|
+
// for every path and the rule below would be a suggestion the model is free
|
|
12033
|
+
// to rationalise its way past.
|
|
12034
|
+
testFilesOnly: true,
|
|
12035
|
+
source: "builtin",
|
|
12036
|
+
prompt: [
|
|
12037
|
+
"You write tests. You may create and edit TEST files only.",
|
|
12038
|
+
"",
|
|
12039
|
+
"Hard rules \u2014 these are the ways test-writing agents destroy value, so they are non-negotiable:",
|
|
12040
|
+
"- NEVER modify production source to make a test pass. If the code looks wrong, REPORT it and stop.",
|
|
12041
|
+
"- NEVER weaken, delete or skip an existing assertion or test.",
|
|
12042
|
+
"- A test that cannot fail is worse than no test. Every test must be able to fail for one clear reason.",
|
|
12043
|
+
"",
|
|
12044
|
+
"Method:",
|
|
12045
|
+
"1. Read the existing tests FIRST and copy their conventions exactly \u2014 runner, file naming, layout,",
|
|
12046
|
+
" assertion style, fixture/helper patterns. Never introduce a new framework.",
|
|
12047
|
+
"2. Test observable behaviour and the contract, not private internals.",
|
|
12048
|
+
"3. Cover the boring-but-real cases: empty input, null/undefined, unicode and non-BMP characters,",
|
|
12049
|
+
" boundaries, error paths, concurrency where it applies.",
|
|
12050
|
+
"4. No sleeps or wall-clock dependence \u2014 those produce the flaky tests that get deleted later.",
|
|
12051
|
+
"5. RUN the tests you wrote and report the real output. Never claim a test passes without running it."
|
|
12052
|
+
].join("\n")
|
|
12053
|
+
},
|
|
12054
|
+
// The DevOps gap — answered with a READ-ONLY advisor, not an operator.
|
|
12055
|
+
//
|
|
12056
|
+
// A "DevOps agent" with write/apply access is a genuinely different risk class
|
|
12057
|
+
// from the others here: its mistakes are `kubectl delete`, a bad `terraform
|
|
12058
|
+
// apply`, a broken deploy pipeline — often not revertible and affecting
|
|
12059
|
+
// production rather than a working tree. So this one diagnoses and proposes a
|
|
12060
|
+
// diff; a human applies it. That asymmetry is the whole design.
|
|
12061
|
+
{
|
|
12062
|
+
name: "devops-advisor",
|
|
12063
|
+
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.",
|
|
12064
|
+
tools: RESEARCH_TOOLS,
|
|
12065
|
+
// No `model` override — inherits the session's, for the same reason as the
|
|
12066
|
+
// other specialists above.
|
|
12067
|
+
source: "builtin",
|
|
12068
|
+
prompt: [
|
|
12069
|
+
"You are an infrastructure and delivery advisor. You DIAGNOSE and PROPOSE. You never apply changes.",
|
|
12070
|
+
"",
|
|
12071
|
+
"Hard rules:",
|
|
12072
|
+
"- READ-ONLY, and stricter than the other read-only agents: bash is for INSPECTION only",
|
|
12073
|
+
" (git log/diff, cat, grep, `kubectl get/describe`, `docker images`, `terraform plan`).",
|
|
12074
|
+
" NEVER run anything that mutates infrastructure \u2014 no apply/delete/scale/rollout/restart/push,",
|
|
12075
|
+
" no `terraform apply`, no `helm upgrade`. If a fix needs such a command, WRITE IT OUT for a human.",
|
|
12076
|
+
"- Never print secret values from env files, k8s Secrets or CI variables. Reference them by name.",
|
|
12077
|
+
"",
|
|
12078
|
+
"Method:",
|
|
12079
|
+
"1. Read what actually exists \u2014 workflow files, Dockerfiles, manifests, kustomize overlays, the",
|
|
12080
|
+
' deploy scripts \u2014 before drawing any conclusion. Never reason from what a stack "usually" looks like.',
|
|
12081
|
+
"2. Follow the real path a change takes to production, and name the step that is broken or missing.",
|
|
12082
|
+
"3. Check the failure modes that bite hardest: CI path filters that skip files a workload actually",
|
|
12083
|
+
" needs, image tags that do not match what is deployed, missing health probes, absent resource",
|
|
12084
|
+
" limits, secrets baked into images, ports/timeouts inconsistent between proxy and app, and",
|
|
12085
|
+
" migrations that must run before the new image is live.",
|
|
12086
|
+
"",
|
|
12087
|
+
"Output: the diagnosis, the evidence (file:line or command output), the proposed change as a diff or",
|
|
12088
|
+
"exact file content, and the command a human should run to apply and verify it."
|
|
12089
|
+
].join("\n")
|
|
12090
|
+
}
|
|
12091
|
+
];
|
|
12092
|
+
var KNOWN_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
12093
|
+
"read_file",
|
|
12094
|
+
"write_file",
|
|
12095
|
+
"write_docx",
|
|
12096
|
+
"write_xlsx",
|
|
12097
|
+
"write_pptx",
|
|
12098
|
+
"edit_file",
|
|
12099
|
+
"multi_edit",
|
|
12100
|
+
"list_directory",
|
|
12101
|
+
"create_directory",
|
|
12102
|
+
"move_file",
|
|
12103
|
+
"copy_file",
|
|
12104
|
+
"delete_file",
|
|
12105
|
+
"search_files",
|
|
12106
|
+
"glob",
|
|
12107
|
+
"bash",
|
|
12108
|
+
"bash_output",
|
|
12109
|
+
"kill_shell",
|
|
12110
|
+
"notebook_read",
|
|
12111
|
+
"notebook_edit",
|
|
12112
|
+
"todo_write",
|
|
12113
|
+
"todo_read",
|
|
12114
|
+
"memory_write",
|
|
12115
|
+
"memory_read",
|
|
12116
|
+
"use_skill",
|
|
12117
|
+
"fetch_url",
|
|
12118
|
+
"web_search",
|
|
12119
|
+
"generate_image",
|
|
12120
|
+
"stock_photo",
|
|
12121
|
+
"open_in_browser",
|
|
12122
|
+
"browser_action",
|
|
12123
|
+
"task",
|
|
12124
|
+
// Granted by a `memory:` scope rather than listed in a `tools:` line, but a user may
|
|
12125
|
+
// still name it explicitly — so it must validate rather than be flagged as a typo.
|
|
12126
|
+
"agent_memory_write",
|
|
12127
|
+
"get_symbols",
|
|
12128
|
+
"get_workspace_symbols",
|
|
12129
|
+
"find_references",
|
|
12130
|
+
"go_to_definition",
|
|
12131
|
+
"get_hover",
|
|
12132
|
+
"get_diagnostics"
|
|
12133
|
+
]);
|
|
12134
|
+
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
12135
|
+
"name",
|
|
12136
|
+
"description",
|
|
12137
|
+
"tools",
|
|
12138
|
+
"model",
|
|
12139
|
+
"test_files_only",
|
|
12140
|
+
"testfilesonly",
|
|
12141
|
+
"memory"
|
|
12142
|
+
]);
|
|
12143
|
+
exports2.VALID_MODELS = [
|
|
12144
|
+
"claude-sonnet-5",
|
|
12145
|
+
"claude-opus-5",
|
|
12146
|
+
"claude-fable-5",
|
|
12147
|
+
"gpt-5.4",
|
|
12148
|
+
"gpt-5.4-mini",
|
|
12149
|
+
"gpt-4.1",
|
|
12150
|
+
// Legacy tier aliases — still accepted, still resolved by the backend.
|
|
12151
|
+
"turbo",
|
|
12152
|
+
"pro",
|
|
12153
|
+
"ultra",
|
|
12154
|
+
"fast"
|
|
12155
|
+
];
|
|
12156
|
+
exports2.MODEL_TIER_ALIASES = ["turbo", "pro", "ultra", "fast"];
|
|
12157
|
+
function parseModel(v) {
|
|
12158
|
+
const raw = (v ?? "").trim();
|
|
12159
|
+
if (!raw)
|
|
12160
|
+
return void 0;
|
|
12161
|
+
const lower2 = raw.toLowerCase();
|
|
12162
|
+
if (exports2.MODEL_TIER_ALIASES.includes(lower2))
|
|
12163
|
+
return lower2;
|
|
12164
|
+
return raw;
|
|
12165
|
+
}
|
|
12166
|
+
function parseBool(v) {
|
|
12167
|
+
return /^(true|yes|1|on)$/i.test((v ?? "").trim());
|
|
12168
|
+
}
|
|
12169
|
+
function parseToolList(v) {
|
|
12170
|
+
if (!v)
|
|
12171
|
+
return void 0;
|
|
12172
|
+
const tools = v.replace(/^\[|\]$/g, "").split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
|
|
12173
|
+
return tools.length ? tools : void 0;
|
|
12174
|
+
}
|
|
12175
|
+
function loadDir(dir, source2, into, warnings) {
|
|
12176
|
+
let entries;
|
|
12177
|
+
try {
|
|
12178
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
12179
|
+
} catch {
|
|
12180
|
+
return;
|
|
12181
|
+
}
|
|
12182
|
+
for (const entry of entries) {
|
|
12183
|
+
const full = path7.join(dir, entry.name);
|
|
12184
|
+
if (entry.isDirectory()) {
|
|
12185
|
+
loadDir(full, source2, into, warnings);
|
|
12186
|
+
continue;
|
|
12187
|
+
}
|
|
12188
|
+
if (!entry.name.endsWith(".md"))
|
|
12189
|
+
continue;
|
|
12190
|
+
let raw;
|
|
12191
|
+
try {
|
|
12192
|
+
raw = fs9.readFileSync(full, "utf-8");
|
|
12193
|
+
} catch (err) {
|
|
12194
|
+
warnings.push({ file: full, agent: path7.basename(entry.name, ".md"), message: `could not be read (${err.message}) \u2014 this agent was skipped` });
|
|
12195
|
+
continue;
|
|
12196
|
+
}
|
|
12197
|
+
const { meta, body, ok } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
12198
|
+
const name = (meta.name || path7.basename(entry.name, ".md")).trim();
|
|
12199
|
+
if (!name)
|
|
12200
|
+
continue;
|
|
12201
|
+
if (source2 !== "project" && into.has(name))
|
|
12202
|
+
continue;
|
|
12203
|
+
const tools = parseToolList(meta.tools);
|
|
12204
|
+
let effectiveTools = tools;
|
|
12205
|
+
if (!ok) {
|
|
12206
|
+
effectiveTools = READ_ONLY_TOOLS;
|
|
12207
|
+
warnings.push({
|
|
12208
|
+
file: full,
|
|
12209
|
+
agent: name,
|
|
12210
|
+
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."
|
|
12211
|
+
});
|
|
12212
|
+
} else {
|
|
12213
|
+
if (!meta.description) {
|
|
12214
|
+
warnings.push({
|
|
12215
|
+
file: full,
|
|
12216
|
+
agent: name,
|
|
12217
|
+
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."
|
|
12218
|
+
});
|
|
12219
|
+
}
|
|
12220
|
+
if (meta.memory !== void 0 && parseMemoryScope(meta.memory) === void 0) {
|
|
12221
|
+
warnings.push({
|
|
12222
|
+
file: full,
|
|
12223
|
+
agent: name,
|
|
12224
|
+
message: `has memory: "${meta.memory}", which is not a valid scope \u2014 use one of ${VALID_MEMORY_SCOPES.join(", ")}, or omit the line to give this agent no persistent notes.`
|
|
12225
|
+
});
|
|
12226
|
+
}
|
|
12227
|
+
const declaredModel = meta.model === void 0 ? void 0 : parseModel(meta.model);
|
|
12228
|
+
if (meta.model !== void 0 && (declaredModel === void 0 || !exports2.VALID_MODELS.includes(declaredModel))) {
|
|
12229
|
+
warnings.push({
|
|
12230
|
+
file: full,
|
|
12231
|
+
agent: name,
|
|
12232
|
+
message: `has model: "${meta.model}", which this version does not recognise \u2014 expected one of ${exports2.VALID_MODELS.join(", ")}, or omit the line to inherit the current session's model. It will still be sent; the server decides whether it is valid.`
|
|
12233
|
+
});
|
|
12234
|
+
}
|
|
12235
|
+
const unknown = (tools ?? []).filter((t2) => !KNOWN_TOOL_NAMES.has(t2) && !t2.includes("__"));
|
|
12236
|
+
if (unknown.length) {
|
|
12237
|
+
warnings.push({
|
|
12238
|
+
file: full,
|
|
12239
|
+
agent: name,
|
|
12240
|
+
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).`
|
|
12241
|
+
});
|
|
12242
|
+
}
|
|
12243
|
+
if ((tools ?? []).length === 1 && tools?.[0] === "task") {
|
|
12244
|
+
warnings.push({
|
|
12245
|
+
file: full,
|
|
12246
|
+
agent: name,
|
|
12247
|
+
message: "lists `task` as its ONLY tool, so it can delegate but cannot read, write or run anything itself \u2014 it has no way to do or verify work. Add the tools it needs, or drop the `tools:` line to inherit its parent's."
|
|
12248
|
+
});
|
|
12249
|
+
}
|
|
12250
|
+
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
12251
|
+
if (strayKeys.length) {
|
|
12252
|
+
warnings.push({
|
|
12253
|
+
file: full,
|
|
12254
|
+
agent: name,
|
|
12255
|
+
message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.`
|
|
12256
|
+
});
|
|
12257
|
+
}
|
|
12258
|
+
if (!body) {
|
|
12259
|
+
warnings.push({
|
|
12260
|
+
file: full,
|
|
12261
|
+
agent: name,
|
|
12262
|
+
message: "has an empty body \u2014 the text below the frontmatter IS the agent's system prompt, so it currently has no instructions."
|
|
12263
|
+
});
|
|
12264
|
+
}
|
|
12265
|
+
}
|
|
12266
|
+
into.set(name, {
|
|
12267
|
+
name,
|
|
12268
|
+
description: meta.description || `Custom ${name} agent`,
|
|
12269
|
+
tools: effectiveTools,
|
|
12270
|
+
model: parseModel(meta.model),
|
|
12271
|
+
prompt: body,
|
|
12272
|
+
source: source2,
|
|
12273
|
+
// Exposed to user/plugin definitions too — `test_files_only: true` (or
|
|
12274
|
+
// `testFilesOnly`) lets anyone build a test-writing agent that genuinely
|
|
12275
|
+
// cannot touch production source, rather than only the builtin getting
|
|
12276
|
+
// that guarantee.
|
|
12277
|
+
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {},
|
|
12278
|
+
// The `ok &&` is defensive, not load-bearing: parseFrontmatter already returns an
|
|
12279
|
+
// EMPTY meta when it cannot find a `---` block, so `meta.memory` is undefined on
|
|
12280
|
+
// that path regardless. It stays because the guarantee we want — an unreadable
|
|
12281
|
+
// definition never receives a writable store — should survive parseFrontmatter
|
|
12282
|
+
// being changed to salvage partial metadata, which is a plausible future edit.
|
|
12283
|
+
...ok && parseMemoryScope(meta.memory) ? { memory: parseMemoryScope(meta.memory) } : {}
|
|
12284
|
+
});
|
|
12285
|
+
}
|
|
12286
|
+
}
|
|
12287
|
+
function loadAgentTypes(workDir) {
|
|
12288
|
+
return loadAgentTypesWithWarnings2(workDir).types;
|
|
12289
|
+
}
|
|
12290
|
+
function loadAgentTypesWithWarnings2(workDir) {
|
|
12291
|
+
const out = /* @__PURE__ */ new Map();
|
|
12292
|
+
const warnings = [];
|
|
12293
|
+
loadDir(path7.join(workDir, ".nexrall", "agents"), "project", out, warnings);
|
|
12294
|
+
loadDir(path7.join(os6.homedir(), ".nexrall", "agents"), "global", out, warnings);
|
|
12295
|
+
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "agents"))
|
|
12296
|
+
loadDir(dir, "plugin", out, warnings);
|
|
12297
|
+
for (const agent of BUILTIN_AGENTS) {
|
|
12298
|
+
if (!out.has(agent.name))
|
|
12299
|
+
out.set(agent.name, agent);
|
|
11666
12300
|
}
|
|
11667
|
-
|
|
12301
|
+
const builtinOrder = new Map(BUILTIN_AGENTS.map((a, i2) => [a.name, i2]));
|
|
12302
|
+
const types3 = [...out.values()].sort((a, b) => {
|
|
12303
|
+
const ai = builtinOrder.get(a.name);
|
|
12304
|
+
const bi = builtinOrder.get(b.name);
|
|
12305
|
+
if (ai !== void 0 && bi !== void 0)
|
|
12306
|
+
return ai - bi;
|
|
12307
|
+
if (ai !== void 0)
|
|
12308
|
+
return -1;
|
|
12309
|
+
if (bi !== void 0)
|
|
12310
|
+
return 1;
|
|
12311
|
+
return a.name.localeCompare(b.name);
|
|
12312
|
+
});
|
|
12313
|
+
return { types: types3, warnings };
|
|
12314
|
+
}
|
|
12315
|
+
function knownToolNames() {
|
|
12316
|
+
return [...KNOWN_TOOL_NAMES].sort();
|
|
12317
|
+
}
|
|
12318
|
+
function builtinAgents() {
|
|
12319
|
+
return BUILTIN_AGENTS;
|
|
12320
|
+
}
|
|
12321
|
+
function summariseAgents(types3) {
|
|
12322
|
+
if (!types3.length)
|
|
12323
|
+
return "";
|
|
12324
|
+
return types3.map((t2) => {
|
|
12325
|
+
const canWrite = !t2.tools || t2.tools.some((x2) => WRITE_TOOL_HINTS.has(x2));
|
|
12326
|
+
const access = t2.testFilesOnly ? "writes TEST files only" : canWrite ? "can modify files" : "read-only";
|
|
12327
|
+
const model = t2.model ? `, ${t2.model} model` : "";
|
|
12328
|
+
return `- ${t2.name} (${access}${model}): ${t2.description}`;
|
|
12329
|
+
}).join("\n");
|
|
12330
|
+
}
|
|
12331
|
+
var WRITE_TOOL_HINTS = /* @__PURE__ */ new Set([
|
|
12332
|
+
"write_file",
|
|
12333
|
+
"edit_file",
|
|
12334
|
+
"multi_edit",
|
|
12335
|
+
"notebook_edit",
|
|
12336
|
+
"delete_file",
|
|
12337
|
+
"move_file",
|
|
12338
|
+
"copy_file",
|
|
12339
|
+
"write_docx",
|
|
12340
|
+
"write_xlsx",
|
|
12341
|
+
"write_pptx"
|
|
12342
|
+
]);
|
|
12343
|
+
function findAgentType(types3, name) {
|
|
12344
|
+
if (!name)
|
|
12345
|
+
return void 0;
|
|
12346
|
+
const want = name.trim().toLowerCase();
|
|
12347
|
+
return types3.find((t2) => t2.name.toLowerCase() === want);
|
|
11668
12348
|
}
|
|
11669
12349
|
}
|
|
11670
12350
|
});
|
|
@@ -11728,6 +12408,7 @@ var require_loader = __commonJS({
|
|
|
11728
12408
|
var child_process_1 = __require("child_process");
|
|
11729
12409
|
var index_1 = require_plugins();
|
|
11730
12410
|
var frontmatter_1 = require_frontmatter();
|
|
12411
|
+
var agentTypes_1 = require_agentTypes();
|
|
11731
12412
|
var BUILTIN_COMMANDS = [
|
|
11732
12413
|
{
|
|
11733
12414
|
name: "review",
|
|
@@ -11778,7 +12459,7 @@ var require_loader = __commonJS({
|
|
|
11778
12459
|
continue;
|
|
11779
12460
|
if (source2 !== "project" && into.has(name))
|
|
11780
12461
|
continue;
|
|
11781
|
-
const model =
|
|
12462
|
+
const model = (0, agentTypes_1.parseModel)(meta.model);
|
|
11782
12463
|
into.set(name, {
|
|
11783
12464
|
name,
|
|
11784
12465
|
description: meta.description || `Custom /${name} command`,
|
|
@@ -11898,6 +12579,7 @@ var require_skills = __commonJS({
|
|
|
11898
12579
|
};
|
|
11899
12580
|
}();
|
|
11900
12581
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
12582
|
+
exports2.loadSkillsWithWarnings = loadSkillsWithWarnings;
|
|
11901
12583
|
exports2.loadSkills = loadSkills2;
|
|
11902
12584
|
exports2.findSkill = findSkill2;
|
|
11903
12585
|
exports2.autoInvokableSkills = autoInvokableSkills;
|
|
@@ -11910,6 +12592,20 @@ var require_skills = __commonJS({
|
|
|
11910
12592
|
var index_1 = require_plugins();
|
|
11911
12593
|
var loader_1 = require_loader();
|
|
11912
12594
|
var frontmatter_1 = require_frontmatter();
|
|
12595
|
+
var agentTypes_1 = require_agentTypes();
|
|
12596
|
+
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
12597
|
+
"name",
|
|
12598
|
+
"description",
|
|
12599
|
+
"model",
|
|
12600
|
+
"mode",
|
|
12601
|
+
"disable-model-invocation",
|
|
12602
|
+
"user-invocable",
|
|
12603
|
+
"license",
|
|
12604
|
+
"compatibility",
|
|
12605
|
+
"metadata",
|
|
12606
|
+
"allowed-tools"
|
|
12607
|
+
]);
|
|
12608
|
+
var SPEC_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
11913
12609
|
function parseBool(v, fallback) {
|
|
11914
12610
|
if (v === void 0)
|
|
11915
12611
|
return fallback;
|
|
@@ -11920,8 +12616,14 @@ var require_skills = __commonJS({
|
|
|
11920
12616
|
return false;
|
|
11921
12617
|
return fallback;
|
|
11922
12618
|
}
|
|
11923
|
-
function
|
|
11924
|
-
|
|
12619
|
+
function parseSpaceList(v) {
|
|
12620
|
+
if (!v)
|
|
12621
|
+
return void 0;
|
|
12622
|
+
const items = v.split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
|
|
12623
|
+
return items.length ? items : void 0;
|
|
12624
|
+
}
|
|
12625
|
+
function toSkill(meta, body, name, source2, dir, nested) {
|
|
12626
|
+
const model = (0, agentTypes_1.parseModel)(meta.model);
|
|
11925
12627
|
return {
|
|
11926
12628
|
name,
|
|
11927
12629
|
description: meta.description || `Custom /${name} skill`,
|
|
@@ -11931,10 +12633,46 @@ var require_skills = __commonJS({
|
|
|
11931
12633
|
source: source2,
|
|
11932
12634
|
dir,
|
|
11933
12635
|
disableModelInvocation: parseBool(meta["disable-model-invocation"], false),
|
|
11934
|
-
userInvocable: parseBool(meta["user-invocable"], true)
|
|
12636
|
+
userInvocable: parseBool(meta["user-invocable"], true),
|
|
12637
|
+
license: meta.license || void 0,
|
|
12638
|
+
compatibility: meta.compatibility || void 0,
|
|
12639
|
+
metadata: nested?.metadata,
|
|
12640
|
+
allowedTools: parseSpaceList(meta["allowed-tools"])
|
|
11935
12641
|
};
|
|
11936
12642
|
}
|
|
11937
|
-
function
|
|
12643
|
+
function validateSkillMeta(meta, rawName, effectiveName, dirName, file, warnings) {
|
|
12644
|
+
if (!warnings)
|
|
12645
|
+
return;
|
|
12646
|
+
if (!SPEC_NAME_RE.test(rawName)) {
|
|
12647
|
+
warnings.push({
|
|
12648
|
+
file,
|
|
12649
|
+
name: effectiveName,
|
|
12650
|
+
message: `name "${rawName}" does not match the agentskills.io convention (lowercase letters, numbers, and single hyphens only) \u2014 loaded anyway, but other clients reading this skill may warn or reject it.`
|
|
12651
|
+
});
|
|
12652
|
+
} else if (rawName.length > 64) {
|
|
12653
|
+
warnings.push({ file, name: effectiveName, message: `name is ${rawName.length} characters \u2014 the spec caps this at 64. Loaded anyway.` });
|
|
12654
|
+
}
|
|
12655
|
+
if (dirName !== void 0 && meta.name && meta.name.trim().toLowerCase() !== dirName.toLowerCase()) {
|
|
12656
|
+
warnings.push({
|
|
12657
|
+
file,
|
|
12658
|
+
name: effectiveName,
|
|
12659
|
+
message: `frontmatter name "${meta.name}" does not match its directory name "${dirName}" \u2014 the spec requires them to match for cross-client compatibility. Loaded anyway, using the frontmatter name.`
|
|
12660
|
+
});
|
|
12661
|
+
}
|
|
12662
|
+
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
12663
|
+
if (strayKeys.length) {
|
|
12664
|
+
warnings.push({ file, name: effectiveName, message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.` });
|
|
12665
|
+
}
|
|
12666
|
+
const declaredModel = meta.model === void 0 ? void 0 : (0, agentTypes_1.parseModel)(meta.model);
|
|
12667
|
+
if (meta.model !== void 0 && (declaredModel === void 0 || !agentTypes_1.VALID_MODELS.includes(declaredModel))) {
|
|
12668
|
+
warnings.push({
|
|
12669
|
+
file,
|
|
12670
|
+
name: effectiveName,
|
|
12671
|
+
message: `has model: "${meta.model}", which this version does not recognise \u2014 expected one of ${agentTypes_1.VALID_MODELS.join(", ")}, or omit the line to inherit the current session's model. It will still be sent; the server decides whether it is valid.`
|
|
12672
|
+
});
|
|
12673
|
+
}
|
|
12674
|
+
}
|
|
12675
|
+
function loadFlatCommandDir(dir, source2, into, warnings) {
|
|
11938
12676
|
let files;
|
|
11939
12677
|
try {
|
|
11940
12678
|
files = fs9.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
@@ -11942,20 +12680,24 @@ var require_skills = __commonJS({
|
|
|
11942
12680
|
return;
|
|
11943
12681
|
}
|
|
11944
12682
|
for (const file of files) {
|
|
12683
|
+
const full = path7.join(dir, file);
|
|
11945
12684
|
try {
|
|
11946
|
-
const raw = fs9.readFileSync(
|
|
12685
|
+
const raw = fs9.readFileSync(full, "utf-8");
|
|
11947
12686
|
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
11948
|
-
const
|
|
12687
|
+
const rawName = (meta.name || path7.basename(file, ".md")).trim();
|
|
12688
|
+
const name = rawName.toLowerCase();
|
|
11949
12689
|
if (!name)
|
|
11950
12690
|
continue;
|
|
11951
12691
|
if (source2 !== "project" && into.has(name))
|
|
11952
12692
|
continue;
|
|
12693
|
+
validateSkillMeta(meta, rawName, name, void 0, full, warnings);
|
|
11953
12694
|
into.set(name, toSkill(meta, body, name, source2));
|
|
11954
|
-
} catch {
|
|
12695
|
+
} catch (err) {
|
|
12696
|
+
warnings?.push({ file: full, name: path7.basename(file, ".md"), message: `could not be read or parsed (${err.message}) \u2014 skipped.` });
|
|
11955
12697
|
}
|
|
11956
12698
|
}
|
|
11957
12699
|
}
|
|
11958
|
-
function loadSkillDir(root, source2, into) {
|
|
12700
|
+
function loadSkillDir(root, source2, into, warnings) {
|
|
11959
12701
|
let entries;
|
|
11960
12702
|
try {
|
|
11961
12703
|
entries = fs9.readdirSync(root, { withFileTypes: true });
|
|
@@ -11975,14 +12717,17 @@ var require_skills = __commonJS({
|
|
|
11975
12717
|
}
|
|
11976
12718
|
try {
|
|
11977
12719
|
const raw = fs9.readFileSync(skillFile, "utf-8");
|
|
11978
|
-
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
11979
|
-
const
|
|
12720
|
+
const { meta, body, nested } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
12721
|
+
const rawName = (meta.name || entry.name).trim();
|
|
12722
|
+
const name = rawName.toLowerCase();
|
|
11980
12723
|
if (!name)
|
|
11981
12724
|
continue;
|
|
11982
12725
|
if (source2 !== "project" && into.has(name))
|
|
11983
12726
|
continue;
|
|
11984
|
-
|
|
11985
|
-
|
|
12727
|
+
validateSkillMeta(meta, rawName, name, entry.name, skillFile, warnings);
|
|
12728
|
+
into.set(name, toSkill(meta, body, name, source2, skillDir, nested));
|
|
12729
|
+
} catch (err) {
|
|
12730
|
+
warnings?.push({ file: skillFile, name: entry.name, message: `could not be read or parsed (${err.message}) \u2014 skipped.` });
|
|
11986
12731
|
}
|
|
11987
12732
|
}
|
|
11988
12733
|
}
|
|
@@ -12022,21 +12767,27 @@ var require_skills = __commonJS({
|
|
|
12022
12767
|
].join("\n")
|
|
12023
12768
|
}
|
|
12024
12769
|
];
|
|
12025
|
-
function
|
|
12770
|
+
function loadSkillsWithWarnings(workDir) {
|
|
12026
12771
|
const out = /* @__PURE__ */ new Map();
|
|
12027
|
-
|
|
12028
|
-
loadSkillDir(path7.join(workDir, ".
|
|
12029
|
-
loadFlatCommandDir(path7.join(
|
|
12030
|
-
loadSkillDir(path7.join(
|
|
12772
|
+
const warnings = [];
|
|
12773
|
+
loadSkillDir(path7.join(workDir, ".agents", "skills"), "project", out, warnings);
|
|
12774
|
+
loadFlatCommandDir(path7.join(workDir, ".nexrall", "commands"), "project", out, warnings);
|
|
12775
|
+
loadSkillDir(path7.join(workDir, ".nexrall", "skills"), "project", out, warnings);
|
|
12776
|
+
loadFlatCommandDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out, warnings);
|
|
12777
|
+
loadSkillDir(path7.join(os6.homedir(), ".nexrall", "skills"), "global", out, warnings);
|
|
12778
|
+
loadSkillDir(path7.join(os6.homedir(), ".agents", "skills"), "global", out, warnings);
|
|
12031
12779
|
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
12032
|
-
loadFlatCommandDir(dir, "plugin", out);
|
|
12780
|
+
loadFlatCommandDir(dir, "plugin", out, warnings);
|
|
12033
12781
|
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "skills"))
|
|
12034
|
-
loadSkillDir(dir, "plugin", out);
|
|
12782
|
+
loadSkillDir(dir, "plugin", out, warnings);
|
|
12035
12783
|
for (const skill of BUILTIN_SKILLS) {
|
|
12036
12784
|
if (!out.has(skill.name))
|
|
12037
12785
|
out.set(skill.name, skill);
|
|
12038
12786
|
}
|
|
12039
|
-
return [...out.values()];
|
|
12787
|
+
return { skills: [...out.values()], warnings };
|
|
12788
|
+
}
|
|
12789
|
+
function loadSkills2(workDir) {
|
|
12790
|
+
return loadSkillsWithWarnings(workDir).skills;
|
|
12040
12791
|
}
|
|
12041
12792
|
function findSkill2(skills, name) {
|
|
12042
12793
|
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
@@ -101580,625 +102331,6 @@ ${expanded}` };
|
|
|
101580
102331
|
}
|
|
101581
102332
|
});
|
|
101582
102333
|
|
|
101583
|
-
// ../core/dist/agent/agentTypes.js
|
|
101584
|
-
var require_agentTypes = __commonJS({
|
|
101585
|
-
"../core/dist/agent/agentTypes.js"(exports2) {
|
|
101586
|
-
"use strict";
|
|
101587
|
-
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
101588
|
-
if (k2 === void 0)
|
|
101589
|
-
k2 = k;
|
|
101590
|
-
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
101591
|
-
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
101592
|
-
desc = { enumerable: true, get: function() {
|
|
101593
|
-
return m2[k];
|
|
101594
|
-
} };
|
|
101595
|
-
}
|
|
101596
|
-
Object.defineProperty(o, k2, desc);
|
|
101597
|
-
} : function(o, m2, k, k2) {
|
|
101598
|
-
if (k2 === void 0)
|
|
101599
|
-
k2 = k;
|
|
101600
|
-
o[k2] = m2[k];
|
|
101601
|
-
});
|
|
101602
|
-
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
101603
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
101604
|
-
} : function(o, v) {
|
|
101605
|
-
o["default"] = v;
|
|
101606
|
-
});
|
|
101607
|
-
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
101608
|
-
var ownKeys = function(o) {
|
|
101609
|
-
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
101610
|
-
var ar = [];
|
|
101611
|
-
for (var k in o2)
|
|
101612
|
-
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
101613
|
-
ar[ar.length] = k;
|
|
101614
|
-
return ar;
|
|
101615
|
-
};
|
|
101616
|
-
return ownKeys(o);
|
|
101617
|
-
};
|
|
101618
|
-
return function(mod) {
|
|
101619
|
-
if (mod && mod.__esModule)
|
|
101620
|
-
return mod;
|
|
101621
|
-
var result = {};
|
|
101622
|
-
if (mod != null) {
|
|
101623
|
-
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
101624
|
-
if (k[i2] !== "default")
|
|
101625
|
-
__createBinding(result, mod, k[i2]);
|
|
101626
|
-
}
|
|
101627
|
-
__setModuleDefault(result, mod);
|
|
101628
|
-
return result;
|
|
101629
|
-
};
|
|
101630
|
-
}();
|
|
101631
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
101632
|
-
exports2.loadAgentTypes = loadAgentTypes;
|
|
101633
|
-
exports2.loadAgentTypesWithWarnings = loadAgentTypesWithWarnings2;
|
|
101634
|
-
exports2.knownToolNames = knownToolNames;
|
|
101635
|
-
exports2.builtinAgents = builtinAgents;
|
|
101636
|
-
exports2.summariseAgents = summariseAgents;
|
|
101637
|
-
exports2.findAgentType = findAgentType;
|
|
101638
|
-
var fs9 = __importStar(__require("fs"));
|
|
101639
|
-
var path7 = __importStar(__require("path"));
|
|
101640
|
-
var os6 = __importStar(__require("os"));
|
|
101641
|
-
var index_1 = require_plugins();
|
|
101642
|
-
var VALID_MEMORY_SCOPES = ["project", "user", "local"];
|
|
101643
|
-
function parseMemoryScope(v) {
|
|
101644
|
-
const s2 = (v ?? "").trim().toLowerCase();
|
|
101645
|
-
return VALID_MEMORY_SCOPES.includes(s2) ? s2 : void 0;
|
|
101646
|
-
}
|
|
101647
|
-
var READ_ONLY_TOOLS = [
|
|
101648
|
-
// Universal
|
|
101649
|
-
"read_file",
|
|
101650
|
-
"search_files",
|
|
101651
|
-
"glob",
|
|
101652
|
-
"list_directory",
|
|
101653
|
-
"bash",
|
|
101654
|
-
"bash_output",
|
|
101655
|
-
// kill_shell belongs next to bash_output: an agent that can start a background
|
|
101656
|
-
// process and poll it but never stop it leaks that process past its own
|
|
101657
|
-
// lifetime. Stopping a shell you started is not a write to the repo.
|
|
101658
|
-
"kill_shell",
|
|
101659
|
-
"notebook_read",
|
|
101660
|
-
"todo_write",
|
|
101661
|
-
"todo_read",
|
|
101662
|
-
// memory_READ, deliberately without memory_write.
|
|
101663
|
-
//
|
|
101664
|
-
// Reading is free capability: a sub-agent that knows "tests run with X" or "never
|
|
101665
|
-
// edit Z directly" does better work, and withholding it meant every sub-agent
|
|
101666
|
-
// rediscovered project conventions from scratch. Writing is a different thing
|
|
101667
|
-
// entirely — memory is durable, cross-session, SHARED state, while sub-agents run
|
|
101668
|
-
// up to 4-wide in parallel with their reasoning hidden from the user. A wrong fact
|
|
101669
|
-
// written there is invisible and permanent, and it competes for a capped, shared
|
|
101670
|
-
// budget that gets periodically condensed.
|
|
101671
|
-
//
|
|
101672
|
-
// So the parent owns writes: a sub-agent that learns something durable says so in
|
|
101673
|
-
// its report, and the main agent — the one actually talking to the user — decides.
|
|
101674
|
-
// A sub-agent that needs its own persistent notes gets the per-agent store instead
|
|
101675
|
-
// (see `memory:` in the frontmatter), which is scoped to itself and cannot pollute
|
|
101676
|
-
// the shared file.
|
|
101677
|
-
"memory_read",
|
|
101678
|
-
// Skills are reusable prompt playbooks, and loop.ts advertises the skills
|
|
101679
|
-
// catalogue to sub-agents at EVERY depth — so withholding the tool that loads
|
|
101680
|
-
// one meant showing every sub-agent a menu it could not order from.
|
|
101681
|
-
"use_skill",
|
|
101682
|
-
// VS Code language server (ignored on the CLI)
|
|
101683
|
-
"get_symbols",
|
|
101684
|
-
"get_workspace_symbols",
|
|
101685
|
-
"find_references",
|
|
101686
|
-
"go_to_definition",
|
|
101687
|
-
"get_hover",
|
|
101688
|
-
"get_diagnostics"
|
|
101689
|
-
];
|
|
101690
|
-
var RESEARCH_TOOLS = [...READ_ONLY_TOOLS, "fetch_url"];
|
|
101691
|
-
var WRITE_TOOLS = [
|
|
101692
|
-
...READ_ONLY_TOOLS,
|
|
101693
|
-
"write_file",
|
|
101694
|
-
"edit_file",
|
|
101695
|
-
"multi_edit",
|
|
101696
|
-
"create_directory",
|
|
101697
|
-
"move_file",
|
|
101698
|
-
"copy_file",
|
|
101699
|
-
// notebook_edit is a write tool like any other, and allowsTestOnlyWrite already
|
|
101700
|
-
// knows its shape (`source` is cell CONTENT, not a path). Omitting it just meant
|
|
101701
|
-
// test-writer silently could not touch notebooks.
|
|
101702
|
-
"notebook_edit",
|
|
101703
|
-
// Office document generation — same write-access tier as write_file, just a
|
|
101704
|
-
// structured content shape instead of raw text (see tools/executor.ts).
|
|
101705
|
-
"write_docx",
|
|
101706
|
-
"write_xlsx",
|
|
101707
|
-
"write_pptx"
|
|
101708
|
-
];
|
|
101709
|
-
var BUILTIN_AGENTS = [
|
|
101710
|
-
{
|
|
101711
|
-
// The catch-all, matching Claude Code's `general-purpose`.
|
|
101712
|
-
//
|
|
101713
|
-
// This capability already existed — omitting `subagent_type` gives an unrestricted
|
|
101714
|
-
// sub-agent — but it had no NAME, and that had two consequences worth fixing:
|
|
101715
|
-
//
|
|
101716
|
-
// 1. `permissions.deny: ["task(...)"]` matches on the agent name, so the ONE
|
|
101717
|
-
// sub-agent that can write files and run bash was the one variant a project
|
|
101718
|
-
// could not disable individually. Only a blanket `deny: ["task"]` reached it.
|
|
101719
|
-
// 2. The model had to infer that leaving the field blank was even an option, so
|
|
101720
|
-
// it would sometimes pick a specialist that fitted badly (an unrestricted
|
|
101721
|
-
// explorer) rather than the general worker it actually wanted.
|
|
101722
|
-
//
|
|
101723
|
-
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
101724
|
-
// inheriting whatever the session permits. That is the same power an unnamed
|
|
101725
|
-
// sub-task always had; naming it changes only who can see and deny it.
|
|
101726
|
-
//
|
|
101727
|
-
// It also makes this the ONLY builtin that can nest, since every specialist omits
|
|
101728
|
-
// `task` on purpose. So the tree this enables is up to `maxSubagentDepth` generations
|
|
101729
|
-
// of unrestricted agents — accepted deliberately, because the alternative (a catch-all
|
|
101730
|
-
// that cannot delegate) removes the one agent suited to orchestration.
|
|
101731
|
-
//
|
|
101732
|
-
// The safety properties that bound it: plan mode is inherited, the user's permission
|
|
101733
|
-
// gate runs on every call, a child's allowlist is INTERSECTED with its parent's so a
|
|
101734
|
-
// restricted ancestor cannot be escaped through this agent, `test_files_only` is
|
|
101735
|
-
// likewise sticky, and both the depth ceiling and the session budget still apply.
|
|
101736
|
-
name: "general-purpose",
|
|
101737
|
-
description: "General-purpose worker for a multi-step task that needs BOTH exploration and changes (edit files, run commands) and that no specialist above fits. Inherits the session model and full tool access, so prefer a narrower agent when one matches.",
|
|
101738
|
-
prompt: "You are a general-purpose engineering sub-agent. Work the task end to end: explore what you need, make the changes, and verify them with the project's own build/test commands.\n\nRules:\n- Mirror existing conventions; make the smallest correct change.\n- Verify before you claim success. If you could not verify, say so explicitly.\n- Your FINAL MESSAGE is the only thing that reaches the main agent: state what you changed (with file paths), what you ran and its outcome, and anything you deliberately left undone.",
|
|
101739
|
-
source: "builtin"
|
|
101740
|
-
},
|
|
101741
|
-
{
|
|
101742
|
-
name: "reviewer",
|
|
101743
|
-
description: "Read-only code reviewer \u2014 finds correctness bugs, edge cases, and security issues in a diff or file set. Cannot modify files.",
|
|
101744
|
-
tools: READ_ONLY_TOOLS,
|
|
101745
|
-
source: "builtin",
|
|
101746
|
-
prompt: [
|
|
101747
|
-
"You are a meticulous senior code reviewer. You NEVER modify files \u2014 you only read, search, and report.",
|
|
101748
|
-
"",
|
|
101749
|
-
"Method:",
|
|
101750
|
-
"1. Read the full context around every change you are asked to review; never judge a hunk in isolation.",
|
|
101751
|
-
"2. Hunt specifically for: correctness bugs, unhandled edge cases (empty/null/unicode/concurrency/timezone),",
|
|
101752
|
-
" security issues (injection, path traversal, secrets in code, unsafe deserialization), breaking API",
|
|
101753
|
-
" changes (search for callers first), silent behaviour changes, and swallowed errors.",
|
|
101754
|
-
"3. Verify test coverage: are the changed paths tested? Were assertions weakened or tests deleted?",
|
|
101755
|
-
"4. Only use bash for read-only commands (git diff/log/show, grep, test runs). Never run mutating commands.",
|
|
101756
|
-
"",
|
|
101757
|
-
"Report format: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F7E2} Suggestion, each with file:line and a concrete fix,",
|
|
101758
|
-
"then a final verdict (APPROVE or REQUEST CHANGES) with a one-paragraph rationale."
|
|
101759
|
-
].join("\n")
|
|
101760
|
-
},
|
|
101761
|
-
// Promoted from the security-audit plugin to a builtin.
|
|
101762
|
-
//
|
|
101763
|
-
// Leaving it plugin-only was indefensible next to `reviewer` being builtin:
|
|
101764
|
-
// reviewer's own prompt already tells it to look for security issues, so
|
|
101765
|
-
// security IS treated as default work — yet the specialist agent for it was
|
|
101766
|
-
// invisible unless the user happened to know the plugin existed. For an agent
|
|
101767
|
-
// that WRITES code, "you only get a security review if you knew to install
|
|
101768
|
-
// something" is the wrong default.
|
|
101769
|
-
{
|
|
101770
|
-
name: "security-auditor",
|
|
101771
|
-
description: "Read-only security auditor \u2014 hunts injection, authz, secrets, and validation flaws in a path or diff. Cannot modify files.",
|
|
101772
|
-
tools: READ_ONLY_TOOLS,
|
|
101773
|
-
// No `model` override — inherits the session's, same as general-purpose.
|
|
101774
|
-
// A previous version forced 'pro' (Claude Opus 5) here, which silently
|
|
101775
|
-
// billed Anthropic even for a session running entirely on OpenAI/DeepSeek/
|
|
101776
|
-
// Qwen. Consistency with the main conversation's provider matters more
|
|
101777
|
-
// than defaulting every specialist to a fixed tier.
|
|
101778
|
-
source: "builtin",
|
|
101779
|
-
prompt: [
|
|
101780
|
-
"You are a security auditor. You find real, exploitable flaws \u2014 not style issues.",
|
|
101781
|
-
"",
|
|
101782
|
-
"Method:",
|
|
101783
|
-
"1. Map the attack surface FIRST: entry points (HTTP routes, message handlers, CLI args, file/network",
|
|
101784
|
-
" input, deserialization), then trace user-controlled data inward to where it is used.",
|
|
101785
|
-
"2. For each finding: file:line, the flaw class, a one-line exploit scenario, and the concrete fix.",
|
|
101786
|
-
"3. Grade severity honestly: Critical = remote compromise or data breach; High = auth bypass/IDOR;",
|
|
101787
|
-
" Medium = needs unusual preconditions; Low = hardening.",
|
|
101788
|
-
"",
|
|
101789
|
-
"Classes worth the most attention, in order: injection (SQL/command/template/prototype), broken",
|
|
101790
|
-
"authz (missing ownership checks, IDOR, trusting client-supplied ids), secrets committed to source,",
|
|
101791
|
-
"path traversal, SSRF, unsafe deserialization, missing rate limits on expensive or auth endpoints,",
|
|
101792
|
-
"and crypto misuse (hand-rolled comparison, predictable randomness).",
|
|
101793
|
-
"",
|
|
101794
|
-
"Hard rules:",
|
|
101795
|
-
"- READ-ONLY: never modify, create or delete files. bash only for read-only inspection.",
|
|
101796
|
-
"- NEVER print a discovered secret's value. Report its location and advise rotation.",
|
|
101797
|
-
"- Distinguish EXPLOITABLE from theoretical, and say which one each finding is.",
|
|
101798
|
-
'- "No issues found in scope X" is a valid, useful result. Do not pad the report to look thorough.'
|
|
101799
|
-
].join("\n")
|
|
101800
|
-
},
|
|
101801
|
-
// The gap Claude Code fills with its built-in `Explore`: read-heavy codebase
|
|
101802
|
-
// search that would otherwise flood the parent's context. Defaults to the
|
|
101803
|
-
// cheapest model on purpose — "find every caller of X" has no need of a
|
|
101804
|
-
// frontier model, and this is the agent most likely to be spawned in bulk.
|
|
101805
|
-
{
|
|
101806
|
-
name: "explorer",
|
|
101807
|
-
// Lean prompt: this agent exists to keep bulk searching cheap, and it reports
|
|
101808
|
-
// findings for the MAIN agent to interpret with full project context.
|
|
101809
|
-
lightPrompt: true,
|
|
101810
|
-
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.",
|
|
101811
|
-
tools: READ_ONLY_TOOLS,
|
|
101812
|
-
// No `model` override — inherits the session's. A previous version forced
|
|
101813
|
-
// 'turbo' (Claude Sonnet 5) on the theory that bulk search doesn't need a
|
|
101814
|
-
// frontier model, but that silently billed Anthropic regardless of which
|
|
101815
|
-
// provider the user actually selected for the conversation.
|
|
101816
|
-
source: "builtin",
|
|
101817
|
-
prompt: [
|
|
101818
|
-
"You map code. You NEVER modify anything.",
|
|
101819
|
-
"",
|
|
101820
|
-
"Method:",
|
|
101821
|
-
"1. Prefer structural search over text search where available (get_workspace_symbols, find_references,",
|
|
101822
|
-
" go_to_definition); fall back to search_files/glob otherwise.",
|
|
101823
|
-
"2. Read only the sections you need \u2014 use read_file with offset/limit on large files instead of",
|
|
101824
|
-
" pulling in thousands of lines.",
|
|
101825
|
-
"3. Follow the real call graph rather than guessing from names.",
|
|
101826
|
-
"",
|
|
101827
|
-
"Your ONLY output is a compact report: the file:line locations that matter, how they relate, and the",
|
|
101828
|
-
"direct answer to the question you were given. This exists to keep bulk search OUT of the parent's",
|
|
101829
|
-
"context, so do not paste large file contents back \u2014 cite locations and summarise. Say plainly when",
|
|
101830
|
-
'something does not exist; a confident wrong answer is far worse than "not found".'
|
|
101831
|
-
].join("\n")
|
|
101832
|
-
},
|
|
101833
|
-
// Matches Claude Code's built-in `Plan`: research a change and return a
|
|
101834
|
-
// strategy, deliberately WITHOUT write access so "make a plan" can never
|
|
101835
|
-
// quietly become "start editing".
|
|
101836
|
-
{
|
|
101837
|
-
name: "planner",
|
|
101838
|
-
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.",
|
|
101839
|
-
tools: RESEARCH_TOOLS,
|
|
101840
|
-
// No `model` override — inherits the session's, for the same reason as
|
|
101841
|
-
// explorer/security-auditor above: a fixed alias silently billed Anthropic
|
|
101842
|
-
// regardless of the user's chosen provider.
|
|
101843
|
-
source: "builtin",
|
|
101844
|
-
prompt: [
|
|
101845
|
-
"You produce implementation plans. You NEVER modify files \u2014 planning and doing are separate steps,",
|
|
101846
|
-
'and this agent exists so "plan it" cannot silently turn into "change it".',
|
|
101847
|
-
"",
|
|
101848
|
-
"Method:",
|
|
101849
|
-
"1. Read the actual code before proposing anything. No plan may rest on an assumed API shape.",
|
|
101850
|
-
"2. Find every affected call site (find_references / search_files) and list them.",
|
|
101851
|
-
"3. Order the steps so the tree stays working after each one \u2014 types, then implementation, then",
|
|
101852
|
-
" tests, then exports/registration.",
|
|
101853
|
-
"",
|
|
101854
|
-
"Output:",
|
|
101855
|
-
"- Goal, in one sentence.",
|
|
101856
|
-
"- Numbered steps, each with the exact files touched and what changes in them.",
|
|
101857
|
-
"- Risks + the specific thing that could break, and how it would be detected.",
|
|
101858
|
-
"- How to verify (the exact test/build command for THIS project, taken from package.json/Makefile).",
|
|
101859
|
-
"- Anything genuinely ambiguous, stated as an open question rather than a silent assumption."
|
|
101860
|
-
].join("\n")
|
|
101861
|
-
},
|
|
101862
|
-
// Promoted from the test-gen plugin. Needs write access — it produces test
|
|
101863
|
-
// files — but is deliberately forbidden from touching source, because "make the
|
|
101864
|
-
// tests pass" is the single most common way an agent destroys signal.
|
|
101865
|
-
{
|
|
101866
|
-
name: "test-writer",
|
|
101867
|
-
description: "Writes tests that follow the project's existing conventions. May create/edit TEST files only \u2014 never production source.",
|
|
101868
|
-
tools: WRITE_TOOLS,
|
|
101869
|
-
// Enforced, not merely requested: the permission gate refuses a write whose
|
|
101870
|
-
// path is not a test file. Without this the allowlist would grant edit_file
|
|
101871
|
-
// for every path and the rule below would be a suggestion the model is free
|
|
101872
|
-
// to rationalise its way past.
|
|
101873
|
-
testFilesOnly: true,
|
|
101874
|
-
source: "builtin",
|
|
101875
|
-
prompt: [
|
|
101876
|
-
"You write tests. You may create and edit TEST files only.",
|
|
101877
|
-
"",
|
|
101878
|
-
"Hard rules \u2014 these are the ways test-writing agents destroy value, so they are non-negotiable:",
|
|
101879
|
-
"- NEVER modify production source to make a test pass. If the code looks wrong, REPORT it and stop.",
|
|
101880
|
-
"- NEVER weaken, delete or skip an existing assertion or test.",
|
|
101881
|
-
"- A test that cannot fail is worse than no test. Every test must be able to fail for one clear reason.",
|
|
101882
|
-
"",
|
|
101883
|
-
"Method:",
|
|
101884
|
-
"1. Read the existing tests FIRST and copy their conventions exactly \u2014 runner, file naming, layout,",
|
|
101885
|
-
" assertion style, fixture/helper patterns. Never introduce a new framework.",
|
|
101886
|
-
"2. Test observable behaviour and the contract, not private internals.",
|
|
101887
|
-
"3. Cover the boring-but-real cases: empty input, null/undefined, unicode and non-BMP characters,",
|
|
101888
|
-
" boundaries, error paths, concurrency where it applies.",
|
|
101889
|
-
"4. No sleeps or wall-clock dependence \u2014 those produce the flaky tests that get deleted later.",
|
|
101890
|
-
"5. RUN the tests you wrote and report the real output. Never claim a test passes without running it."
|
|
101891
|
-
].join("\n")
|
|
101892
|
-
},
|
|
101893
|
-
// The DevOps gap — answered with a READ-ONLY advisor, not an operator.
|
|
101894
|
-
//
|
|
101895
|
-
// A "DevOps agent" with write/apply access is a genuinely different risk class
|
|
101896
|
-
// from the others here: its mistakes are `kubectl delete`, a bad `terraform
|
|
101897
|
-
// apply`, a broken deploy pipeline — often not revertible and affecting
|
|
101898
|
-
// production rather than a working tree. So this one diagnoses and proposes a
|
|
101899
|
-
// diff; a human applies it. That asymmetry is the whole design.
|
|
101900
|
-
{
|
|
101901
|
-
name: "devops-advisor",
|
|
101902
|
-
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.",
|
|
101903
|
-
tools: RESEARCH_TOOLS,
|
|
101904
|
-
// No `model` override — inherits the session's, for the same reason as the
|
|
101905
|
-
// other specialists above.
|
|
101906
|
-
source: "builtin",
|
|
101907
|
-
prompt: [
|
|
101908
|
-
"You are an infrastructure and delivery advisor. You DIAGNOSE and PROPOSE. You never apply changes.",
|
|
101909
|
-
"",
|
|
101910
|
-
"Hard rules:",
|
|
101911
|
-
"- READ-ONLY, and stricter than the other read-only agents: bash is for INSPECTION only",
|
|
101912
|
-
" (git log/diff, cat, grep, `kubectl get/describe`, `docker images`, `terraform plan`).",
|
|
101913
|
-
" NEVER run anything that mutates infrastructure \u2014 no apply/delete/scale/rollout/restart/push,",
|
|
101914
|
-
" no `terraform apply`, no `helm upgrade`. If a fix needs such a command, WRITE IT OUT for a human.",
|
|
101915
|
-
"- Never print secret values from env files, k8s Secrets or CI variables. Reference them by name.",
|
|
101916
|
-
"",
|
|
101917
|
-
"Method:",
|
|
101918
|
-
"1. Read what actually exists \u2014 workflow files, Dockerfiles, manifests, kustomize overlays, the",
|
|
101919
|
-
' deploy scripts \u2014 before drawing any conclusion. Never reason from what a stack "usually" looks like.',
|
|
101920
|
-
"2. Follow the real path a change takes to production, and name the step that is broken or missing.",
|
|
101921
|
-
"3. Check the failure modes that bite hardest: CI path filters that skip files a workload actually",
|
|
101922
|
-
" needs, image tags that do not match what is deployed, missing health probes, absent resource",
|
|
101923
|
-
" limits, secrets baked into images, ports/timeouts inconsistent between proxy and app, and",
|
|
101924
|
-
" migrations that must run before the new image is live.",
|
|
101925
|
-
"",
|
|
101926
|
-
"Output: the diagnosis, the evidence (file:line or command output), the proposed change as a diff or",
|
|
101927
|
-
"exact file content, and the command a human should run to apply and verify it."
|
|
101928
|
-
].join("\n")
|
|
101929
|
-
}
|
|
101930
|
-
];
|
|
101931
|
-
var KNOWN_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
101932
|
-
"read_file",
|
|
101933
|
-
"write_file",
|
|
101934
|
-
"write_docx",
|
|
101935
|
-
"write_xlsx",
|
|
101936
|
-
"write_pptx",
|
|
101937
|
-
"edit_file",
|
|
101938
|
-
"multi_edit",
|
|
101939
|
-
"list_directory",
|
|
101940
|
-
"create_directory",
|
|
101941
|
-
"move_file",
|
|
101942
|
-
"copy_file",
|
|
101943
|
-
"delete_file",
|
|
101944
|
-
"search_files",
|
|
101945
|
-
"glob",
|
|
101946
|
-
"bash",
|
|
101947
|
-
"bash_output",
|
|
101948
|
-
"kill_shell",
|
|
101949
|
-
"notebook_read",
|
|
101950
|
-
"notebook_edit",
|
|
101951
|
-
"todo_write",
|
|
101952
|
-
"todo_read",
|
|
101953
|
-
"memory_write",
|
|
101954
|
-
"memory_read",
|
|
101955
|
-
"use_skill",
|
|
101956
|
-
"fetch_url",
|
|
101957
|
-
"web_search",
|
|
101958
|
-
"generate_image",
|
|
101959
|
-
"stock_photo",
|
|
101960
|
-
"open_in_browser",
|
|
101961
|
-
"browser_action",
|
|
101962
|
-
"task",
|
|
101963
|
-
// Granted by a `memory:` scope rather than listed in a `tools:` line, but a user may
|
|
101964
|
-
// still name it explicitly — so it must validate rather than be flagged as a typo.
|
|
101965
|
-
"agent_memory_write",
|
|
101966
|
-
"get_symbols",
|
|
101967
|
-
"get_workspace_symbols",
|
|
101968
|
-
"find_references",
|
|
101969
|
-
"go_to_definition",
|
|
101970
|
-
"get_hover",
|
|
101971
|
-
"get_diagnostics"
|
|
101972
|
-
]);
|
|
101973
|
-
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
101974
|
-
"name",
|
|
101975
|
-
"description",
|
|
101976
|
-
"tools",
|
|
101977
|
-
"model",
|
|
101978
|
-
"test_files_only",
|
|
101979
|
-
"testfilesonly",
|
|
101980
|
-
"memory"
|
|
101981
|
-
]);
|
|
101982
|
-
var VALID_MODELS = [
|
|
101983
|
-
"claude-sonnet-5",
|
|
101984
|
-
"claude-opus-5",
|
|
101985
|
-
"claude-fable-5",
|
|
101986
|
-
"gpt-5.4",
|
|
101987
|
-
"gpt-5.4-mini",
|
|
101988
|
-
"gpt-4.1",
|
|
101989
|
-
// Legacy tier aliases — still accepted, still resolved by the backend.
|
|
101990
|
-
"turbo",
|
|
101991
|
-
"pro",
|
|
101992
|
-
"ultra",
|
|
101993
|
-
"fast"
|
|
101994
|
-
];
|
|
101995
|
-
function parseFrontmatter(raw) {
|
|
101996
|
-
const m2 = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
101997
|
-
if (!m2)
|
|
101998
|
-
return { meta: {}, body: raw.trim(), ok: false };
|
|
101999
|
-
const meta = {};
|
|
102000
|
-
for (const line of m2[1].split(/\r?\n/)) {
|
|
102001
|
-
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
|
|
102002
|
-
if (kv)
|
|
102003
|
-
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
|
|
102004
|
-
}
|
|
102005
|
-
return { meta, body: (m2[2] ?? "").trim(), ok: true };
|
|
102006
|
-
}
|
|
102007
|
-
function parseModel(v) {
|
|
102008
|
-
const raw = (v ?? "").trim();
|
|
102009
|
-
if (!raw)
|
|
102010
|
-
return void 0;
|
|
102011
|
-
const lower2 = raw.toLowerCase();
|
|
102012
|
-
if (lower2 === "turbo" || lower2 === "pro" || lower2 === "ultra" || lower2 === "fast")
|
|
102013
|
-
return lower2;
|
|
102014
|
-
return raw;
|
|
102015
|
-
}
|
|
102016
|
-
function parseBool(v) {
|
|
102017
|
-
return /^(true|yes|1|on)$/i.test((v ?? "").trim());
|
|
102018
|
-
}
|
|
102019
|
-
function parseToolList(v) {
|
|
102020
|
-
if (!v)
|
|
102021
|
-
return void 0;
|
|
102022
|
-
const tools = v.replace(/^\[|\]$/g, "").split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
|
|
102023
|
-
return tools.length ? tools : void 0;
|
|
102024
|
-
}
|
|
102025
|
-
function loadDir(dir, source2, into, warnings) {
|
|
102026
|
-
let entries;
|
|
102027
|
-
try {
|
|
102028
|
-
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
102029
|
-
} catch {
|
|
102030
|
-
return;
|
|
102031
|
-
}
|
|
102032
|
-
for (const entry of entries) {
|
|
102033
|
-
const full = path7.join(dir, entry.name);
|
|
102034
|
-
if (entry.isDirectory()) {
|
|
102035
|
-
loadDir(full, source2, into, warnings);
|
|
102036
|
-
continue;
|
|
102037
|
-
}
|
|
102038
|
-
if (!entry.name.endsWith(".md"))
|
|
102039
|
-
continue;
|
|
102040
|
-
let raw;
|
|
102041
|
-
try {
|
|
102042
|
-
raw = fs9.readFileSync(full, "utf-8");
|
|
102043
|
-
} catch (err) {
|
|
102044
|
-
warnings.push({ file: full, agent: path7.basename(entry.name, ".md"), message: `could not be read (${err.message}) \u2014 this agent was skipped` });
|
|
102045
|
-
continue;
|
|
102046
|
-
}
|
|
102047
|
-
const { meta, body, ok } = parseFrontmatter(raw);
|
|
102048
|
-
const name = (meta.name || path7.basename(entry.name, ".md")).trim();
|
|
102049
|
-
if (!name)
|
|
102050
|
-
continue;
|
|
102051
|
-
if (source2 !== "project" && into.has(name))
|
|
102052
|
-
continue;
|
|
102053
|
-
const tools = parseToolList(meta.tools);
|
|
102054
|
-
let effectiveTools = tools;
|
|
102055
|
-
if (!ok) {
|
|
102056
|
-
effectiveTools = READ_ONLY_TOOLS;
|
|
102057
|
-
warnings.push({
|
|
102058
|
-
file: full,
|
|
102059
|
-
agent: name,
|
|
102060
|
-
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."
|
|
102061
|
-
});
|
|
102062
|
-
} else {
|
|
102063
|
-
if (!meta.description) {
|
|
102064
|
-
warnings.push({
|
|
102065
|
-
file: full,
|
|
102066
|
-
agent: name,
|
|
102067
|
-
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."
|
|
102068
|
-
});
|
|
102069
|
-
}
|
|
102070
|
-
if (meta.memory !== void 0 && parseMemoryScope(meta.memory) === void 0) {
|
|
102071
|
-
warnings.push({
|
|
102072
|
-
file: full,
|
|
102073
|
-
agent: name,
|
|
102074
|
-
message: `has memory: "${meta.memory}", which is not a valid scope \u2014 use one of ${VALID_MEMORY_SCOPES.join(", ")}, or omit the line to give this agent no persistent notes.`
|
|
102075
|
-
});
|
|
102076
|
-
}
|
|
102077
|
-
const declaredModel = meta.model === void 0 ? void 0 : parseModel(meta.model);
|
|
102078
|
-
if (meta.model !== void 0 && (declaredModel === void 0 || !VALID_MODELS.includes(declaredModel))) {
|
|
102079
|
-
warnings.push({
|
|
102080
|
-
file: full,
|
|
102081
|
-
agent: name,
|
|
102082
|
-
message: `has model: "${meta.model}", which this version does not recognise \u2014 expected one of ${VALID_MODELS.join(", ")}, or omit the line to inherit the current session's model. It will still be sent; the server decides whether it is valid.`
|
|
102083
|
-
});
|
|
102084
|
-
}
|
|
102085
|
-
const unknown = (tools ?? []).filter((t2) => !KNOWN_TOOL_NAMES.has(t2) && !t2.includes("__"));
|
|
102086
|
-
if (unknown.length) {
|
|
102087
|
-
warnings.push({
|
|
102088
|
-
file: full,
|
|
102089
|
-
agent: name,
|
|
102090
|
-
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).`
|
|
102091
|
-
});
|
|
102092
|
-
}
|
|
102093
|
-
if ((tools ?? []).length === 1 && tools?.[0] === "task") {
|
|
102094
|
-
warnings.push({
|
|
102095
|
-
file: full,
|
|
102096
|
-
agent: name,
|
|
102097
|
-
message: "lists `task` as its ONLY tool, so it can delegate but cannot read, write or run anything itself \u2014 it has no way to do or verify work. Add the tools it needs, or drop the `tools:` line to inherit its parent's."
|
|
102098
|
-
});
|
|
102099
|
-
}
|
|
102100
|
-
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
102101
|
-
if (strayKeys.length) {
|
|
102102
|
-
warnings.push({
|
|
102103
|
-
file: full,
|
|
102104
|
-
agent: name,
|
|
102105
|
-
message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.`
|
|
102106
|
-
});
|
|
102107
|
-
}
|
|
102108
|
-
if (!body) {
|
|
102109
|
-
warnings.push({
|
|
102110
|
-
file: full,
|
|
102111
|
-
agent: name,
|
|
102112
|
-
message: "has an empty body \u2014 the text below the frontmatter IS the agent's system prompt, so it currently has no instructions."
|
|
102113
|
-
});
|
|
102114
|
-
}
|
|
102115
|
-
}
|
|
102116
|
-
into.set(name, {
|
|
102117
|
-
name,
|
|
102118
|
-
description: meta.description || `Custom ${name} agent`,
|
|
102119
|
-
tools: effectiveTools,
|
|
102120
|
-
model: parseModel(meta.model),
|
|
102121
|
-
prompt: body,
|
|
102122
|
-
source: source2,
|
|
102123
|
-
// Exposed to user/plugin definitions too — `test_files_only: true` (or
|
|
102124
|
-
// `testFilesOnly`) lets anyone build a test-writing agent that genuinely
|
|
102125
|
-
// cannot touch production source, rather than only the builtin getting
|
|
102126
|
-
// that guarantee.
|
|
102127
|
-
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {},
|
|
102128
|
-
// The `ok &&` is defensive, not load-bearing: parseFrontmatter already returns an
|
|
102129
|
-
// EMPTY meta when it cannot find a `---` block, so `meta.memory` is undefined on
|
|
102130
|
-
// that path regardless. It stays because the guarantee we want — an unreadable
|
|
102131
|
-
// definition never receives a writable store — should survive parseFrontmatter
|
|
102132
|
-
// being changed to salvage partial metadata, which is a plausible future edit.
|
|
102133
|
-
...ok && parseMemoryScope(meta.memory) ? { memory: parseMemoryScope(meta.memory) } : {}
|
|
102134
|
-
});
|
|
102135
|
-
}
|
|
102136
|
-
}
|
|
102137
|
-
function loadAgentTypes(workDir) {
|
|
102138
|
-
return loadAgentTypesWithWarnings2(workDir).types;
|
|
102139
|
-
}
|
|
102140
|
-
function loadAgentTypesWithWarnings2(workDir) {
|
|
102141
|
-
const out = /* @__PURE__ */ new Map();
|
|
102142
|
-
const warnings = [];
|
|
102143
|
-
loadDir(path7.join(workDir, ".nexrall", "agents"), "project", out, warnings);
|
|
102144
|
-
loadDir(path7.join(os6.homedir(), ".nexrall", "agents"), "global", out, warnings);
|
|
102145
|
-
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "agents"))
|
|
102146
|
-
loadDir(dir, "plugin", out, warnings);
|
|
102147
|
-
for (const agent of BUILTIN_AGENTS) {
|
|
102148
|
-
if (!out.has(agent.name))
|
|
102149
|
-
out.set(agent.name, agent);
|
|
102150
|
-
}
|
|
102151
|
-
const builtinOrder = new Map(BUILTIN_AGENTS.map((a, i2) => [a.name, i2]));
|
|
102152
|
-
const types3 = [...out.values()].sort((a, b) => {
|
|
102153
|
-
const ai = builtinOrder.get(a.name);
|
|
102154
|
-
const bi = builtinOrder.get(b.name);
|
|
102155
|
-
if (ai !== void 0 && bi !== void 0)
|
|
102156
|
-
return ai - bi;
|
|
102157
|
-
if (ai !== void 0)
|
|
102158
|
-
return -1;
|
|
102159
|
-
if (bi !== void 0)
|
|
102160
|
-
return 1;
|
|
102161
|
-
return a.name.localeCompare(b.name);
|
|
102162
|
-
});
|
|
102163
|
-
return { types: types3, warnings };
|
|
102164
|
-
}
|
|
102165
|
-
function knownToolNames() {
|
|
102166
|
-
return [...KNOWN_TOOL_NAMES].sort();
|
|
102167
|
-
}
|
|
102168
|
-
function builtinAgents() {
|
|
102169
|
-
return BUILTIN_AGENTS;
|
|
102170
|
-
}
|
|
102171
|
-
function summariseAgents(types3) {
|
|
102172
|
-
if (!types3.length)
|
|
102173
|
-
return "";
|
|
102174
|
-
return types3.map((t2) => {
|
|
102175
|
-
const canWrite = !t2.tools || t2.tools.some((x2) => WRITE_TOOL_HINTS.has(x2));
|
|
102176
|
-
const access = t2.testFilesOnly ? "writes TEST files only" : canWrite ? "can modify files" : "read-only";
|
|
102177
|
-
const model = t2.model ? `, ${t2.model} model` : "";
|
|
102178
|
-
return `- ${t2.name} (${access}${model}): ${t2.description}`;
|
|
102179
|
-
}).join("\n");
|
|
102180
|
-
}
|
|
102181
|
-
var WRITE_TOOL_HINTS = /* @__PURE__ */ new Set([
|
|
102182
|
-
"write_file",
|
|
102183
|
-
"edit_file",
|
|
102184
|
-
"multi_edit",
|
|
102185
|
-
"notebook_edit",
|
|
102186
|
-
"delete_file",
|
|
102187
|
-
"move_file",
|
|
102188
|
-
"copy_file",
|
|
102189
|
-
"write_docx",
|
|
102190
|
-
"write_xlsx",
|
|
102191
|
-
"write_pptx"
|
|
102192
|
-
]);
|
|
102193
|
-
function findAgentType(types3, name) {
|
|
102194
|
-
if (!name)
|
|
102195
|
-
return void 0;
|
|
102196
|
-
const want = name.trim().toLowerCase();
|
|
102197
|
-
return types3.find((t2) => t2.name.toLowerCase() === want);
|
|
102198
|
-
}
|
|
102199
|
-
}
|
|
102200
|
-
});
|
|
102201
|
-
|
|
102202
102334
|
// ../core/dist/permissions/rules.js
|
|
102203
102335
|
var require_rules = __commonJS({
|
|
102204
102336
|
"../core/dist/permissions/rules.js"(exports2) {
|
|
@@ -106956,6 +107088,9 @@ var require_trust = __commonJS({
|
|
|
106956
107088
|
if (exists(".nexrall", "agents")) {
|
|
106957
107089
|
signals.push(".nexrall/agents/ \u2014 custom sub-agent definitions");
|
|
106958
107090
|
}
|
|
107091
|
+
if (exists(".agents", "skills")) {
|
|
107092
|
+
signals.push(".agents/skills/ \u2014 cross-client agent playbooks (agentskills.io convention)");
|
|
107093
|
+
}
|
|
106959
107094
|
return signals;
|
|
106960
107095
|
}
|
|
106961
107096
|
}
|
|
@@ -143340,6 +143475,9 @@ function detectTrustSignals(dir) {
|
|
|
143340
143475
|
if (exists(".nexrall", "agents")) {
|
|
143341
143476
|
signals.push(".nexrall/agents/ \u2014 custom sub-agent definitions");
|
|
143342
143477
|
}
|
|
143478
|
+
if (exists(".agents", "skills")) {
|
|
143479
|
+
signals.push(".agents/skills/ \u2014 cross-client agent playbooks (agentskills.io convention)");
|
|
143480
|
+
}
|
|
143343
143481
|
return signals;
|
|
143344
143482
|
}
|
|
143345
143483
|
|