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.
Files changed (2) hide show
  1. package/dist/index.js +784 -646
  2. 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
- for (const line of m2[1].split(/\r?\n/)) {
11663
- const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line.trim());
11664
- if (kv)
11665
- meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
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
- return { meta, body: (m2[2] ?? "").trim() };
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 = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
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 toSkill(meta, body, name, source2, dir) {
11924
- const model = ["turbo", "pro", "ultra"].find((x2) => x2 === (meta.model ?? "").toLowerCase());
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 loadFlatCommandDir(dir, source2, into) {
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(path7.join(dir, file), "utf-8");
12685
+ const raw = fs9.readFileSync(full, "utf-8");
11947
12686
  const { meta, body } = (0, frontmatter_1.parseFrontmatter)(raw);
11948
- const name = (meta.name || path7.basename(file, ".md")).trim().toLowerCase();
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 name = (meta.name || entry.name).trim().toLowerCase();
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
- into.set(name, toSkill(meta, body, name, source2, skillDir));
11985
- } catch {
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 loadSkills2(workDir) {
12770
+ function loadSkillsWithWarnings(workDir) {
12026
12771
  const out = /* @__PURE__ */ new Map();
12027
- loadFlatCommandDir(path7.join(workDir, ".nexrall", "commands"), "project", out);
12028
- loadSkillDir(path7.join(workDir, ".nexrall", "skills"), "project", out);
12029
- loadFlatCommandDir(path7.join(os6.homedir(), ".nexrall", "commands"), "global", out);
12030
- loadSkillDir(path7.join(os6.homedir(), ".nexrall", "skills"), "global", out);
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