nexrall-code 0.5.95 → 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 +752 -739
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11738,9 +11738,9 @@ var require_frontmatter = __commonJS({
|
|
|
11738
11738
|
}
|
|
11739
11739
|
});
|
|
11740
11740
|
|
|
11741
|
-
// ../core/dist/
|
|
11742
|
-
var
|
|
11743
|
-
"../core/dist/
|
|
11741
|
+
// ../core/dist/agent/agentTypes.js
|
|
11742
|
+
var require_agentTypes = __commonJS({
|
|
11743
|
+
"../core/dist/agent/agentTypes.js"(exports2) {
|
|
11744
11744
|
"use strict";
|
|
11745
11745
|
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
11746
11746
|
if (k2 === void 0)
|
|
@@ -11787,155 +11787,767 @@ var require_loader = __commonJS({
|
|
|
11787
11787
|
};
|
|
11788
11788
|
}();
|
|
11789
11789
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11790
|
-
exports2.
|
|
11791
|
-
exports2.
|
|
11792
|
-
exports2.
|
|
11793
|
-
exports2.
|
|
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;
|
|
11794
11798
|
var fs9 = __importStar(__require("fs"));
|
|
11795
11799
|
var path7 = __importStar(__require("path"));
|
|
11796
11800
|
var os6 = __importStar(__require("os"));
|
|
11797
|
-
var child_process_1 = __require("child_process");
|
|
11798
11801
|
var index_1 = require_plugins();
|
|
11799
11802
|
var frontmatter_1 = require_frontmatter();
|
|
11800
|
-
var
|
|
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 = [
|
|
11801
11871
|
{
|
|
11802
|
-
|
|
11803
|
-
|
|
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,
|
|
11804
11906
|
source: "builtin",
|
|
11805
|
-
|
|
11806
|
-
"
|
|
11807
|
-
"(If no target given, review the uncommitted working-tree changes below. If a branch or PR",
|
|
11808
|
-
"number is given, run the appropriate `git diff <base>...` or `gh pr diff <n>` yourself first.)",
|
|
11907
|
+
prompt: [
|
|
11908
|
+
"You are a meticulous senior code reviewer. You NEVER modify files \u2014 you only read, search, and report.",
|
|
11809
11909
|
"",
|
|
11810
|
-
"
|
|
11811
|
-
"
|
|
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.",
|
|
11812
11917
|
"",
|
|
11813
|
-
"
|
|
11814
|
-
"
|
|
11815
|
-
|
|
11816
|
-
|
|
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.",
|
|
11817
11942
|
"",
|
|
11818
|
-
"
|
|
11819
|
-
"1.
|
|
11820
|
-
"
|
|
11821
|
-
"
|
|
11822
|
-
"
|
|
11823
|
-
"
|
|
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.",
|
|
11824
11949
|
"",
|
|
11825
|
-
"
|
|
11826
|
-
"
|
|
11827
|
-
"
|
|
11828
|
-
"
|
|
11829
|
-
"
|
|
11830
|
-
"
|
|
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."
|
|
11831
12089
|
].join("\n")
|
|
11832
12090
|
}
|
|
11833
12091
|
];
|
|
11834
|
-
|
|
11835
|
-
|
|
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;
|
|
11836
12177
|
try {
|
|
11837
|
-
|
|
12178
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
11838
12179
|
} catch {
|
|
11839
12180
|
return;
|
|
11840
12181
|
}
|
|
11841
|
-
for (const
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
if (!name)
|
|
11847
|
-
continue;
|
|
11848
|
-
if (source2 !== "project" && into.has(name))
|
|
11849
|
-
continue;
|
|
11850
|
-
const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
|
|
11851
|
-
into.set(name, {
|
|
11852
|
-
name,
|
|
11853
|
-
description: meta.description || `Custom /${name} command`,
|
|
11854
|
-
model,
|
|
11855
|
-
mode: meta.mode || void 0,
|
|
11856
|
-
body,
|
|
11857
|
-
source: source2
|
|
11858
|
-
});
|
|
11859
|
-
} catch {
|
|
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;
|
|
11860
12187
|
}
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
const out = /* @__PURE__ */ new Map();
|
|
11865
|
-
loadDir(path7.join(workDir, ".nexrall", "commands"), "project", out);
|
|
11866
|
-
loadDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out);
|
|
11867
|
-
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
11868
|
-
loadDir(dir, "plugin", out);
|
|
11869
|
-
for (const cmd of BUILTIN_COMMANDS) {
|
|
11870
|
-
if (!out.has(cmd.name))
|
|
11871
|
-
out.set(cmd.name, cmd);
|
|
11872
|
-
}
|
|
11873
|
-
return [...out.values()];
|
|
11874
|
-
}
|
|
11875
|
-
function findSlashCommand(cmds, name) {
|
|
11876
|
-
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
11877
|
-
return cmds.find((c) => c.name === want);
|
|
11878
|
-
}
|
|
11879
|
-
function expandBody(body, argString, workDir) {
|
|
11880
|
-
const args = argString.trim();
|
|
11881
|
-
const positional = args ? args.split(/\s+/) : [];
|
|
11882
|
-
let out = body;
|
|
11883
|
-
out = out.replace(/!`([^`]+)`/g, (_m, c) => {
|
|
12188
|
+
if (!entry.name.endsWith(".md"))
|
|
12189
|
+
continue;
|
|
12190
|
+
let raw;
|
|
11884
12191
|
try {
|
|
11885
|
-
|
|
11886
|
-
return stdout.trim();
|
|
12192
|
+
raw = fs9.readFileSync(full, "utf-8");
|
|
11887
12193
|
} catch (err) {
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
});
|
|
11891
|
-
out = out.replace(/(^|\s)@([^\s]+)/g, (_m, lead, rel) => {
|
|
11892
|
-
const abs = path7.isAbsolute(rel) ? rel : path7.join(workDir, rel);
|
|
11893
|
-
try {
|
|
11894
|
-
const content = fs9.readFileSync(abs, "utf-8").slice(0, 12e3);
|
|
11895
|
-
return `${lead}
|
|
11896
|
-
[File: ${rel}]
|
|
11897
|
-
\`\`\`
|
|
11898
|
-
${content}
|
|
11899
|
-
\`\`\`
|
|
11900
|
-
`;
|
|
11901
|
-
} catch {
|
|
11902
|
-
return `${lead}[missing file: ${rel}]`;
|
|
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;
|
|
11903
12196
|
}
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
}
|
|
11920
|
-
|
|
11921
|
-
|
|
11922
|
-
|
|
11923
|
-
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
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);
|
|
12300
|
+
}
|
|
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);
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
12350
|
+
});
|
|
12351
|
+
|
|
12352
|
+
// ../core/dist/commands/loader.js
|
|
12353
|
+
var require_loader = __commonJS({
|
|
12354
|
+
"../core/dist/commands/loader.js"(exports2) {
|
|
12355
|
+
"use strict";
|
|
12356
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
12357
|
+
if (k2 === void 0)
|
|
12358
|
+
k2 = k;
|
|
12359
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
12360
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
12361
|
+
desc = { enumerable: true, get: function() {
|
|
12362
|
+
return m2[k];
|
|
12363
|
+
} };
|
|
12364
|
+
}
|
|
12365
|
+
Object.defineProperty(o, k2, desc);
|
|
12366
|
+
} : function(o, m2, k, k2) {
|
|
12367
|
+
if (k2 === void 0)
|
|
12368
|
+
k2 = k;
|
|
12369
|
+
o[k2] = m2[k];
|
|
12370
|
+
});
|
|
12371
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
12372
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
12373
|
+
} : function(o, v) {
|
|
12374
|
+
o["default"] = v;
|
|
12375
|
+
});
|
|
12376
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
12377
|
+
var ownKeys = function(o) {
|
|
12378
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
12379
|
+
var ar = [];
|
|
12380
|
+
for (var k in o2)
|
|
12381
|
+
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
12382
|
+
ar[ar.length] = k;
|
|
12383
|
+
return ar;
|
|
12384
|
+
};
|
|
12385
|
+
return ownKeys(o);
|
|
12386
|
+
};
|
|
12387
|
+
return function(mod) {
|
|
12388
|
+
if (mod && mod.__esModule)
|
|
12389
|
+
return mod;
|
|
12390
|
+
var result = {};
|
|
12391
|
+
if (mod != null) {
|
|
12392
|
+
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
12393
|
+
if (k[i2] !== "default")
|
|
12394
|
+
__createBinding(result, mod, k[i2]);
|
|
12395
|
+
}
|
|
12396
|
+
__setModuleDefault(result, mod);
|
|
12397
|
+
return result;
|
|
12398
|
+
};
|
|
12399
|
+
}();
|
|
12400
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
12401
|
+
exports2.loadSlashCommands = loadSlashCommands;
|
|
12402
|
+
exports2.findSlashCommand = findSlashCommand;
|
|
12403
|
+
exports2.expandBody = expandBody;
|
|
12404
|
+
exports2.expandCommand = expandCommand;
|
|
12405
|
+
var fs9 = __importStar(__require("fs"));
|
|
12406
|
+
var path7 = __importStar(__require("path"));
|
|
12407
|
+
var os6 = __importStar(__require("os"));
|
|
12408
|
+
var child_process_1 = __require("child_process");
|
|
12409
|
+
var index_1 = require_plugins();
|
|
12410
|
+
var frontmatter_1 = require_frontmatter();
|
|
12411
|
+
var agentTypes_1 = require_agentTypes();
|
|
12412
|
+
var BUILTIN_COMMANDS = [
|
|
12413
|
+
{
|
|
12414
|
+
name: "review",
|
|
12415
|
+
description: "Review uncommitted changes (or a PR/branch diff) for bugs and risks",
|
|
12416
|
+
source: "builtin",
|
|
12417
|
+
body: [
|
|
12418
|
+
"Review the following diff like a meticulous senior engineer. Target: $ARGUMENTS",
|
|
12419
|
+
"(If no target given, review the uncommitted working-tree changes below. If a branch or PR",
|
|
12420
|
+
"number is given, run the appropriate `git diff <base>...` or `gh pr diff <n>` yourself first.)",
|
|
12421
|
+
"",
|
|
12422
|
+
"Branch: !`git branch --show-current`",
|
|
12423
|
+
"Status: !`git status --short`",
|
|
12424
|
+
"",
|
|
12425
|
+
"Diff (uncommitted):",
|
|
12426
|
+
"```diff",
|
|
12427
|
+
"!`git diff HEAD --unified=5 --no-color | head -4000`",
|
|
12428
|
+
"```",
|
|
12429
|
+
"",
|
|
12430
|
+
"Review methodology:",
|
|
12431
|
+
"1. Read the surrounding code of every changed hunk (read_file with offset/limit) \u2014 never judge a hunk in isolation.",
|
|
12432
|
+
"2. Look for: correctness bugs, edge cases (empty/null/unicode/concurrency), security issues",
|
|
12433
|
+
" (injection, path traversal, secrets), breaking API changes (find_references / search callers),",
|
|
12434
|
+
" silent behaviour changes, and missing error handling.",
|
|
12435
|
+
"3. Check tests: do existing tests cover the change? Are assertions weakened?",
|
|
12436
|
+
"",
|
|
12437
|
+
"Output format:",
|
|
12438
|
+
"- \u{1F534} Critical (must fix before merge) \u2014 with file:line and a concrete fix",
|
|
12439
|
+
"- \u{1F7E1} Warning (should fix) \u2014 with file:line",
|
|
12440
|
+
"- \u{1F7E2} Suggestion (nice to have)",
|
|
12441
|
+
"- Verdict: APPROVE / REQUEST CHANGES with a one-paragraph summary.",
|
|
12442
|
+
"Do NOT modify any files \u2014 this is a read-only review."
|
|
12443
|
+
].join("\n")
|
|
12444
|
+
}
|
|
12445
|
+
];
|
|
12446
|
+
function loadDir(dir, source2, into) {
|
|
12447
|
+
let files;
|
|
12448
|
+
try {
|
|
12449
|
+
files = fs9.readdirSync(dir).filter((f3) => f3.endsWith(".md"));
|
|
12450
|
+
} catch {
|
|
12451
|
+
return;
|
|
12452
|
+
}
|
|
12453
|
+
for (const file of files) {
|
|
12454
|
+
try {
|
|
12455
|
+
const raw = fs9.readFileSync(path7.join(dir, file), "utf-8");
|
|
12456
|
+
const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
12457
|
+
const name = (meta.name || path7.basename(file, ".md")).trim().toLowerCase();
|
|
12458
|
+
if (!name)
|
|
12459
|
+
continue;
|
|
12460
|
+
if (source2 !== "project" && into.has(name))
|
|
12461
|
+
continue;
|
|
12462
|
+
const model = (0, agentTypes_1.parseModel)(meta.model);
|
|
12463
|
+
into.set(name, {
|
|
12464
|
+
name,
|
|
12465
|
+
description: meta.description || `Custom /${name} command`,
|
|
12466
|
+
model,
|
|
12467
|
+
mode: meta.mode || void 0,
|
|
12468
|
+
body,
|
|
12469
|
+
source: source2
|
|
12470
|
+
});
|
|
12471
|
+
} catch {
|
|
12472
|
+
}
|
|
12473
|
+
}
|
|
12474
|
+
}
|
|
12475
|
+
function loadSlashCommands(workDir) {
|
|
12476
|
+
const out = /* @__PURE__ */ new Map();
|
|
12477
|
+
loadDir(path7.join(workDir, ".nexrall", "commands"), "project", out);
|
|
12478
|
+
loadDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out);
|
|
12479
|
+
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "commands"))
|
|
12480
|
+
loadDir(dir, "plugin", out);
|
|
12481
|
+
for (const cmd of BUILTIN_COMMANDS) {
|
|
12482
|
+
if (!out.has(cmd.name))
|
|
12483
|
+
out.set(cmd.name, cmd);
|
|
12484
|
+
}
|
|
12485
|
+
return [...out.values()];
|
|
12486
|
+
}
|
|
12487
|
+
function findSlashCommand(cmds, name) {
|
|
12488
|
+
const want = name.replace(/^\//, "").trim().toLowerCase();
|
|
12489
|
+
return cmds.find((c) => c.name === want);
|
|
12490
|
+
}
|
|
12491
|
+
function expandBody(body, argString, workDir) {
|
|
12492
|
+
const args = argString.trim();
|
|
12493
|
+
const positional = args ? args.split(/\s+/) : [];
|
|
12494
|
+
let out = body;
|
|
12495
|
+
out = out.replace(/!`([^`]+)`/g, (_m, c) => {
|
|
12496
|
+
try {
|
|
12497
|
+
const stdout = (0, child_process_1.execSync)(c, { cwd: workDir, encoding: "utf-8", timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] });
|
|
12498
|
+
return stdout.trim();
|
|
12499
|
+
} catch (err) {
|
|
12500
|
+
return `[command failed: ${c} \u2014 ${err.message}]`;
|
|
12501
|
+
}
|
|
12502
|
+
});
|
|
12503
|
+
out = out.replace(/(^|\s)@([^\s]+)/g, (_m, lead, rel) => {
|
|
12504
|
+
const abs = path7.isAbsolute(rel) ? rel : path7.join(workDir, rel);
|
|
12505
|
+
try {
|
|
12506
|
+
const content = fs9.readFileSync(abs, "utf-8").slice(0, 12e3);
|
|
12507
|
+
return `${lead}
|
|
12508
|
+
[File: ${rel}]
|
|
12509
|
+
\`\`\`
|
|
12510
|
+
${content}
|
|
12511
|
+
\`\`\`
|
|
12512
|
+
`;
|
|
12513
|
+
} catch {
|
|
12514
|
+
return `${lead}[missing file: ${rel}]`;
|
|
12515
|
+
}
|
|
12516
|
+
});
|
|
12517
|
+
out = out.replace(/\$(\d+)/g, (_m, n) => positional[Number(n) - 1] ?? "");
|
|
12518
|
+
const hadArgsToken = /\$ARGUMENTS/.test(out);
|
|
12519
|
+
out = out.replace(/\$ARGUMENTS/g, args);
|
|
12520
|
+
if (!hadArgsToken && !/\$\d+/.test(body) && args) {
|
|
12521
|
+
out = `${out}
|
|
12522
|
+
|
|
12523
|
+
${args}`;
|
|
12524
|
+
}
|
|
12525
|
+
return out.trim();
|
|
12526
|
+
}
|
|
12527
|
+
function expandCommand(cmd, argString, workDir) {
|
|
12528
|
+
return expandBody(cmd.body, argString, workDir);
|
|
12529
|
+
}
|
|
12530
|
+
}
|
|
12531
|
+
});
|
|
12532
|
+
|
|
12533
|
+
// ../core/dist/agent/skills.js
|
|
12534
|
+
var require_skills = __commonJS({
|
|
12535
|
+
"../core/dist/agent/skills.js"(exports2) {
|
|
12536
|
+
"use strict";
|
|
12537
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
12538
|
+
if (k2 === void 0)
|
|
12539
|
+
k2 = k;
|
|
12540
|
+
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
12541
|
+
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
12542
|
+
desc = { enumerable: true, get: function() {
|
|
12543
|
+
return m2[k];
|
|
12544
|
+
} };
|
|
12545
|
+
}
|
|
12546
|
+
Object.defineProperty(o, k2, desc);
|
|
12547
|
+
} : function(o, m2, k, k2) {
|
|
12548
|
+
if (k2 === void 0)
|
|
12549
|
+
k2 = k;
|
|
12550
|
+
o[k2] = m2[k];
|
|
11939
12551
|
});
|
|
11940
12552
|
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
11941
12553
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
@@ -11980,6 +12592,7 @@ var require_skills = __commonJS({
|
|
|
11980
12592
|
var index_1 = require_plugins();
|
|
11981
12593
|
var loader_1 = require_loader();
|
|
11982
12594
|
var frontmatter_1 = require_frontmatter();
|
|
12595
|
+
var agentTypes_1 = require_agentTypes();
|
|
11983
12596
|
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
11984
12597
|
"name",
|
|
11985
12598
|
"description",
|
|
@@ -12010,7 +12623,7 @@ var require_skills = __commonJS({
|
|
|
12010
12623
|
return items.length ? items : void 0;
|
|
12011
12624
|
}
|
|
12012
12625
|
function toSkill(meta, body, name, source2, dir, nested) {
|
|
12013
|
-
const model =
|
|
12626
|
+
const model = (0, agentTypes_1.parseModel)(meta.model);
|
|
12014
12627
|
return {
|
|
12015
12628
|
name,
|
|
12016
12629
|
description: meta.description || `Custom /${name} skill`,
|
|
@@ -12050,6 +12663,14 @@ var require_skills = __commonJS({
|
|
|
12050
12663
|
if (strayKeys.length) {
|
|
12051
12664
|
warnings.push({ file, name: effectiveName, message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.` });
|
|
12052
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
|
+
}
|
|
12053
12674
|
}
|
|
12054
12675
|
function loadFlatCommandDir(dir, source2, into, warnings) {
|
|
12055
12676
|
let files;
|
|
@@ -101710,614 +102331,6 @@ ${expanded}` };
|
|
|
101710
102331
|
}
|
|
101711
102332
|
});
|
|
101712
102333
|
|
|
101713
|
-
// ../core/dist/agent/agentTypes.js
|
|
101714
|
-
var require_agentTypes = __commonJS({
|
|
101715
|
-
"../core/dist/agent/agentTypes.js"(exports2) {
|
|
101716
|
-
"use strict";
|
|
101717
|
-
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? function(o, m2, k, k2) {
|
|
101718
|
-
if (k2 === void 0)
|
|
101719
|
-
k2 = k;
|
|
101720
|
-
var desc = Object.getOwnPropertyDescriptor(m2, k);
|
|
101721
|
-
if (!desc || ("get" in desc ? !m2.__esModule : desc.writable || desc.configurable)) {
|
|
101722
|
-
desc = { enumerable: true, get: function() {
|
|
101723
|
-
return m2[k];
|
|
101724
|
-
} };
|
|
101725
|
-
}
|
|
101726
|
-
Object.defineProperty(o, k2, desc);
|
|
101727
|
-
} : function(o, m2, k, k2) {
|
|
101728
|
-
if (k2 === void 0)
|
|
101729
|
-
k2 = k;
|
|
101730
|
-
o[k2] = m2[k];
|
|
101731
|
-
});
|
|
101732
|
-
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? function(o, v) {
|
|
101733
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
101734
|
-
} : function(o, v) {
|
|
101735
|
-
o["default"] = v;
|
|
101736
|
-
});
|
|
101737
|
-
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ function() {
|
|
101738
|
-
var ownKeys = function(o) {
|
|
101739
|
-
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
101740
|
-
var ar = [];
|
|
101741
|
-
for (var k in o2)
|
|
101742
|
-
if (Object.prototype.hasOwnProperty.call(o2, k))
|
|
101743
|
-
ar[ar.length] = k;
|
|
101744
|
-
return ar;
|
|
101745
|
-
};
|
|
101746
|
-
return ownKeys(o);
|
|
101747
|
-
};
|
|
101748
|
-
return function(mod) {
|
|
101749
|
-
if (mod && mod.__esModule)
|
|
101750
|
-
return mod;
|
|
101751
|
-
var result = {};
|
|
101752
|
-
if (mod != null) {
|
|
101753
|
-
for (var k = ownKeys(mod), i2 = 0; i2 < k.length; i2++)
|
|
101754
|
-
if (k[i2] !== "default")
|
|
101755
|
-
__createBinding(result, mod, k[i2]);
|
|
101756
|
-
}
|
|
101757
|
-
__setModuleDefault(result, mod);
|
|
101758
|
-
return result;
|
|
101759
|
-
};
|
|
101760
|
-
}();
|
|
101761
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
101762
|
-
exports2.loadAgentTypes = loadAgentTypes;
|
|
101763
|
-
exports2.loadAgentTypesWithWarnings = loadAgentTypesWithWarnings2;
|
|
101764
|
-
exports2.knownToolNames = knownToolNames;
|
|
101765
|
-
exports2.builtinAgents = builtinAgents;
|
|
101766
|
-
exports2.summariseAgents = summariseAgents;
|
|
101767
|
-
exports2.findAgentType = findAgentType;
|
|
101768
|
-
var fs9 = __importStar(__require("fs"));
|
|
101769
|
-
var path7 = __importStar(__require("path"));
|
|
101770
|
-
var os6 = __importStar(__require("os"));
|
|
101771
|
-
var index_1 = require_plugins();
|
|
101772
|
-
var frontmatter_1 = require_frontmatter();
|
|
101773
|
-
var VALID_MEMORY_SCOPES = ["project", "user", "local"];
|
|
101774
|
-
function parseMemoryScope(v) {
|
|
101775
|
-
const s2 = (v ?? "").trim().toLowerCase();
|
|
101776
|
-
return VALID_MEMORY_SCOPES.includes(s2) ? s2 : void 0;
|
|
101777
|
-
}
|
|
101778
|
-
var READ_ONLY_TOOLS = [
|
|
101779
|
-
// Universal
|
|
101780
|
-
"read_file",
|
|
101781
|
-
"search_files",
|
|
101782
|
-
"glob",
|
|
101783
|
-
"list_directory",
|
|
101784
|
-
"bash",
|
|
101785
|
-
"bash_output",
|
|
101786
|
-
// kill_shell belongs next to bash_output: an agent that can start a background
|
|
101787
|
-
// process and poll it but never stop it leaks that process past its own
|
|
101788
|
-
// lifetime. Stopping a shell you started is not a write to the repo.
|
|
101789
|
-
"kill_shell",
|
|
101790
|
-
"notebook_read",
|
|
101791
|
-
"todo_write",
|
|
101792
|
-
"todo_read",
|
|
101793
|
-
// memory_READ, deliberately without memory_write.
|
|
101794
|
-
//
|
|
101795
|
-
// Reading is free capability: a sub-agent that knows "tests run with X" or "never
|
|
101796
|
-
// edit Z directly" does better work, and withholding it meant every sub-agent
|
|
101797
|
-
// rediscovered project conventions from scratch. Writing is a different thing
|
|
101798
|
-
// entirely — memory is durable, cross-session, SHARED state, while sub-agents run
|
|
101799
|
-
// up to 4-wide in parallel with their reasoning hidden from the user. A wrong fact
|
|
101800
|
-
// written there is invisible and permanent, and it competes for a capped, shared
|
|
101801
|
-
// budget that gets periodically condensed.
|
|
101802
|
-
//
|
|
101803
|
-
// So the parent owns writes: a sub-agent that learns something durable says so in
|
|
101804
|
-
// its report, and the main agent — the one actually talking to the user — decides.
|
|
101805
|
-
// A sub-agent that needs its own persistent notes gets the per-agent store instead
|
|
101806
|
-
// (see `memory:` in the frontmatter), which is scoped to itself and cannot pollute
|
|
101807
|
-
// the shared file.
|
|
101808
|
-
"memory_read",
|
|
101809
|
-
// Skills are reusable prompt playbooks, and loop.ts advertises the skills
|
|
101810
|
-
// catalogue to sub-agents at EVERY depth — so withholding the tool that loads
|
|
101811
|
-
// one meant showing every sub-agent a menu it could not order from.
|
|
101812
|
-
"use_skill",
|
|
101813
|
-
// VS Code language server (ignored on the CLI)
|
|
101814
|
-
"get_symbols",
|
|
101815
|
-
"get_workspace_symbols",
|
|
101816
|
-
"find_references",
|
|
101817
|
-
"go_to_definition",
|
|
101818
|
-
"get_hover",
|
|
101819
|
-
"get_diagnostics"
|
|
101820
|
-
];
|
|
101821
|
-
var RESEARCH_TOOLS = [...READ_ONLY_TOOLS, "fetch_url"];
|
|
101822
|
-
var WRITE_TOOLS = [
|
|
101823
|
-
...READ_ONLY_TOOLS,
|
|
101824
|
-
"write_file",
|
|
101825
|
-
"edit_file",
|
|
101826
|
-
"multi_edit",
|
|
101827
|
-
"create_directory",
|
|
101828
|
-
"move_file",
|
|
101829
|
-
"copy_file",
|
|
101830
|
-
// notebook_edit is a write tool like any other, and allowsTestOnlyWrite already
|
|
101831
|
-
// knows its shape (`source` is cell CONTENT, not a path). Omitting it just meant
|
|
101832
|
-
// test-writer silently could not touch notebooks.
|
|
101833
|
-
"notebook_edit",
|
|
101834
|
-
// Office document generation — same write-access tier as write_file, just a
|
|
101835
|
-
// structured content shape instead of raw text (see tools/executor.ts).
|
|
101836
|
-
"write_docx",
|
|
101837
|
-
"write_xlsx",
|
|
101838
|
-
"write_pptx"
|
|
101839
|
-
];
|
|
101840
|
-
var BUILTIN_AGENTS = [
|
|
101841
|
-
{
|
|
101842
|
-
// The catch-all, matching Claude Code's `general-purpose`.
|
|
101843
|
-
//
|
|
101844
|
-
// This capability already existed — omitting `subagent_type` gives an unrestricted
|
|
101845
|
-
// sub-agent — but it had no NAME, and that had two consequences worth fixing:
|
|
101846
|
-
//
|
|
101847
|
-
// 1. `permissions.deny: ["task(...)"]` matches on the agent name, so the ONE
|
|
101848
|
-
// sub-agent that can write files and run bash was the one variant a project
|
|
101849
|
-
// could not disable individually. Only a blanket `deny: ["task"]` reached it.
|
|
101850
|
-
// 2. The model had to infer that leaving the field blank was even an option, so
|
|
101851
|
-
// it would sometimes pick a specialist that fitted badly (an unrestricted
|
|
101852
|
-
// explorer) rather than the general worker it actually wanted.
|
|
101853
|
-
//
|
|
101854
|
-
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
101855
|
-
// inheriting whatever the session permits. That is the same power an unnamed
|
|
101856
|
-
// sub-task always had; naming it changes only who can see and deny it.
|
|
101857
|
-
//
|
|
101858
|
-
// It also makes this the ONLY builtin that can nest, since every specialist omits
|
|
101859
|
-
// `task` on purpose. So the tree this enables is up to `maxSubagentDepth` generations
|
|
101860
|
-
// of unrestricted agents — accepted deliberately, because the alternative (a catch-all
|
|
101861
|
-
// that cannot delegate) removes the one agent suited to orchestration.
|
|
101862
|
-
//
|
|
101863
|
-
// The safety properties that bound it: plan mode is inherited, the user's permission
|
|
101864
|
-
// gate runs on every call, a child's allowlist is INTERSECTED with its parent's so a
|
|
101865
|
-
// restricted ancestor cannot be escaped through this agent, `test_files_only` is
|
|
101866
|
-
// likewise sticky, and both the depth ceiling and the session budget still apply.
|
|
101867
|
-
name: "general-purpose",
|
|
101868
|
-
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.",
|
|
101869
|
-
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.",
|
|
101870
|
-
source: "builtin"
|
|
101871
|
-
},
|
|
101872
|
-
{
|
|
101873
|
-
name: "reviewer",
|
|
101874
|
-
description: "Read-only code reviewer \u2014 finds correctness bugs, edge cases, and security issues in a diff or file set. Cannot modify files.",
|
|
101875
|
-
tools: READ_ONLY_TOOLS,
|
|
101876
|
-
source: "builtin",
|
|
101877
|
-
prompt: [
|
|
101878
|
-
"You are a meticulous senior code reviewer. You NEVER modify files \u2014 you only read, search, and report.",
|
|
101879
|
-
"",
|
|
101880
|
-
"Method:",
|
|
101881
|
-
"1. Read the full context around every change you are asked to review; never judge a hunk in isolation.",
|
|
101882
|
-
"2. Hunt specifically for: correctness bugs, unhandled edge cases (empty/null/unicode/concurrency/timezone),",
|
|
101883
|
-
" security issues (injection, path traversal, secrets in code, unsafe deserialization), breaking API",
|
|
101884
|
-
" changes (search for callers first), silent behaviour changes, and swallowed errors.",
|
|
101885
|
-
"3. Verify test coverage: are the changed paths tested? Were assertions weakened or tests deleted?",
|
|
101886
|
-
"4. Only use bash for read-only commands (git diff/log/show, grep, test runs). Never run mutating commands.",
|
|
101887
|
-
"",
|
|
101888
|
-
"Report format: \u{1F534} Critical / \u{1F7E1} Warning / \u{1F7E2} Suggestion, each with file:line and a concrete fix,",
|
|
101889
|
-
"then a final verdict (APPROVE or REQUEST CHANGES) with a one-paragraph rationale."
|
|
101890
|
-
].join("\n")
|
|
101891
|
-
},
|
|
101892
|
-
// Promoted from the security-audit plugin to a builtin.
|
|
101893
|
-
//
|
|
101894
|
-
// Leaving it plugin-only was indefensible next to `reviewer` being builtin:
|
|
101895
|
-
// reviewer's own prompt already tells it to look for security issues, so
|
|
101896
|
-
// security IS treated as default work — yet the specialist agent for it was
|
|
101897
|
-
// invisible unless the user happened to know the plugin existed. For an agent
|
|
101898
|
-
// that WRITES code, "you only get a security review if you knew to install
|
|
101899
|
-
// something" is the wrong default.
|
|
101900
|
-
{
|
|
101901
|
-
name: "security-auditor",
|
|
101902
|
-
description: "Read-only security auditor \u2014 hunts injection, authz, secrets, and validation flaws in a path or diff. Cannot modify files.",
|
|
101903
|
-
tools: READ_ONLY_TOOLS,
|
|
101904
|
-
// No `model` override — inherits the session's, same as general-purpose.
|
|
101905
|
-
// A previous version forced 'pro' (Claude Opus 5) here, which silently
|
|
101906
|
-
// billed Anthropic even for a session running entirely on OpenAI/DeepSeek/
|
|
101907
|
-
// Qwen. Consistency with the main conversation's provider matters more
|
|
101908
|
-
// than defaulting every specialist to a fixed tier.
|
|
101909
|
-
source: "builtin",
|
|
101910
|
-
prompt: [
|
|
101911
|
-
"You are a security auditor. You find real, exploitable flaws \u2014 not style issues.",
|
|
101912
|
-
"",
|
|
101913
|
-
"Method:",
|
|
101914
|
-
"1. Map the attack surface FIRST: entry points (HTTP routes, message handlers, CLI args, file/network",
|
|
101915
|
-
" input, deserialization), then trace user-controlled data inward to where it is used.",
|
|
101916
|
-
"2. For each finding: file:line, the flaw class, a one-line exploit scenario, and the concrete fix.",
|
|
101917
|
-
"3. Grade severity honestly: Critical = remote compromise or data breach; High = auth bypass/IDOR;",
|
|
101918
|
-
" Medium = needs unusual preconditions; Low = hardening.",
|
|
101919
|
-
"",
|
|
101920
|
-
"Classes worth the most attention, in order: injection (SQL/command/template/prototype), broken",
|
|
101921
|
-
"authz (missing ownership checks, IDOR, trusting client-supplied ids), secrets committed to source,",
|
|
101922
|
-
"path traversal, SSRF, unsafe deserialization, missing rate limits on expensive or auth endpoints,",
|
|
101923
|
-
"and crypto misuse (hand-rolled comparison, predictable randomness).",
|
|
101924
|
-
"",
|
|
101925
|
-
"Hard rules:",
|
|
101926
|
-
"- READ-ONLY: never modify, create or delete files. bash only for read-only inspection.",
|
|
101927
|
-
"- NEVER print a discovered secret's value. Report its location and advise rotation.",
|
|
101928
|
-
"- Distinguish EXPLOITABLE from theoretical, and say which one each finding is.",
|
|
101929
|
-
'- "No issues found in scope X" is a valid, useful result. Do not pad the report to look thorough.'
|
|
101930
|
-
].join("\n")
|
|
101931
|
-
},
|
|
101932
|
-
// The gap Claude Code fills with its built-in `Explore`: read-heavy codebase
|
|
101933
|
-
// search that would otherwise flood the parent's context. Defaults to the
|
|
101934
|
-
// cheapest model on purpose — "find every caller of X" has no need of a
|
|
101935
|
-
// frontier model, and this is the agent most likely to be spawned in bulk.
|
|
101936
|
-
{
|
|
101937
|
-
name: "explorer",
|
|
101938
|
-
// Lean prompt: this agent exists to keep bulk searching cheap, and it reports
|
|
101939
|
-
// findings for the MAIN agent to interpret with full project context.
|
|
101940
|
-
lightPrompt: true,
|
|
101941
|
-
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.",
|
|
101942
|
-
tools: READ_ONLY_TOOLS,
|
|
101943
|
-
// No `model` override — inherits the session's. A previous version forced
|
|
101944
|
-
// 'turbo' (Claude Sonnet 5) on the theory that bulk search doesn't need a
|
|
101945
|
-
// frontier model, but that silently billed Anthropic regardless of which
|
|
101946
|
-
// provider the user actually selected for the conversation.
|
|
101947
|
-
source: "builtin",
|
|
101948
|
-
prompt: [
|
|
101949
|
-
"You map code. You NEVER modify anything.",
|
|
101950
|
-
"",
|
|
101951
|
-
"Method:",
|
|
101952
|
-
"1. Prefer structural search over text search where available (get_workspace_symbols, find_references,",
|
|
101953
|
-
" go_to_definition); fall back to search_files/glob otherwise.",
|
|
101954
|
-
"2. Read only the sections you need \u2014 use read_file with offset/limit on large files instead of",
|
|
101955
|
-
" pulling in thousands of lines.",
|
|
101956
|
-
"3. Follow the real call graph rather than guessing from names.",
|
|
101957
|
-
"",
|
|
101958
|
-
"Your ONLY output is a compact report: the file:line locations that matter, how they relate, and the",
|
|
101959
|
-
"direct answer to the question you were given. This exists to keep bulk search OUT of the parent's",
|
|
101960
|
-
"context, so do not paste large file contents back \u2014 cite locations and summarise. Say plainly when",
|
|
101961
|
-
'something does not exist; a confident wrong answer is far worse than "not found".'
|
|
101962
|
-
].join("\n")
|
|
101963
|
-
},
|
|
101964
|
-
// Matches Claude Code's built-in `Plan`: research a change and return a
|
|
101965
|
-
// strategy, deliberately WITHOUT write access so "make a plan" can never
|
|
101966
|
-
// quietly become "start editing".
|
|
101967
|
-
{
|
|
101968
|
-
name: "planner",
|
|
101969
|
-
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.",
|
|
101970
|
-
tools: RESEARCH_TOOLS,
|
|
101971
|
-
// No `model` override — inherits the session's, for the same reason as
|
|
101972
|
-
// explorer/security-auditor above: a fixed alias silently billed Anthropic
|
|
101973
|
-
// regardless of the user's chosen provider.
|
|
101974
|
-
source: "builtin",
|
|
101975
|
-
prompt: [
|
|
101976
|
-
"You produce implementation plans. You NEVER modify files \u2014 planning and doing are separate steps,",
|
|
101977
|
-
'and this agent exists so "plan it" cannot silently turn into "change it".',
|
|
101978
|
-
"",
|
|
101979
|
-
"Method:",
|
|
101980
|
-
"1. Read the actual code before proposing anything. No plan may rest on an assumed API shape.",
|
|
101981
|
-
"2. Find every affected call site (find_references / search_files) and list them.",
|
|
101982
|
-
"3. Order the steps so the tree stays working after each one \u2014 types, then implementation, then",
|
|
101983
|
-
" tests, then exports/registration.",
|
|
101984
|
-
"",
|
|
101985
|
-
"Output:",
|
|
101986
|
-
"- Goal, in one sentence.",
|
|
101987
|
-
"- Numbered steps, each with the exact files touched and what changes in them.",
|
|
101988
|
-
"- Risks + the specific thing that could break, and how it would be detected.",
|
|
101989
|
-
"- How to verify (the exact test/build command for THIS project, taken from package.json/Makefile).",
|
|
101990
|
-
"- Anything genuinely ambiguous, stated as an open question rather than a silent assumption."
|
|
101991
|
-
].join("\n")
|
|
101992
|
-
},
|
|
101993
|
-
// Promoted from the test-gen plugin. Needs write access — it produces test
|
|
101994
|
-
// files — but is deliberately forbidden from touching source, because "make the
|
|
101995
|
-
// tests pass" is the single most common way an agent destroys signal.
|
|
101996
|
-
{
|
|
101997
|
-
name: "test-writer",
|
|
101998
|
-
description: "Writes tests that follow the project's existing conventions. May create/edit TEST files only \u2014 never production source.",
|
|
101999
|
-
tools: WRITE_TOOLS,
|
|
102000
|
-
// Enforced, not merely requested: the permission gate refuses a write whose
|
|
102001
|
-
// path is not a test file. Without this the allowlist would grant edit_file
|
|
102002
|
-
// for every path and the rule below would be a suggestion the model is free
|
|
102003
|
-
// to rationalise its way past.
|
|
102004
|
-
testFilesOnly: true,
|
|
102005
|
-
source: "builtin",
|
|
102006
|
-
prompt: [
|
|
102007
|
-
"You write tests. You may create and edit TEST files only.",
|
|
102008
|
-
"",
|
|
102009
|
-
"Hard rules \u2014 these are the ways test-writing agents destroy value, so they are non-negotiable:",
|
|
102010
|
-
"- NEVER modify production source to make a test pass. If the code looks wrong, REPORT it and stop.",
|
|
102011
|
-
"- NEVER weaken, delete or skip an existing assertion or test.",
|
|
102012
|
-
"- A test that cannot fail is worse than no test. Every test must be able to fail for one clear reason.",
|
|
102013
|
-
"",
|
|
102014
|
-
"Method:",
|
|
102015
|
-
"1. Read the existing tests FIRST and copy their conventions exactly \u2014 runner, file naming, layout,",
|
|
102016
|
-
" assertion style, fixture/helper patterns. Never introduce a new framework.",
|
|
102017
|
-
"2. Test observable behaviour and the contract, not private internals.",
|
|
102018
|
-
"3. Cover the boring-but-real cases: empty input, null/undefined, unicode and non-BMP characters,",
|
|
102019
|
-
" boundaries, error paths, concurrency where it applies.",
|
|
102020
|
-
"4. No sleeps or wall-clock dependence \u2014 those produce the flaky tests that get deleted later.",
|
|
102021
|
-
"5. RUN the tests you wrote and report the real output. Never claim a test passes without running it."
|
|
102022
|
-
].join("\n")
|
|
102023
|
-
},
|
|
102024
|
-
// The DevOps gap — answered with a READ-ONLY advisor, not an operator.
|
|
102025
|
-
//
|
|
102026
|
-
// A "DevOps agent" with write/apply access is a genuinely different risk class
|
|
102027
|
-
// from the others here: its mistakes are `kubectl delete`, a bad `terraform
|
|
102028
|
-
// apply`, a broken deploy pipeline — often not revertible and affecting
|
|
102029
|
-
// production rather than a working tree. So this one diagnoses and proposes a
|
|
102030
|
-
// diff; a human applies it. That asymmetry is the whole design.
|
|
102031
|
-
{
|
|
102032
|
-
name: "devops-advisor",
|
|
102033
|
-
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.",
|
|
102034
|
-
tools: RESEARCH_TOOLS,
|
|
102035
|
-
// No `model` override — inherits the session's, for the same reason as the
|
|
102036
|
-
// other specialists above.
|
|
102037
|
-
source: "builtin",
|
|
102038
|
-
prompt: [
|
|
102039
|
-
"You are an infrastructure and delivery advisor. You DIAGNOSE and PROPOSE. You never apply changes.",
|
|
102040
|
-
"",
|
|
102041
|
-
"Hard rules:",
|
|
102042
|
-
"- READ-ONLY, and stricter than the other read-only agents: bash is for INSPECTION only",
|
|
102043
|
-
" (git log/diff, cat, grep, `kubectl get/describe`, `docker images`, `terraform plan`).",
|
|
102044
|
-
" NEVER run anything that mutates infrastructure \u2014 no apply/delete/scale/rollout/restart/push,",
|
|
102045
|
-
" no `terraform apply`, no `helm upgrade`. If a fix needs such a command, WRITE IT OUT for a human.",
|
|
102046
|
-
"- Never print secret values from env files, k8s Secrets or CI variables. Reference them by name.",
|
|
102047
|
-
"",
|
|
102048
|
-
"Method:",
|
|
102049
|
-
"1. Read what actually exists \u2014 workflow files, Dockerfiles, manifests, kustomize overlays, the",
|
|
102050
|
-
' deploy scripts \u2014 before drawing any conclusion. Never reason from what a stack "usually" looks like.',
|
|
102051
|
-
"2. Follow the real path a change takes to production, and name the step that is broken or missing.",
|
|
102052
|
-
"3. Check the failure modes that bite hardest: CI path filters that skip files a workload actually",
|
|
102053
|
-
" needs, image tags that do not match what is deployed, missing health probes, absent resource",
|
|
102054
|
-
" limits, secrets baked into images, ports/timeouts inconsistent between proxy and app, and",
|
|
102055
|
-
" migrations that must run before the new image is live.",
|
|
102056
|
-
"",
|
|
102057
|
-
"Output: the diagnosis, the evidence (file:line or command output), the proposed change as a diff or",
|
|
102058
|
-
"exact file content, and the command a human should run to apply and verify it."
|
|
102059
|
-
].join("\n")
|
|
102060
|
-
}
|
|
102061
|
-
];
|
|
102062
|
-
var KNOWN_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
102063
|
-
"read_file",
|
|
102064
|
-
"write_file",
|
|
102065
|
-
"write_docx",
|
|
102066
|
-
"write_xlsx",
|
|
102067
|
-
"write_pptx",
|
|
102068
|
-
"edit_file",
|
|
102069
|
-
"multi_edit",
|
|
102070
|
-
"list_directory",
|
|
102071
|
-
"create_directory",
|
|
102072
|
-
"move_file",
|
|
102073
|
-
"copy_file",
|
|
102074
|
-
"delete_file",
|
|
102075
|
-
"search_files",
|
|
102076
|
-
"glob",
|
|
102077
|
-
"bash",
|
|
102078
|
-
"bash_output",
|
|
102079
|
-
"kill_shell",
|
|
102080
|
-
"notebook_read",
|
|
102081
|
-
"notebook_edit",
|
|
102082
|
-
"todo_write",
|
|
102083
|
-
"todo_read",
|
|
102084
|
-
"memory_write",
|
|
102085
|
-
"memory_read",
|
|
102086
|
-
"use_skill",
|
|
102087
|
-
"fetch_url",
|
|
102088
|
-
"web_search",
|
|
102089
|
-
"generate_image",
|
|
102090
|
-
"stock_photo",
|
|
102091
|
-
"open_in_browser",
|
|
102092
|
-
"browser_action",
|
|
102093
|
-
"task",
|
|
102094
|
-
// Granted by a `memory:` scope rather than listed in a `tools:` line, but a user may
|
|
102095
|
-
// still name it explicitly — so it must validate rather than be flagged as a typo.
|
|
102096
|
-
"agent_memory_write",
|
|
102097
|
-
"get_symbols",
|
|
102098
|
-
"get_workspace_symbols",
|
|
102099
|
-
"find_references",
|
|
102100
|
-
"go_to_definition",
|
|
102101
|
-
"get_hover",
|
|
102102
|
-
"get_diagnostics"
|
|
102103
|
-
]);
|
|
102104
|
-
var KNOWN_META_KEYS = /* @__PURE__ */ new Set([
|
|
102105
|
-
"name",
|
|
102106
|
-
"description",
|
|
102107
|
-
"tools",
|
|
102108
|
-
"model",
|
|
102109
|
-
"test_files_only",
|
|
102110
|
-
"testfilesonly",
|
|
102111
|
-
"memory"
|
|
102112
|
-
]);
|
|
102113
|
-
var VALID_MODELS = [
|
|
102114
|
-
"claude-sonnet-5",
|
|
102115
|
-
"claude-opus-5",
|
|
102116
|
-
"claude-fable-5",
|
|
102117
|
-
"gpt-5.4",
|
|
102118
|
-
"gpt-5.4-mini",
|
|
102119
|
-
"gpt-4.1",
|
|
102120
|
-
// Legacy tier aliases — still accepted, still resolved by the backend.
|
|
102121
|
-
"turbo",
|
|
102122
|
-
"pro",
|
|
102123
|
-
"ultra",
|
|
102124
|
-
"fast"
|
|
102125
|
-
];
|
|
102126
|
-
function parseModel(v) {
|
|
102127
|
-
const raw = (v ?? "").trim();
|
|
102128
|
-
if (!raw)
|
|
102129
|
-
return void 0;
|
|
102130
|
-
const lower2 = raw.toLowerCase();
|
|
102131
|
-
if (lower2 === "turbo" || lower2 === "pro" || lower2 === "ultra" || lower2 === "fast")
|
|
102132
|
-
return lower2;
|
|
102133
|
-
return raw;
|
|
102134
|
-
}
|
|
102135
|
-
function parseBool(v) {
|
|
102136
|
-
return /^(true|yes|1|on)$/i.test((v ?? "").trim());
|
|
102137
|
-
}
|
|
102138
|
-
function parseToolList(v) {
|
|
102139
|
-
if (!v)
|
|
102140
|
-
return void 0;
|
|
102141
|
-
const tools = v.replace(/^\[|\]$/g, "").split(/[,\s]+/).map((t2) => t2.trim()).filter(Boolean);
|
|
102142
|
-
return tools.length ? tools : void 0;
|
|
102143
|
-
}
|
|
102144
|
-
function loadDir(dir, source2, into, warnings) {
|
|
102145
|
-
let entries;
|
|
102146
|
-
try {
|
|
102147
|
-
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
102148
|
-
} catch {
|
|
102149
|
-
return;
|
|
102150
|
-
}
|
|
102151
|
-
for (const entry of entries) {
|
|
102152
|
-
const full = path7.join(dir, entry.name);
|
|
102153
|
-
if (entry.isDirectory()) {
|
|
102154
|
-
loadDir(full, source2, into, warnings);
|
|
102155
|
-
continue;
|
|
102156
|
-
}
|
|
102157
|
-
if (!entry.name.endsWith(".md"))
|
|
102158
|
-
continue;
|
|
102159
|
-
let raw;
|
|
102160
|
-
try {
|
|
102161
|
-
raw = fs9.readFileSync(full, "utf-8");
|
|
102162
|
-
} catch (err) {
|
|
102163
|
-
warnings.push({ file: full, agent: path7.basename(entry.name, ".md"), message: `could not be read (${err.message}) \u2014 this agent was skipped` });
|
|
102164
|
-
continue;
|
|
102165
|
-
}
|
|
102166
|
-
const { meta, body, ok } = (0, frontmatter_1.parseFrontmatter)(raw);
|
|
102167
|
-
const name = (meta.name || path7.basename(entry.name, ".md")).trim();
|
|
102168
|
-
if (!name)
|
|
102169
|
-
continue;
|
|
102170
|
-
if (source2 !== "project" && into.has(name))
|
|
102171
|
-
continue;
|
|
102172
|
-
const tools = parseToolList(meta.tools);
|
|
102173
|
-
let effectiveTools = tools;
|
|
102174
|
-
if (!ok) {
|
|
102175
|
-
effectiveTools = READ_ONLY_TOOLS;
|
|
102176
|
-
warnings.push({
|
|
102177
|
-
file: full,
|
|
102178
|
-
agent: name,
|
|
102179
|
-
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."
|
|
102180
|
-
});
|
|
102181
|
-
} else {
|
|
102182
|
-
if (!meta.description) {
|
|
102183
|
-
warnings.push({
|
|
102184
|
-
file: full,
|
|
102185
|
-
agent: name,
|
|
102186
|
-
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."
|
|
102187
|
-
});
|
|
102188
|
-
}
|
|
102189
|
-
if (meta.memory !== void 0 && parseMemoryScope(meta.memory) === void 0) {
|
|
102190
|
-
warnings.push({
|
|
102191
|
-
file: full,
|
|
102192
|
-
agent: name,
|
|
102193
|
-
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.`
|
|
102194
|
-
});
|
|
102195
|
-
}
|
|
102196
|
-
const declaredModel = meta.model === void 0 ? void 0 : parseModel(meta.model);
|
|
102197
|
-
if (meta.model !== void 0 && (declaredModel === void 0 || !VALID_MODELS.includes(declaredModel))) {
|
|
102198
|
-
warnings.push({
|
|
102199
|
-
file: full,
|
|
102200
|
-
agent: name,
|
|
102201
|
-
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.`
|
|
102202
|
-
});
|
|
102203
|
-
}
|
|
102204
|
-
const unknown = (tools ?? []).filter((t2) => !KNOWN_TOOL_NAMES.has(t2) && !t2.includes("__"));
|
|
102205
|
-
if (unknown.length) {
|
|
102206
|
-
warnings.push({
|
|
102207
|
-
file: full,
|
|
102208
|
-
agent: name,
|
|
102209
|
-
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).`
|
|
102210
|
-
});
|
|
102211
|
-
}
|
|
102212
|
-
if ((tools ?? []).length === 1 && tools?.[0] === "task") {
|
|
102213
|
-
warnings.push({
|
|
102214
|
-
file: full,
|
|
102215
|
-
agent: name,
|
|
102216
|
-
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."
|
|
102217
|
-
});
|
|
102218
|
-
}
|
|
102219
|
-
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
|
102220
|
-
if (strayKeys.length) {
|
|
102221
|
-
warnings.push({
|
|
102222
|
-
file: full,
|
|
102223
|
-
agent: name,
|
|
102224
|
-
message: `has unrecognised frontmatter key(s): ${strayKeys.join(", ")} \u2014 these are ignored.`
|
|
102225
|
-
});
|
|
102226
|
-
}
|
|
102227
|
-
if (!body) {
|
|
102228
|
-
warnings.push({
|
|
102229
|
-
file: full,
|
|
102230
|
-
agent: name,
|
|
102231
|
-
message: "has an empty body \u2014 the text below the frontmatter IS the agent's system prompt, so it currently has no instructions."
|
|
102232
|
-
});
|
|
102233
|
-
}
|
|
102234
|
-
}
|
|
102235
|
-
into.set(name, {
|
|
102236
|
-
name,
|
|
102237
|
-
description: meta.description || `Custom ${name} agent`,
|
|
102238
|
-
tools: effectiveTools,
|
|
102239
|
-
model: parseModel(meta.model),
|
|
102240
|
-
prompt: body,
|
|
102241
|
-
source: source2,
|
|
102242
|
-
// Exposed to user/plugin definitions too — `test_files_only: true` (or
|
|
102243
|
-
// `testFilesOnly`) lets anyone build a test-writing agent that genuinely
|
|
102244
|
-
// cannot touch production source, rather than only the builtin getting
|
|
102245
|
-
// that guarantee.
|
|
102246
|
-
...parseBool(meta.test_files_only ?? meta.testfilesonly) ? { testFilesOnly: true } : {},
|
|
102247
|
-
// The `ok &&` is defensive, not load-bearing: parseFrontmatter already returns an
|
|
102248
|
-
// EMPTY meta when it cannot find a `---` block, so `meta.memory` is undefined on
|
|
102249
|
-
// that path regardless. It stays because the guarantee we want — an unreadable
|
|
102250
|
-
// definition never receives a writable store — should survive parseFrontmatter
|
|
102251
|
-
// being changed to salvage partial metadata, which is a plausible future edit.
|
|
102252
|
-
...ok && parseMemoryScope(meta.memory) ? { memory: parseMemoryScope(meta.memory) } : {}
|
|
102253
|
-
});
|
|
102254
|
-
}
|
|
102255
|
-
}
|
|
102256
|
-
function loadAgentTypes(workDir) {
|
|
102257
|
-
return loadAgentTypesWithWarnings2(workDir).types;
|
|
102258
|
-
}
|
|
102259
|
-
function loadAgentTypesWithWarnings2(workDir) {
|
|
102260
|
-
const out = /* @__PURE__ */ new Map();
|
|
102261
|
-
const warnings = [];
|
|
102262
|
-
loadDir(path7.join(workDir, ".nexrall", "agents"), "project", out, warnings);
|
|
102263
|
-
loadDir(path7.join(os6.homedir(), ".nexrall", "agents"), "global", out, warnings);
|
|
102264
|
-
for (const dir of (0, index_1.pluginAssetDirs)(workDir, "agents"))
|
|
102265
|
-
loadDir(dir, "plugin", out, warnings);
|
|
102266
|
-
for (const agent of BUILTIN_AGENTS) {
|
|
102267
|
-
if (!out.has(agent.name))
|
|
102268
|
-
out.set(agent.name, agent);
|
|
102269
|
-
}
|
|
102270
|
-
const builtinOrder = new Map(BUILTIN_AGENTS.map((a, i2) => [a.name, i2]));
|
|
102271
|
-
const types3 = [...out.values()].sort((a, b) => {
|
|
102272
|
-
const ai = builtinOrder.get(a.name);
|
|
102273
|
-
const bi = builtinOrder.get(b.name);
|
|
102274
|
-
if (ai !== void 0 && bi !== void 0)
|
|
102275
|
-
return ai - bi;
|
|
102276
|
-
if (ai !== void 0)
|
|
102277
|
-
return -1;
|
|
102278
|
-
if (bi !== void 0)
|
|
102279
|
-
return 1;
|
|
102280
|
-
return a.name.localeCompare(b.name);
|
|
102281
|
-
});
|
|
102282
|
-
return { types: types3, warnings };
|
|
102283
|
-
}
|
|
102284
|
-
function knownToolNames() {
|
|
102285
|
-
return [...KNOWN_TOOL_NAMES].sort();
|
|
102286
|
-
}
|
|
102287
|
-
function builtinAgents() {
|
|
102288
|
-
return BUILTIN_AGENTS;
|
|
102289
|
-
}
|
|
102290
|
-
function summariseAgents(types3) {
|
|
102291
|
-
if (!types3.length)
|
|
102292
|
-
return "";
|
|
102293
|
-
return types3.map((t2) => {
|
|
102294
|
-
const canWrite = !t2.tools || t2.tools.some((x2) => WRITE_TOOL_HINTS.has(x2));
|
|
102295
|
-
const access = t2.testFilesOnly ? "writes TEST files only" : canWrite ? "can modify files" : "read-only";
|
|
102296
|
-
const model = t2.model ? `, ${t2.model} model` : "";
|
|
102297
|
-
return `- ${t2.name} (${access}${model}): ${t2.description}`;
|
|
102298
|
-
}).join("\n");
|
|
102299
|
-
}
|
|
102300
|
-
var WRITE_TOOL_HINTS = /* @__PURE__ */ new Set([
|
|
102301
|
-
"write_file",
|
|
102302
|
-
"edit_file",
|
|
102303
|
-
"multi_edit",
|
|
102304
|
-
"notebook_edit",
|
|
102305
|
-
"delete_file",
|
|
102306
|
-
"move_file",
|
|
102307
|
-
"copy_file",
|
|
102308
|
-
"write_docx",
|
|
102309
|
-
"write_xlsx",
|
|
102310
|
-
"write_pptx"
|
|
102311
|
-
]);
|
|
102312
|
-
function findAgentType(types3, name) {
|
|
102313
|
-
if (!name)
|
|
102314
|
-
return void 0;
|
|
102315
|
-
const want = name.trim().toLowerCase();
|
|
102316
|
-
return types3.find((t2) => t2.name.toLowerCase() === want);
|
|
102317
|
-
}
|
|
102318
|
-
}
|
|
102319
|
-
});
|
|
102320
|
-
|
|
102321
102334
|
// ../core/dist/permissions/rules.js
|
|
102322
102335
|
var require_rules = __commonJS({
|
|
102323
102336
|
"../core/dist/permissions/rules.js"(exports2) {
|