rulesync 16.34.2 → 16.36.0
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/README.md +4 -2
- package/dist/cli/index.cjs +19 -4
- package/dist/cli/index.js +19 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-D50FT0lm.cjs → import-D_daTMgR.cjs} +1182 -213
- package/dist/{import-DoCUTXaU.js → import-D_iukHSy.js} +1172 -215
- package/dist/import-D_iukHSy.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-DoCUTXaU.js.map +0 -1
|
@@ -540,6 +540,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
540
540
|
"musecode",
|
|
541
541
|
"opencode",
|
|
542
542
|
"pi",
|
|
543
|
+
"pool",
|
|
543
544
|
"qwencode",
|
|
544
545
|
"reasonix",
|
|
545
546
|
"replit",
|
|
@@ -553,7 +554,6 @@ const rulesProcessorToolTargetTuple = [
|
|
|
553
554
|
"zcode",
|
|
554
555
|
"zed",
|
|
555
556
|
"zoocode",
|
|
556
|
-
"pool",
|
|
557
557
|
"dsh"
|
|
558
558
|
];
|
|
559
559
|
const ignoreProcessorToolTargetTuple = [
|
|
@@ -4078,24 +4078,60 @@ const CONTROL_CHARS = [
|
|
|
4078
4078
|
const hasControlChars = (val) => CONTROL_CHARS.some((char) => val.includes(char));
|
|
4079
4079
|
const safeString = zod_mini.z.pipe(zod_mini.z.string(), zod_mini.z.custom((val) => typeof val === "string" && !hasControlChars(val), "must not contain newline, carriage return, or NUL characters"));
|
|
4080
4080
|
/**
|
|
4081
|
+
* All canonical hook types — the union of the `type` values accepted by every
|
|
4082
|
+
* supported tool:
|
|
4083
|
+
* - `command` — universal (Claude Code, Codex CLI, Qwen Code, Cursor, …)
|
|
4084
|
+
* - `prompt` — Claude Code, Codex CLI, Qwen Code, Copilot CLI, Cursor, Devin
|
|
4085
|
+
* - `http` — Claude Code, Qwen Code, Copilot CLI, Grok CLI
|
|
4086
|
+
* - `agent` — Claude Code, Codex CLI
|
|
4087
|
+
* - `mcp_tool` — Claude Code
|
|
4088
|
+
* - `function` — Qwen Code (internal Skill-system executor)
|
|
4089
|
+
*/
|
|
4090
|
+
const HOOK_TYPES = [
|
|
4091
|
+
"command",
|
|
4092
|
+
"prompt",
|
|
4093
|
+
"http",
|
|
4094
|
+
"agent",
|
|
4095
|
+
"mcp_tool",
|
|
4096
|
+
"function"
|
|
4097
|
+
];
|
|
4098
|
+
/**
|
|
4099
|
+
* One choice offered by a Kiro `confirm` prompt: `run` decides whether picking
|
|
4100
|
+
* it executes the hook command. The prompt text is written back into JSON,
|
|
4101
|
+
* never embedded in generated code, so it is a plain string.
|
|
4102
|
+
*/
|
|
4103
|
+
const HookConfirmOptionSchema = zod_mini.z.looseObject({
|
|
4104
|
+
id: zod_mini.z.string(),
|
|
4105
|
+
label: zod_mini.z.string(),
|
|
4106
|
+
run: zod_mini.z.boolean()
|
|
4107
|
+
});
|
|
4108
|
+
/**
|
|
4109
|
+
* Kiro's confirmation prompt shown before a `Stop`-trigger command hook runs.
|
|
4110
|
+
* `confirmCommand` (optional, inside the block) names a command whose JSON
|
|
4111
|
+
* stdout drives the prompt at run time: `{ "skip": true }` suppresses it, a
|
|
4112
|
+
* replacement `question` / `options` object overrides the static prompt, and
|
|
4113
|
+
* on failure or timeout the static prompt is used. Stored verbatim: Kiro is
|
|
4114
|
+
* the only consumer, and it owns the shape.
|
|
4115
|
+
* https://kiro.dev/docs/hooks/
|
|
4116
|
+
*/
|
|
4117
|
+
const HookConfirmSchema = zod_mini.z.looseObject({
|
|
4118
|
+
question: zod_mini.z.string(),
|
|
4119
|
+
options: zod_mini.z.array(HookConfirmOptionSchema),
|
|
4120
|
+
confirmCommand: zod_mini.z.optional(safeString)
|
|
4121
|
+
});
|
|
4122
|
+
/**
|
|
4081
4123
|
* Canonical hook definition.
|
|
4082
4124
|
* Used in .rulesync/hooks.jsonc and mapped to tool-specific formats.
|
|
4083
4125
|
*/
|
|
4084
4126
|
const HookDefinitionSchema = zod_mini.z.looseObject({
|
|
4085
4127
|
command: zod_mini.z.optional(safeString),
|
|
4086
|
-
type: zod_mini.z.optional(zod_mini.z.enum(
|
|
4087
|
-
"command",
|
|
4088
|
-
"prompt",
|
|
4089
|
-
"http",
|
|
4090
|
-
"agent",
|
|
4091
|
-
"mcp_tool",
|
|
4092
|
-
"function"
|
|
4093
|
-
])),
|
|
4128
|
+
type: zod_mini.z.optional(zod_mini.z.enum(HOOK_TYPES)),
|
|
4094
4129
|
url: zod_mini.z.optional(safeString),
|
|
4095
4130
|
timeout: zod_mini.z.optional(zod_mini.z.number()),
|
|
4096
4131
|
cacheTtl: zod_mini.z.optional(zod_mini.z.number().check((0, zod_mini.nonnegative)())),
|
|
4097
4132
|
matcher: zod_mini.z.optional(safeString),
|
|
4098
4133
|
enabled: zod_mini.z.optional(zod_mini.z.boolean()),
|
|
4134
|
+
confirm: zod_mini.z.optional(HookConfirmSchema),
|
|
4099
4135
|
prompt: zod_mini.z.optional(safeString),
|
|
4100
4136
|
loop_limit: zod_mini.z.optional(zod_mini.z.nullable(zod_mini.z.number())),
|
|
4101
4137
|
name: zod_mini.z.optional(safeString),
|
|
@@ -4116,6 +4152,10 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
|
|
|
4116
4152
|
model: zod_mini.z.optional(safeString),
|
|
4117
4153
|
args: zod_mini.z.optional(zod_mini.z.array(safeString)),
|
|
4118
4154
|
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
4155
|
+
source: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
4156
|
+
source: safeString,
|
|
4157
|
+
ref: zod_mini.z.optional(safeString)
|
|
4158
|
+
})),
|
|
4119
4159
|
if: zod_mini.z.optional(safeString),
|
|
4120
4160
|
commandWindows: zod_mini.z.optional(safeString),
|
|
4121
4161
|
additionalContextLimit: zod_mini.z.optional(zod_mini.z.int().check((0, zod_mini.nonnegative)())),
|
|
@@ -4369,9 +4409,13 @@ const AMP_HOOK_EVENTS = [
|
|
|
4369
4409
|
* the platform the other one owns — see `generateClineHookScript` and
|
|
4370
4410
|
* `generateClineHookPowerShellScript`.
|
|
4371
4411
|
*
|
|
4372
|
-
* `
|
|
4373
|
-
*
|
|
4374
|
-
*
|
|
4412
|
+
* `TaskCancel` is the shape of canonical `stopCancelled` (as Grok CLI's
|
|
4413
|
+
* `StopCancelled` and Codex CLI's `Interrupt` are): both runtimes fire it
|
|
4414
|
+
* *instead of* `TaskComplete` when the run ends aborted — the VS Code
|
|
4415
|
+
* extension from `afterRun` when the result status is `aborted`, the SDK/CLI
|
|
4416
|
+
* as `agent_abort` — and neither lets the script cancel anything. `TaskResume`
|
|
4417
|
+
* has no canonical counterpart and stays unmapped rather than being
|
|
4418
|
+
* approximated by `sessionStart`, whose semantics differ.
|
|
4375
4419
|
*
|
|
4376
4420
|
* @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
|
|
4377
4421
|
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
|
|
@@ -4385,7 +4429,8 @@ const CLINE_HOOK_EVENTS = [
|
|
|
4385
4429
|
"preCompact",
|
|
4386
4430
|
"notification",
|
|
4387
4431
|
"taskCompleted",
|
|
4388
|
-
"afterError"
|
|
4432
|
+
"afterError",
|
|
4433
|
+
"stopCancelled"
|
|
4389
4434
|
];
|
|
4390
4435
|
/**
|
|
4391
4436
|
* Hook events supported by GitHub Copilot (cloud coding agent).
|
|
@@ -4703,23 +4748,32 @@ const QWENCODE_HOOK_EVENTS = [
|
|
|
4703
4748
|
* Hook events supported by Reasonix.
|
|
4704
4749
|
*
|
|
4705
4750
|
* Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
|
|
4706
|
-
* (global)
|
|
4707
|
-
* `
|
|
4708
|
-
* `
|
|
4751
|
+
* (global) accepts the thirteen events declared in `internal/hook/hook.go`
|
|
4752
|
+
* (`var Events`): `PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
|
|
4753
|
+
* `PermissionRequest`, `UserPromptSubmit`, `Stop`, `StopFailure`,
|
|
4754
|
+
* `PostLLMCall`, `SessionStart`, `SessionEnd`, `SubagentStop`, `Notification`,
|
|
4755
|
+
* `PreCompact`. The zh-CN desktop hooks page documents only ten of them, so the
|
|
4756
|
+
* source file is the authoritative list. All thirteen have a clean canonical
|
|
4709
4757
|
* equivalent and are mapped: `PreToolUse`, `PostToolUse`,
|
|
4710
|
-
* `
|
|
4711
|
-
* `
|
|
4712
|
-
* `
|
|
4713
|
-
* `
|
|
4714
|
-
* `
|
|
4715
|
-
*
|
|
4758
|
+
* `PostToolUseFailure`, `PermissionRequest`, `UserPromptSubmit` ←
|
|
4759
|
+
* `beforeSubmitPrompt`, `Stop`, `StopFailure`, `SessionStart`, `SessionEnd`,
|
|
4760
|
+
* `SubagentStop`, `PostLLMCall` ← `postModelInvocation`, `Notification` ←
|
|
4761
|
+
* `notification`, and `PreCompact` ← `preCompact`.
|
|
4762
|
+
* `match` (Reasonix's matcher field name) is honored on the four tool-scoped
|
|
4763
|
+
* events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
|
|
4764
|
+
* `PermissionRequest` — `UsesToolMatcher` in `internal/hook/inspect.go`) and
|
|
4765
|
+
* ignored everywhere else.
|
|
4766
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
|
|
4716
4767
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
4717
4768
|
*/
|
|
4718
4769
|
const REASONIX_HOOK_EVENTS = [
|
|
4719
4770
|
"preToolUse",
|
|
4720
4771
|
"postToolUse",
|
|
4772
|
+
"postToolUseFailure",
|
|
4773
|
+
"permissionRequest",
|
|
4721
4774
|
"beforeSubmitPrompt",
|
|
4722
4775
|
"stop",
|
|
4776
|
+
"stopFailure",
|
|
4723
4777
|
"sessionStart",
|
|
4724
4778
|
"sessionEnd",
|
|
4725
4779
|
"subagentStop",
|
|
@@ -4765,8 +4819,8 @@ const GROKCLI_HOOK_EVENTS = [
|
|
|
4765
4819
|
* added in 0.32.0 (`TurnStarted`, `UserPromptQueued`, `TaskStarted`,
|
|
4766
4820
|
* `SessionHeartbeat`), none of which is mapped onto a canonical rulesync event
|
|
4767
4821
|
* here. `Interrupt` (fires instead of `Stop` when the user interrupts a turn)
|
|
4768
|
-
* does have a canonical shape — `stopCancelled`, which Grok CLI
|
|
4769
|
-
* map — but Kimi keeps it native-only for now, since an existing `kimi-code`
|
|
4822
|
+
* does have a canonical shape — `stopCancelled`, which Grok CLI, Codex CLI and
|
|
4823
|
+
* Cline map — but Kimi keeps it native-only for now, since an existing `kimi-code`
|
|
4770
4824
|
* override addressing it by name would otherwise double up with a canonical
|
|
4771
4825
|
* `stopCancelled` block; folding it in is a follow-up. All six are listed in
|
|
4772
4826
|
* `KIMI_CODE_NATIVE_HOOK_EVENTS` so a per-tool `kimi-code` override can
|
|
@@ -4876,10 +4930,12 @@ const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONIC
|
|
|
4876
4930
|
*
|
|
4877
4931
|
* Bob reads `hooks` from `.bob/settings.json` (project) and
|
|
4878
4932
|
* `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
|
|
4879
|
-
*
|
|
4880
|
-
*
|
|
4881
|
-
*
|
|
4933
|
+
* seven events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
4934
|
+
* PreCompact, PostCompact (both added by Bob Shell 2.0.3) and Stop. Handlers
|
|
4935
|
+
* are `command` or `https` hooks, and `matcher` (a regex over the tool name)
|
|
4936
|
+
* applies to the two tool events.
|
|
4882
4937
|
*
|
|
4938
|
+
* @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
|
|
4883
4939
|
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
4884
4940
|
*/
|
|
4885
4941
|
const BOB_HOOK_EVENTS = [
|
|
@@ -4887,6 +4943,8 @@ const BOB_HOOK_EVENTS = [
|
|
|
4887
4943
|
"beforeSubmitPrompt",
|
|
4888
4944
|
"preToolUse",
|
|
4889
4945
|
"postToolUse",
|
|
4946
|
+
"preCompact",
|
|
4947
|
+
"postCompact",
|
|
4890
4948
|
"stop"
|
|
4891
4949
|
];
|
|
4892
4950
|
const CANONICAL_TO_BOB_EVENT_NAMES = {
|
|
@@ -4894,6 +4952,8 @@ const CANONICAL_TO_BOB_EVENT_NAMES = {
|
|
|
4894
4952
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
4895
4953
|
preToolUse: "PreToolUse",
|
|
4896
4954
|
postToolUse: "PostToolUse",
|
|
4955
|
+
preCompact: "PreCompact",
|
|
4956
|
+
postCompact: "PostCompact",
|
|
4897
4957
|
stop: "Stop"
|
|
4898
4958
|
};
|
|
4899
4959
|
const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
@@ -5055,8 +5115,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
5055
5115
|
/**
|
|
5056
5116
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
5057
5117
|
*
|
|
5058
|
-
* Hermes validates hook events against a fixed `VALID_HOOKS` set —
|
|
5059
|
-
* of v0.
|
|
5118
|
+
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 39 entries as
|
|
5119
|
+
* of v0.21.3 (`v2026.9.14`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
|
|
5060
5120
|
* full list. Only the events with a clean 1:1 canonical equivalent are mapped
|
|
5061
5121
|
* here. All other native events round-trip through `hermesagent.hooks`.
|
|
5062
5122
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
@@ -5080,11 +5140,13 @@ const HERMESAGENT_HOOK_EVENTS = [
|
|
|
5080
5140
|
* observers, `gateway_platform_event` and `transform_api_error_classification`.
|
|
5081
5141
|
* Shell hooks are gated on the same set.
|
|
5082
5142
|
*
|
|
5083
|
-
* Verified against the tag `v2026.
|
|
5084
|
-
* entries, grown from 23 at `v2026.8.3` (v0.20.0)
|
|
5085
|
-
*
|
|
5143
|
+
* Verified against the tag `v2026.9.14` (v0.21.3): `VALID_HOOKS` holds 39
|
|
5144
|
+
* entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
|
|
5145
|
+
* (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`.
|
|
5146
|
+
* This list holds 38 of them — see
|
|
5147
|
+
* {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
|
|
5086
5148
|
*
|
|
5087
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5149
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py
|
|
5088
5150
|
*/
|
|
5089
5151
|
const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
5090
5152
|
"pre_tool_call",
|
|
@@ -5110,8 +5172,10 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5110
5172
|
"subagent_start",
|
|
5111
5173
|
"subagent_stop",
|
|
5112
5174
|
"pre_gateway_dispatch",
|
|
5175
|
+
"agent_loop_stopped",
|
|
5113
5176
|
"pre_approval_request",
|
|
5114
5177
|
"post_approval_response",
|
|
5178
|
+
"on_room_member_activity",
|
|
5115
5179
|
"pre_transcription",
|
|
5116
5180
|
"kanban_task_claimed",
|
|
5117
5181
|
"kanban_task_completed",
|
|
@@ -5134,8 +5198,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5134
5198
|
* rulesync only ever writes shell hooks, so these are excluded from
|
|
5135
5199
|
* {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
|
|
5136
5200
|
*
|
|
5137
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5138
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5201
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
|
|
5202
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/shell_hooks.py — `_parse_hooks_block`
|
|
5139
5203
|
*/
|
|
5140
5204
|
const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
|
|
5141
5205
|
/**
|
|
@@ -5436,7 +5500,8 @@ const CANONICAL_TO_CLINE_EVENT_NAMES = {
|
|
|
5436
5500
|
preCompact: "PreCompact",
|
|
5437
5501
|
notification: "Notification",
|
|
5438
5502
|
taskCompleted: "TaskComplete",
|
|
5439
|
-
afterError: "TaskError"
|
|
5503
|
+
afterError: "TaskError",
|
|
5504
|
+
stopCancelled: "TaskCancel"
|
|
5440
5505
|
};
|
|
5441
5506
|
/**
|
|
5442
5507
|
* Map canonical camelCase event names to Copilot camelCase.
|
|
@@ -5754,8 +5819,11 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
5754
5819
|
const CANONICAL_TO_REASONIX_EVENT_NAMES = {
|
|
5755
5820
|
preToolUse: "PreToolUse",
|
|
5756
5821
|
postToolUse: "PostToolUse",
|
|
5822
|
+
postToolUseFailure: "PostToolUseFailure",
|
|
5823
|
+
permissionRequest: "PermissionRequest",
|
|
5757
5824
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
5758
5825
|
stop: "Stop",
|
|
5826
|
+
stopFailure: "StopFailure",
|
|
5759
5827
|
sessionStart: "SessionStart",
|
|
5760
5828
|
sessionEnd: "SessionEnd",
|
|
5761
5829
|
subagentStop: "SubagentStop",
|
|
@@ -7305,13 +7373,30 @@ const DeepagentsExtensionsOverrideSchema = zod_mini.z.looseObject({
|
|
|
7305
7373
|
]))
|
|
7306
7374
|
});
|
|
7307
7375
|
/**
|
|
7376
|
+
* The `[mcp]` table of `~/.deepagents/config.toml`, where dcode keeps its
|
|
7377
|
+
* per-server trust for the MCP servers a project's `.mcp.json` /
|
|
7378
|
+
* `.deepagents/.mcp.json` declares. dcode reads these lists from the user-level
|
|
7379
|
+
* file only — never from a project file — which is exactly why the global
|
|
7380
|
+
* permissions adapter is the place to author them.
|
|
7381
|
+
*
|
|
7382
|
+
* `disabled_project_servers` names project MCP servers dcode always rejects;
|
|
7383
|
+
* a rejection wins over any approval or trust. The sibling
|
|
7384
|
+
* `enabled_project_server_approvals` is the fingerprint-bound approval store
|
|
7385
|
+
* the interactive prompt fills in, and `enabled_project_servers` is a
|
|
7386
|
+
* deprecated flat allowlist dcode ignores, so neither has a key here.
|
|
7387
|
+
*
|
|
7388
|
+
* @see https://docs.langchain.com/oss/deepagents/code/configuration
|
|
7389
|
+
*/
|
|
7390
|
+
const DeepagentsMcpOverrideSchema = zod_mini.z.looseObject({ disabled_project_servers: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())) });
|
|
7391
|
+
/**
|
|
7308
7392
|
* The `[shell].allow_list` array itself is rulesync-owned, driven by the
|
|
7309
7393
|
* shared `permission.bash` block, so it has no key here.
|
|
7310
7394
|
*/
|
|
7311
7395
|
const DeepagentsPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
7312
7396
|
permission: zod_mini.z.optional(ToolScopedPermissionSchema),
|
|
7313
7397
|
startup: zod_mini.z.optional(DeepagentsStartupOverrideSchema),
|
|
7314
|
-
extensions: zod_mini.z.optional(DeepagentsExtensionsOverrideSchema)
|
|
7398
|
+
extensions: zod_mini.z.optional(DeepagentsExtensionsOverrideSchema),
|
|
7399
|
+
mcp: zod_mini.z.optional(DeepagentsMcpOverrideSchema)
|
|
7315
7400
|
});
|
|
7316
7401
|
/**
|
|
7317
7402
|
* The actions Junie's allowlist accepts. Verified against the shipped Junie
|
|
@@ -7629,11 +7714,15 @@ const KiroPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
|
7629
7714
|
});
|
|
7630
7715
|
/**
|
|
7631
7716
|
* Codex CLI's approval-workflow policy. Serialized as a kebab-case string in
|
|
7632
|
-
* `.codex/config.toml`. `on-failure` is a
|
|
7633
|
-
* Codex still
|
|
7634
|
-
*
|
|
7635
|
-
*
|
|
7717
|
+
* `.codex/config.toml`. `on-failure` is a deprecated alias for `on-request`
|
|
7718
|
+
* that Codex still reads, so it stays accepted (generate warns) so existing
|
|
7719
|
+
* configs round-trip. `untrusted` was retired in Codex 0.149.0 and makes
|
|
7720
|
+
* Codex refuse to start; it stays in the enum only so an existing
|
|
7721
|
+
* `.rulesync/permissions.jsonc` still parses — generate warns and does not
|
|
7722
|
+
* write it, and import does not lift it. The granular table form
|
|
7723
|
+
* (`{ granular = { … } }`) is modeled separately in the override union.
|
|
7636
7724
|
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
7725
|
+
* @see https://github.com/openai/codex/pull/39630
|
|
7637
7726
|
*/
|
|
7638
7727
|
const CodexApprovalPolicySchema = zod_mini.z.enum([
|
|
7639
7728
|
"untrusted",
|
|
@@ -7703,11 +7792,12 @@ const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_P
|
|
|
7703
7792
|
* selected directly via `default_permissions` and skips the managed profile
|
|
7704
7793
|
* entirely — canonical filesystem/network rules are ignored in that mode.
|
|
7705
7794
|
* Defaults to `:workspace` when unspecified.
|
|
7706
|
-
* - `approval_policy` — `
|
|
7795
|
+
* - `approval_policy` — `on-request` (deprecated alias `on-failure`) |
|
|
7707
7796
|
* `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
|
|
7708
7797
|
* schema has required fields that are brittle to model as typed keys).
|
|
7709
7798
|
* Defaults to `on-request` when neither the override nor the existing
|
|
7710
|
-
* config sets it.
|
|
7799
|
+
* config sets it. `untrusted` (retired in Codex 0.149.0) is still parsed
|
|
7800
|
+
* but skipped with a warning on generate, since Codex refuses to start on it.
|
|
7711
7801
|
* - `sandbox_mode` — **deprecated.** `read-only` | `workspace-write` |
|
|
7712
7802
|
* `danger-full-access`, with the sibling `sandbox_workspace_write` table
|
|
7713
7803
|
* (`network_access`, `writable_roots`, …). Codex has superseded the classic
|
|
@@ -9593,7 +9683,8 @@ const RulesyncSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
9593
9683
|
})),
|
|
9594
9684
|
grokcli: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9595
9685
|
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9596
|
-
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
9686
|
+
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9687
|
+
paths: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
|
|
9597
9688
|
})),
|
|
9598
9689
|
dsh: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9599
9690
|
whenToUse: zod_mini.z.optional(zod_mini.z.string()),
|
|
@@ -9625,7 +9716,13 @@ const RulesyncSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
9625
9716
|
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9626
9717
|
"allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
|
|
9627
9718
|
})),
|
|
9628
|
-
commandcode: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9719
|
+
commandcode: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
9720
|
+
license: zod_mini.z.optional(zod_mini.z.string()),
|
|
9721
|
+
compatibility: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})])),
|
|
9722
|
+
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
9723
|
+
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
9724
|
+
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
9725
|
+
})),
|
|
9629
9726
|
cortexcode: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
9630
9727
|
continue: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
9631
9728
|
tabnine: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
@@ -9814,8 +9911,32 @@ var RulesyncSubagent = class RulesyncSubagent extends RulesyncFile {
|
|
|
9814
9911
|
});
|
|
9815
9912
|
}
|
|
9816
9913
|
};
|
|
9817
|
-
|
|
9818
|
-
|
|
9914
|
+
const SKILL_DESCRIPTION_MAX_LENGTH = 1024;
|
|
9915
|
+
const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
9916
|
+
/**
|
|
9917
|
+
* Check a skill `name` against the length and character rules, phrased for the
|
|
9918
|
+
* `authority` that enforces them (e.g. "the Agent Skills spec" or "Zed"). When
|
|
9919
|
+
* that authority does something with a bad name beyond flagging it — Zed
|
|
9920
|
+
* refuses to load the skill — `consequence` names it so the warning says what
|
|
9921
|
+
* the author is about to lose. An empty name is the caller's concern: what it
|
|
9922
|
+
* means differs per consumer.
|
|
9923
|
+
*/
|
|
9924
|
+
function collectSkillNameViolations({ name, authority, consequence }) {
|
|
9925
|
+
const violations = [];
|
|
9926
|
+
if (name.length > 64) violations.push(`\`name\` is ${name.length} characters; ${authority} allows at most 64${consequence === void 0 ? "" : ` and ${consequence}`}`);
|
|
9927
|
+
if (!SKILL_NAME_PATTERN.test(name)) violations.push(`\`name\` ${quoteForLog(name)} must contain only lowercase letters, digits and single hyphens, with no leading, trailing or consecutive hyphens${consequence === void 0 ? "" : `; ${authority} ${consequence}`}`);
|
|
9928
|
+
return violations;
|
|
9929
|
+
}
|
|
9930
|
+
/**
|
|
9931
|
+
* Report each violation as a warning prefixed with the skill file it concerns.
|
|
9932
|
+
* The path is shown POSIX-style and stripped of control characters so a hostile
|
|
9933
|
+
* directory name cannot forge log lines; `warnWithFallback` keeps the report
|
|
9934
|
+
* visible when the caller has no logger to hand.
|
|
9935
|
+
*/
|
|
9936
|
+
function warnSkillViolations({ skillPath, violations, logger }) {
|
|
9937
|
+
const shownPath = stripControlCharacters(toPosixPath(skillPath));
|
|
9938
|
+
for (const violation of violations) warnWithFallback(logger, `${shownPath}: ${violation}`);
|
|
9939
|
+
}
|
|
9819
9940
|
/**
|
|
9820
9941
|
* Whether a directory on disk can be addressed by its name.
|
|
9821
9942
|
*
|
|
@@ -9869,7 +9990,7 @@ async function getLocalSkillDirNames(sourceTree) {
|
|
|
9869
9990
|
*
|
|
9870
9991
|
* The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
|
|
9871
9992
|
* default that applies to every tool supporting the flag (claudecode, copilot,
|
|
9872
|
-
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh). Each tool's own section may override that
|
|
9993
|
+
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that
|
|
9873
9994
|
* default with a per-target value. A defined section value (including `false`)
|
|
9874
9995
|
* always wins over the root default.
|
|
9875
9996
|
*
|
|
@@ -9888,7 +10009,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
|
9888
10009
|
*
|
|
9889
10010
|
* The rulesync skill frontmatter exposes a root-level `user-invocable` default
|
|
9890
10011
|
* that applies to every tool supporting the flag (claudecode, copilot,
|
|
9891
|
-
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh). Each tool's own section may override that default with a
|
|
10012
|
+
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that default with a
|
|
9892
10013
|
* per-target value. A defined section value (including `false`) always wins
|
|
9893
10014
|
* over the root default.
|
|
9894
10015
|
*
|
|
@@ -10639,10 +10760,13 @@ const AmpCheckFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
10639
10760
|
/**
|
|
10640
10761
|
* Represents an Amp code review check.
|
|
10641
10762
|
*
|
|
10642
|
-
* Amp
|
|
10643
|
-
* scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks
|
|
10644
|
-
*
|
|
10645
|
-
*
|
|
10763
|
+
* Amp reads code review checks as Markdown files with YAML frontmatter,
|
|
10764
|
+
* scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`;
|
|
10765
|
+
* Amp also reads `~/.config/agents/checks/`, which rulesync does not write).
|
|
10766
|
+
* Since the docs rebuild the supported runner is the official
|
|
10767
|
+
* `@amp/review-checks` plugin (`amp review` is deprecated); existing checks in
|
|
10768
|
+
* this format continue to apply.
|
|
10769
|
+
* @see https://ampcode.com/docs/review
|
|
10646
10770
|
*/
|
|
10647
10771
|
var AmpCheck = class AmpCheck extends ToolCheck {
|
|
10648
10772
|
frontmatter;
|
|
@@ -15456,6 +15580,7 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
|
|
|
15456
15580
|
const COMMANDCODE_DIR = ".commandcode";
|
|
15457
15581
|
const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
|
|
15458
15582
|
const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
|
|
15583
|
+
const COMMANDCODE_SETTINGS_LOCAL_FILE_NAME = "settings.local.json";
|
|
15459
15584
|
const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
|
|
15460
15585
|
const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
|
|
15461
15586
|
const COMMANDCODE_COMMANDS_DIR_PATH = (0, node_path.join)(COMMANDCODE_DIR, "commands");
|
|
@@ -16881,10 +17006,8 @@ const AgentsSkillsSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
16881
17006
|
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
16882
17007
|
"allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
|
|
16883
17008
|
});
|
|
16884
|
-
const NAME_MAX_LENGTH = 64;
|
|
16885
|
-
const DESCRIPTION_MAX_LENGTH = 1024;
|
|
16886
17009
|
const COMPATIBILITY_MAX_LENGTH = 500;
|
|
16887
|
-
const
|
|
17010
|
+
const AGENT_SKILLS_SPEC = "the Agent Skills spec";
|
|
16888
17011
|
/**
|
|
16889
17012
|
* Placeholder for an object that has already been encoded once in the same
|
|
16890
17013
|
* value. YAML anchors let one document reference the same node repeatedly, and
|
|
@@ -17048,12 +17171,14 @@ function collectAgentSkillViolations({ frontmatter, dirName, sourceAllowedTools
|
|
|
17048
17171
|
const { name, description } = frontmatter;
|
|
17049
17172
|
if (name.length === 0) violations.push("`name` is required and must not be empty");
|
|
17050
17173
|
else {
|
|
17051
|
-
|
|
17052
|
-
|
|
17174
|
+
violations.push(...collectSkillNameViolations({
|
|
17175
|
+
name,
|
|
17176
|
+
authority: AGENT_SKILLS_SPEC
|
|
17177
|
+
}));
|
|
17053
17178
|
if (name !== dirName) violations.push(`\`name\` "${name}" must match its parent directory name "${dirName}"; conformant clients require them to be equal`);
|
|
17054
17179
|
}
|
|
17055
17180
|
if (description.length === 0) violations.push(EMPTY_SKILL_DESCRIPTION_VIOLATION);
|
|
17056
|
-
else if (description.length >
|
|
17181
|
+
else if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${AGENT_SKILLS_SPEC} allows at most ${SKILL_DESCRIPTION_MAX_LENGTH}`);
|
|
17057
17182
|
const { compatibility } = frontmatter;
|
|
17058
17183
|
if (typeof compatibility !== "string" && compatibility !== void 0) violations.push("`compatibility` must be a string; the Agent Skills spec does not allow a mapping here");
|
|
17059
17184
|
else if (compatibility !== void 0 && compatibility.length > COMPATIBILITY_MAX_LENGTH) violations.push(`\`compatibility\` is ${compatibility.length} characters; the Agent Skills spec allows at most ${COMPATIBILITY_MAX_LENGTH}`);
|
|
@@ -17171,16 +17296,19 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
|
|
|
17171
17296
|
* directory rather than a same-named project path.
|
|
17172
17297
|
*/
|
|
17173
17298
|
static reportSpecViolations({ outputRoot, relativeDirPath, dirName, frontmatter, sourceAllowedTools, logger }) {
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17299
|
+
warnSkillViolations({
|
|
17300
|
+
skillPath: agentSkillFilePath({
|
|
17301
|
+
outputRoot,
|
|
17302
|
+
relativeDirPath,
|
|
17303
|
+
dirName
|
|
17304
|
+
}),
|
|
17305
|
+
violations: collectAgentSkillViolations({
|
|
17306
|
+
frontmatter,
|
|
17307
|
+
dirName,
|
|
17308
|
+
sourceAllowedTools
|
|
17309
|
+
}),
|
|
17310
|
+
logger
|
|
17178
17311
|
});
|
|
17179
|
-
for (const violation of collectAgentSkillViolations({
|
|
17180
|
-
frontmatter,
|
|
17181
|
-
dirName,
|
|
17182
|
-
sourceAllowedTools
|
|
17183
|
-
})) warnWithFallback(logger, `${stripControlCharacters(toPosixPath(skillPath))}: ${violation}`);
|
|
17184
17312
|
}
|
|
17185
17313
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
17186
17314
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
@@ -17820,6 +17948,15 @@ const KiloCommandFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
17820
17948
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
17821
17949
|
variant: zod_mini.z.optional(zod_mini.z.string())
|
|
17822
17950
|
});
|
|
17951
|
+
/**
|
|
17952
|
+
* Command names Kilo keeps for itself. Its loader throws on a custom command
|
|
17953
|
+
* file named `goal.md` in either the project or the global directory ("The
|
|
17954
|
+
* /goal command is reserved for session goals. Rename the custom command."),
|
|
17955
|
+
* and a throw there aborts the whole command scan — every other custom
|
|
17956
|
+
* command becomes unreachable too, not only the reserved one.
|
|
17957
|
+
* https://github.com/Kilo-Org/kilocode/blob/v7.6.2/packages/opencode/src/command/index.ts
|
|
17958
|
+
*/
|
|
17959
|
+
const KILO_RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set(["goal"]);
|
|
17823
17960
|
var KiloCommand = class KiloCommand extends ToolCommand {
|
|
17824
17961
|
frontmatter;
|
|
17825
17962
|
body;
|
|
@@ -17862,6 +17999,20 @@ var KiloCommand = class KiloCommand extends ToolCommand {
|
|
|
17862
17999
|
validate: true
|
|
17863
18000
|
});
|
|
17864
18001
|
}
|
|
18002
|
+
/**
|
|
18003
|
+
* Refuse to write a command Kilo reserves (see
|
|
18004
|
+
* {@link KILO_RESERVED_COMMAND_NAMES}). The name Kilo loads is the file
|
|
18005
|
+
* stem, and the processor calls this on the flattened path, so `goal.md`
|
|
18006
|
+
* is refused at any nesting depth under the basename naming and only at the
|
|
18007
|
+
* top level under the path naming, where `git/goal.md` becomes `git-goal`.
|
|
18008
|
+
* Import is untouched: a `goal.md` already in `.kilo/commands/` is read as
|
|
18009
|
+
* it is, since the reservation is Kilo's to enforce on its own files.
|
|
18010
|
+
*/
|
|
18011
|
+
static getWriteBlockReason({ rulesyncCommand }) {
|
|
18012
|
+
const stem = (0, node_path.basename)(rulesyncCommand.getRelativeFilePath(), ".md");
|
|
18013
|
+
if (!KILO_RESERVED_COMMAND_NAMES.has(stem)) return null;
|
|
18014
|
+
return `Kilo reserves the /${stem} command for session goals and refuses to load a custom command by that name — it would abort Kilo's whole command scan, making every other custom command unreachable too. Rename the command, or exclude it from this target with \`targets\`.`;
|
|
18015
|
+
}
|
|
17865
18016
|
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
|
|
17866
18017
|
const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
|
|
17867
18018
|
const kiloFields = rulesyncFrontmatter.kilo ?? {};
|
|
@@ -20084,6 +20235,14 @@ var CommandsProcessor = class extends FeatureProcessor {
|
|
|
20084
20235
|
if (!factory.class.isTargetedByRulesyncCommand(rulesyncCommand)) return null;
|
|
20085
20236
|
const originalRelativePath = rulesyncCommand.getRelativeFilePath();
|
|
20086
20237
|
const commandToConvert = factory.meta.supportsSubdirectory ? rulesyncCommand : this.flattenRelativeFilePath(rulesyncCommand);
|
|
20238
|
+
const writeBlockReason = factory.class.getWriteBlockReason?.({
|
|
20239
|
+
rulesyncCommand: commandToConvert,
|
|
20240
|
+
global: this.global
|
|
20241
|
+
});
|
|
20242
|
+
if (writeBlockReason !== void 0 && writeBlockReason !== null) {
|
|
20243
|
+
this.logger.warn(`Skipping command ${quoteForLog(originalRelativePath)} for '${this.toolTarget}': ${writeBlockReason}`);
|
|
20244
|
+
return null;
|
|
20245
|
+
}
|
|
20087
20246
|
if (!factory.meta.supportsSubdirectory) {
|
|
20088
20247
|
const flattenedPath = commandToConvert.getRelativeFilePath();
|
|
20089
20248
|
const firstOrigin = flattenedPathOrigins.get(flattenedPath);
|
|
@@ -20670,6 +20829,18 @@ function isToolMatcherEntry(x) {
|
|
|
20670
20829
|
return true;
|
|
20671
20830
|
}
|
|
20672
20831
|
/**
|
|
20832
|
+
* Convert a canonical `timeout` (seconds) into the tool's unit.
|
|
20833
|
+
*/
|
|
20834
|
+
function emitTimeout({ timeout, converterConfig }) {
|
|
20835
|
+
return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? Math.round(timeout * 1e3) : timeout;
|
|
20836
|
+
}
|
|
20837
|
+
/**
|
|
20838
|
+
* Convert a tool `timeout` back into the canonical unit (seconds).
|
|
20839
|
+
*/
|
|
20840
|
+
function importTimeout({ timeout, converterConfig }) {
|
|
20841
|
+
return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? timeout / 1e3 : timeout;
|
|
20842
|
+
}
|
|
20843
|
+
/**
|
|
20673
20844
|
* Filter the shared canonical hooks to the supported events and merge tool overrides on top.
|
|
20674
20845
|
*/
|
|
20675
20846
|
function buildEffectiveHooks$1({ config, toolOverrideHooks, supportedEvents }) {
|
|
@@ -20810,6 +20981,7 @@ const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
|
20810
20981
|
const isEmittableArray = ({ value }) => isStringArray$1(value);
|
|
20811
20982
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
20812
20983
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
20984
|
+
const isEmittableObject = ({ value }) => isPlainObject$1(value);
|
|
20813
20985
|
/**
|
|
20814
20986
|
* The canonical schema of one hook field, looked up by name. Read off the
|
|
20815
20987
|
* schema's own shape rather than by parsing a one-field object, so a `canonical`
|
|
@@ -20844,11 +21016,17 @@ const isImportableNumber = ({ value, canonical }) => Number.isFinite(value) && s
|
|
|
20844
21016
|
value,
|
|
20845
21017
|
canonical
|
|
20846
21018
|
});
|
|
21019
|
+
const isImportableObject = ({ value, canonical }) => isPlainObject$1(value) && satisfiesCanonicalField({
|
|
21020
|
+
value,
|
|
21021
|
+
canonical
|
|
21022
|
+
});
|
|
20847
21023
|
/**
|
|
20848
21024
|
* Say which rule the value broke, so the warning names the actual constraint
|
|
20849
21025
|
* rather than asserting a canonical rejection that may not be the reason. A
|
|
20850
21026
|
* closed enum lists its members; a rule carrying its own message (the
|
|
20851
|
-
* control-character check behind `safeString`) reuses it.
|
|
21027
|
+
* control-character check behind `safeString`) reuses it. An object field
|
|
21028
|
+
* names the offending key, since zod's generic "Invalid input" would not tell
|
|
21029
|
+
* the reader which key is missing or malformed.
|
|
20852
21030
|
*/
|
|
20853
21031
|
function describeScalarConstraint({ canonical, value }) {
|
|
20854
21032
|
const schema = canonicalFieldSchema(canonical);
|
|
@@ -20856,13 +21034,22 @@ function describeScalarConstraint({ canonical, value }) {
|
|
|
20856
21034
|
if (result === void 0 || result.success) return `it is not a value this field carries through.`;
|
|
20857
21035
|
const issue = result.error.issues[0];
|
|
20858
21036
|
if (issue === void 0) return `it is not a value the canonical "${canonical}" field accepts.`;
|
|
20859
|
-
return `it does not satisfy the canonical "${canonical}" field: ${issue.message}.`;
|
|
21037
|
+
return `it does not satisfy the canonical "${canonical}" field${issue.path.length > 0 ? ` at ${quoteValueForWarning(issue.path.map(String).join("."))}` : ""}: ${issue.message}.`;
|
|
20860
21038
|
}
|
|
20861
21039
|
const describeInvalidScalar = ({ tool, canonical, value }) => `Dropping "${tool}" (${quoteValueForWarning(value)}) while importing a hook: ${describeScalarConstraint({
|
|
20862
21040
|
canonical,
|
|
20863
21041
|
value
|
|
20864
21042
|
})} Importing it would fail validation on the next run.`;
|
|
20865
21043
|
const describeInvalidArray = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a list of strings without newline, carriage return or NUL characters.`;
|
|
21044
|
+
/**
|
|
21045
|
+
* An object is quoted by its offending key rather than its value: the payload
|
|
21046
|
+
* can be arbitrarily large, and the missing or malformed key is what the
|
|
21047
|
+
* reader needs to fix.
|
|
21048
|
+
*/
|
|
21049
|
+
const describeInvalidObject = ({ tool, canonical, value }) => isPlainObject$1(value) ? `Dropping "${tool}" while importing a hook: ${describeScalarConstraint({
|
|
21050
|
+
canonical,
|
|
21051
|
+
value
|
|
21052
|
+
})} Importing it would fail validation on the next run.` : `Dropping "${tool}" while importing a hook: it must be an object.`;
|
|
20866
21053
|
const describeInvalidRecord = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a map of strings whose keys are non-empty and free of "=", and whose keys and values carry no newline, carriage return or NUL characters.`;
|
|
20867
21054
|
/**
|
|
20868
21055
|
* Check a value against the shape its field documents. A string field also
|
|
@@ -20925,6 +21112,72 @@ function isSupportedHookType({ type, converterConfig }) {
|
|
|
20925
21112
|
return converterConfig.supportedHookTypes?.has(type ?? "command") ?? true;
|
|
20926
21113
|
}
|
|
20927
21114
|
/**
|
|
21115
|
+
* Whether a keyword field applies to this hook, warning about the reason when
|
|
21116
|
+
* it does not. Shared by both directions: the field is unusable on the tool
|
|
21117
|
+
* side either way, so the value is dropped rather than carried into a config
|
|
21118
|
+
* the tool would ignore or refuse.
|
|
21119
|
+
*/
|
|
21120
|
+
function isKeywordFieldApplicable({ spec, hookType, eventName, direction, warn }) {
|
|
21121
|
+
const { canonical, tool, commandOnly, events } = spec;
|
|
21122
|
+
const field = direction === "generated" ? canonical : tool;
|
|
21123
|
+
if (!isFieldApplicable({
|
|
21124
|
+
commandOnly,
|
|
21125
|
+
hookType
|
|
21126
|
+
})) {
|
|
21127
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on "command" hooks only, so it is not ${direction}.`);
|
|
21128
|
+
return false;
|
|
21129
|
+
}
|
|
21130
|
+
if (events !== void 0 && !events.has(eventName)) {
|
|
21131
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${[...events].map((event) => `"${event}"`).join(", ")} only, so it is not ${direction}.`);
|
|
21132
|
+
return false;
|
|
21133
|
+
}
|
|
21134
|
+
return true;
|
|
21135
|
+
}
|
|
21136
|
+
/**
|
|
21137
|
+
* Emit the keyword passthrough fields: a canonical `true` becomes the tool's
|
|
21138
|
+
* keyword, while `false` is omitted as the tool's own default. A `true` the
|
|
21139
|
+
* tool would not honour on this hook is warned about, like the other kinds;
|
|
21140
|
+
* a `false` there is dropped silently, since omitting it changes nothing.
|
|
21141
|
+
*/
|
|
21142
|
+
function emitKeywordFields({ def, hookType, eventName, fields, warn }) {
|
|
21143
|
+
const emitted = {};
|
|
21144
|
+
for (const spec of fields) {
|
|
21145
|
+
if (def[spec.canonical] !== true) continue;
|
|
21146
|
+
if (isKeywordFieldApplicable({
|
|
21147
|
+
spec,
|
|
21148
|
+
hookType,
|
|
21149
|
+
eventName,
|
|
21150
|
+
direction: "generated",
|
|
21151
|
+
warn
|
|
21152
|
+
})) emitted[spec.tool] = spec.trueValue;
|
|
21153
|
+
}
|
|
21154
|
+
return emitted;
|
|
21155
|
+
}
|
|
21156
|
+
/**
|
|
21157
|
+
* Import the keyword passthrough fields back into canonical booleans,
|
|
21158
|
+
* reversing {@link emitKeywordFields}. Only the two documented keywords are
|
|
21159
|
+
* read; anything else is warned about and dropped, so a typo in a hand-written
|
|
21160
|
+
* tool file never lands in `.rulesync/hooks.*` as a boolean it did not mean.
|
|
21161
|
+
*/
|
|
21162
|
+
function importKeywordFields({ h, hookType, eventName, fields, warn }) {
|
|
21163
|
+
const imported = {};
|
|
21164
|
+
for (const spec of fields) {
|
|
21165
|
+
const value = h[spec.tool];
|
|
21166
|
+
if (value === void 0) continue;
|
|
21167
|
+
if (!isKeywordFieldApplicable({
|
|
21168
|
+
spec,
|
|
21169
|
+
hookType,
|
|
21170
|
+
eventName,
|
|
21171
|
+
direction: "imported",
|
|
21172
|
+
warn
|
|
21173
|
+
})) continue;
|
|
21174
|
+
if (value === spec.trueValue) imported[spec.canonical] = true;
|
|
21175
|
+
else if (spec.falseValue !== void 0 && value === spec.falseValue) imported[spec.canonical] = false;
|
|
21176
|
+
else warn?.(`Dropping "${spec.tool}" from an imported "${hookType}" hook on "${eventName}": ${quoteValueForWarning(value)} is not a value this tool documents for it.`);
|
|
21177
|
+
}
|
|
21178
|
+
return imported;
|
|
21179
|
+
}
|
|
21180
|
+
/**
|
|
20928
21181
|
* Emit every per-hook passthrough kind for one canonical definition.
|
|
20929
21182
|
*/
|
|
20930
21183
|
function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, warn }) {
|
|
@@ -20937,6 +21190,13 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
|
|
|
20937
21190
|
isValid: isBooleanValue,
|
|
20938
21191
|
warn
|
|
20939
21192
|
}),
|
|
21193
|
+
...emitKeywordFields({
|
|
21194
|
+
def,
|
|
21195
|
+
hookType,
|
|
21196
|
+
eventName,
|
|
21197
|
+
fields: converterConfig.keywordPassthroughFields ?? [],
|
|
21198
|
+
warn
|
|
21199
|
+
}),
|
|
20940
21200
|
...emitPassthroughFields({
|
|
20941
21201
|
def,
|
|
20942
21202
|
hookType,
|
|
@@ -20968,6 +21228,14 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
|
|
|
20968
21228
|
fields: converterConfig.recordPassthroughFields ?? [],
|
|
20969
21229
|
isValid: isEmittableRecord,
|
|
20970
21230
|
warn
|
|
21231
|
+
}),
|
|
21232
|
+
...emitPassthroughFields({
|
|
21233
|
+
def,
|
|
21234
|
+
hookType,
|
|
21235
|
+
eventName,
|
|
21236
|
+
fields: converterConfig.objectPassthroughFields ?? [],
|
|
21237
|
+
isValid: isEmittableObject,
|
|
21238
|
+
warn
|
|
20971
21239
|
})
|
|
20972
21240
|
};
|
|
20973
21241
|
}
|
|
@@ -20991,9 +21259,12 @@ function buildToolHooks({ defs, eventName, converterConfig, warn }) {
|
|
|
20991
21259
|
converterConfig,
|
|
20992
21260
|
warn
|
|
20993
21261
|
}),
|
|
20994
|
-
type: hookType,
|
|
21262
|
+
type: converterConfig.hookTypeNames?.[hookType] ?? hookType,
|
|
20995
21263
|
...command !== void 0 && command !== null && { command },
|
|
20996
|
-
...def.timeout !== void 0 && def.timeout !== null && { timeout:
|
|
21264
|
+
...def.timeout !== void 0 && def.timeout !== null && { timeout: emitTimeout({
|
|
21265
|
+
timeout: def.timeout,
|
|
21266
|
+
converterConfig
|
|
21267
|
+
}) },
|
|
20997
21268
|
...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
|
|
20998
21269
|
...emitTypePayloadFields({
|
|
20999
21270
|
def,
|
|
@@ -21115,6 +21386,20 @@ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
|
|
|
21115
21386
|
function isImportedHookType(value) {
|
|
21116
21387
|
return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
|
|
21117
21388
|
}
|
|
21389
|
+
/**
|
|
21390
|
+
* The canonical hook type a tool-side `type` value denotes: the tool's own
|
|
21391
|
+
* spelling from {@link ToolHooksConverterConfig.hookTypeNames} when it has
|
|
21392
|
+
* one, otherwise the value itself when it is a canonical type, and `command`
|
|
21393
|
+
* for anything else (the same coercion as before the renames existed).
|
|
21394
|
+
*/
|
|
21395
|
+
function resolveImportedHookType({ value, converterConfig }) {
|
|
21396
|
+
const names = converterConfig.hookTypeNames;
|
|
21397
|
+
if (names !== void 0 && typeof value === "string") {
|
|
21398
|
+
for (const [canonical, tool] of Object.entries(names)) if (tool === value) return canonical;
|
|
21399
|
+
if (Object.hasOwn(names, value)) return "command";
|
|
21400
|
+
}
|
|
21401
|
+
return isImportedHookType(value) ? value : "command";
|
|
21402
|
+
}
|
|
21118
21403
|
function isStringRecord(value) {
|
|
21119
21404
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21120
21405
|
return Object.values(value).every((v) => typeof v === "string");
|
|
@@ -21240,7 +21525,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
21240
21525
|
* Import every per-hook passthrough kind for one tool hook record, reversing
|
|
21241
21526
|
* the emit side in {@link buildToolHooks}.
|
|
21242
21527
|
*/
|
|
21243
|
-
function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
21528
|
+
function importAllPassthroughFields({ h, hookType, eventName, converterConfig, warn }) {
|
|
21244
21529
|
return {
|
|
21245
21530
|
...importPassthroughFields({
|
|
21246
21531
|
h,
|
|
@@ -21249,6 +21534,13 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
|
21249
21534
|
isValid: isBooleanValue,
|
|
21250
21535
|
warn
|
|
21251
21536
|
}),
|
|
21537
|
+
...importKeywordFields({
|
|
21538
|
+
h,
|
|
21539
|
+
hookType,
|
|
21540
|
+
eventName,
|
|
21541
|
+
fields: converterConfig.keywordPassthroughFields ?? [],
|
|
21542
|
+
warn
|
|
21543
|
+
}),
|
|
21252
21544
|
...importPassthroughFields({
|
|
21253
21545
|
h,
|
|
21254
21546
|
hookType,
|
|
@@ -21280,14 +21572,25 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
|
21280
21572
|
isValid: isEmittableRecord,
|
|
21281
21573
|
describeInvalid: describeInvalidRecord,
|
|
21282
21574
|
warn
|
|
21575
|
+
}),
|
|
21576
|
+
...importPassthroughFields({
|
|
21577
|
+
h,
|
|
21578
|
+
hookType,
|
|
21579
|
+
fields: converterConfig.objectPassthroughFields ?? [],
|
|
21580
|
+
isValid: isImportableObject,
|
|
21581
|
+
describeInvalid: describeInvalidObject,
|
|
21582
|
+
warn
|
|
21283
21583
|
})
|
|
21284
21584
|
};
|
|
21285
21585
|
}
|
|
21286
21586
|
/**
|
|
21287
21587
|
* Convert a single tool hook record into a canonical hook definition.
|
|
21288
21588
|
*/
|
|
21289
|
-
function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
21290
|
-
const hookType =
|
|
21589
|
+
function toolHookToCanonical({ h, rawEntry, eventName, converterConfig, warn }) {
|
|
21590
|
+
const hookType = resolveImportedHookType({
|
|
21591
|
+
value: h.type,
|
|
21592
|
+
converterConfig
|
|
21593
|
+
});
|
|
21291
21594
|
const command = importCanonicalString({
|
|
21292
21595
|
value: stripCommandPrefix({
|
|
21293
21596
|
command: h.command,
|
|
@@ -21296,7 +21599,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
|
21296
21599
|
canonical: "command",
|
|
21297
21600
|
warn
|
|
21298
21601
|
});
|
|
21299
|
-
const timeout = typeof h.timeout === "number" ?
|
|
21602
|
+
const timeout = typeof h.timeout === "number" ? importTimeout({
|
|
21603
|
+
timeout: h.timeout,
|
|
21604
|
+
converterConfig
|
|
21605
|
+
}) : void 0;
|
|
21300
21606
|
const prompt = importCanonicalString({
|
|
21301
21607
|
value: h.prompt,
|
|
21302
21608
|
canonical: "prompt",
|
|
@@ -21328,6 +21634,7 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
|
21328
21634
|
...importAllPassthroughFields({
|
|
21329
21635
|
h,
|
|
21330
21636
|
hookType,
|
|
21637
|
+
eventName,
|
|
21331
21638
|
converterConfig,
|
|
21332
21639
|
warn
|
|
21333
21640
|
}),
|
|
@@ -21414,7 +21721,7 @@ function describeHookSkipReason({ h, rawEntry, hookType, converterConfig }) {
|
|
|
21414
21721
|
/**
|
|
21415
21722
|
* Convert a single tool matcher entry into canonical hook definitions.
|
|
21416
21723
|
*/
|
|
21417
|
-
function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
21724
|
+
function toolMatcherEntryToCanonical({ rawEntry, eventName, converterConfig, warn }) {
|
|
21418
21725
|
const hookDefs = rawEntry.hooks ?? [];
|
|
21419
21726
|
const groupSkipReason = describeGroupSkipReason({
|
|
21420
21727
|
rawEntry,
|
|
@@ -21429,7 +21736,10 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
|
21429
21736
|
const skipReason = describeHookSkipReason({
|
|
21430
21737
|
h,
|
|
21431
21738
|
rawEntry,
|
|
21432
|
-
hookType:
|
|
21739
|
+
hookType: resolveImportedHookType({
|
|
21740
|
+
value: h.type,
|
|
21741
|
+
converterConfig
|
|
21742
|
+
}),
|
|
21433
21743
|
converterConfig
|
|
21434
21744
|
});
|
|
21435
21745
|
if (skipReason !== void 0) {
|
|
@@ -21439,6 +21749,7 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
|
21439
21749
|
definitions.push(toolHookToCanonical({
|
|
21440
21750
|
h,
|
|
21441
21751
|
rawEntry,
|
|
21752
|
+
eventName,
|
|
21442
21753
|
converterConfig,
|
|
21443
21754
|
warn
|
|
21444
21755
|
}));
|
|
@@ -21486,6 +21797,7 @@ function toolHooksToCanonical({ hooks, converterConfig, logger }) {
|
|
|
21486
21797
|
if (!isToolMatcherEntry(rawEntry)) continue;
|
|
21487
21798
|
defs.push(...toolMatcherEntryToCanonical({
|
|
21488
21799
|
rawEntry,
|
|
21800
|
+
eventName,
|
|
21489
21801
|
converterConfig,
|
|
21490
21802
|
warn
|
|
21491
21803
|
}));
|
|
@@ -21914,6 +22226,7 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
|
|
|
21914
22226
|
"beforeSubmitPrompt"
|
|
21915
22227
|
]),
|
|
21916
22228
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
22229
|
+
timeoutUnit: "milliseconds",
|
|
21917
22230
|
arrayPassthroughFields: [{
|
|
21918
22231
|
canonical: "args",
|
|
21919
22232
|
tool: "args"
|
|
@@ -22512,9 +22825,12 @@ const BOB_SPEC = {
|
|
|
22512
22825
|
noMatcherEvents: /* @__PURE__ */ new Set([
|
|
22513
22826
|
"sessionStart",
|
|
22514
22827
|
"beforeSubmitPrompt",
|
|
22828
|
+
"preCompact",
|
|
22829
|
+
"postCompact",
|
|
22515
22830
|
"stop"
|
|
22516
22831
|
]),
|
|
22517
|
-
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
22832
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command", "http"]),
|
|
22833
|
+
hookTypeNames: { http: "https" },
|
|
22518
22834
|
wildcardMatcherMeansAll: true
|
|
22519
22835
|
}
|
|
22520
22836
|
};
|
|
@@ -22525,10 +22841,13 @@ const BOB_SPEC = {
|
|
|
22525
22841
|
* `<project>/.bob/settings.json` (project scope) and
|
|
22526
22842
|
* `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
|
|
22527
22843
|
* `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
|
|
22528
|
-
* "command": "...", "timeout"?: <seconds> }] }] }
|
|
22529
|
-
*
|
|
22530
|
-
*
|
|
22844
|
+
* "command": "...", "timeout"?: <seconds> }] }] }`, where a handler may
|
|
22845
|
+
* instead be `{ "type": "https", "url": "...", "timeout"?: <seconds> }`. The
|
|
22846
|
+
* file also holds settings rulesync does not own, so generation merges the
|
|
22847
|
+
* `hooks` key into it (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting
|
|
22848
|
+
* the file.
|
|
22531
22849
|
*
|
|
22850
|
+
* @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
|
|
22532
22851
|
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
22533
22852
|
*/
|
|
22534
22853
|
var BobHooks = class extends SettingsJsonHooks {
|
|
@@ -23958,6 +24277,11 @@ const CORTEXCODE_SPEC = {
|
|
|
23958
24277
|
booleanPassthroughFields: [{
|
|
23959
24278
|
canonical: "enabled",
|
|
23960
24279
|
tool: "enabled"
|
|
24280
|
+
}],
|
|
24281
|
+
objectPassthroughFields: [{
|
|
24282
|
+
canonical: "source",
|
|
24283
|
+
tool: "source",
|
|
24284
|
+
commandOnly: true
|
|
23961
24285
|
}]
|
|
23962
24286
|
}
|
|
23963
24287
|
};
|
|
@@ -23968,8 +24292,9 @@ const CORTEXCODE_SPEC = {
|
|
|
23968
24292
|
* (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
|
|
23969
24293
|
* scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
|
|
23970
24294
|
* "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
|
|
23971
|
-
* <seconds>, "enabled"?: <boolean
|
|
23972
|
-
* settings rulesync does not own, so generation
|
|
24295
|
+
* <seconds>, "enabled"?: <boolean>, "source"?: { "source", "ref"? } }] }] }`.
|
|
24296
|
+
* The project file also holds settings rulesync does not own, so generation
|
|
24297
|
+
* merges the `hooks` key into
|
|
23973
24298
|
* either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
|
|
23974
24299
|
* neither file is removed wholesale (hooks.json sits in the CLI-owned
|
|
23975
24300
|
* `~/.snowflake/cortex/` tree).
|
|
@@ -25093,7 +25418,15 @@ const GOOSE_CONVERTER_CONFIG = {
|
|
|
25093
25418
|
toolToCanonicalEventNames: GOOSE_TO_CANONICAL_EVENT_NAMES,
|
|
25094
25419
|
projectDirVar: "",
|
|
25095
25420
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
25096
|
-
wildcardMatcherMeansAll: true
|
|
25421
|
+
wildcardMatcherMeansAll: true,
|
|
25422
|
+
keywordPassthroughFields: [{
|
|
25423
|
+
canonical: "failClosed",
|
|
25424
|
+
tool: "on_failure",
|
|
25425
|
+
trueValue: "block",
|
|
25426
|
+
falseValue: "allow",
|
|
25427
|
+
commandOnly: true,
|
|
25428
|
+
events: /* @__PURE__ */ new Set(["preToolUse"])
|
|
25429
|
+
}]
|
|
25097
25430
|
};
|
|
25098
25431
|
/**
|
|
25099
25432
|
* Represents a Goose lifecycle hooks file.
|
|
@@ -25402,7 +25735,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
25402
25735
|
for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
|
|
25403
25736
|
if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
|
|
25404
25737
|
if (HERMESAGENT_SHELL_UNSUPPORTED_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is Python-plugin-only; Hermes Agent refuses it for shell hooks, so the generated entry will never run.`);
|
|
25405
|
-
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.
|
|
25738
|
+
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.3; preserving it for forward compatibility.`);
|
|
25406
25739
|
setHermesHookEntries({
|
|
25407
25740
|
result,
|
|
25408
25741
|
event: nativeEvent,
|
|
@@ -26282,7 +26615,8 @@ const KiroIdeHookEntrySchema = zod_mini.z.looseObject({
|
|
|
26282
26615
|
matcher: zod_mini.z.optional(zod_mini.z.string()),
|
|
26283
26616
|
action: zod_mini.z.optional(KiroIdeHookActionSchema),
|
|
26284
26617
|
timeout: zod_mini.z.optional(zod_mini.z.number()),
|
|
26285
|
-
enabled: zod_mini.z.optional(zod_mini.z.boolean())
|
|
26618
|
+
enabled: zod_mini.z.optional(zod_mini.z.boolean()),
|
|
26619
|
+
confirm: zod_mini.z.optional(HookConfirmSchema)
|
|
26286
26620
|
});
|
|
26287
26621
|
const KiroIdeHooksFileSchema = zod_mini.z.looseObject({
|
|
26288
26622
|
version: zod_mini.z.optional(zod_mini.z.string()),
|
|
@@ -26321,7 +26655,8 @@ function buildKiroIdeEntriesForEvent(trigger, definitions) {
|
|
|
26321
26655
|
...def.matcher !== void 0 && def.matcher !== null && def.matcher !== "" && { matcher: def.matcher },
|
|
26322
26656
|
action,
|
|
26323
26657
|
...def.timeout !== void 0 && def.timeout !== null && def.timeout >= 0 && { timeout: def.timeout },
|
|
26324
|
-
enabled: def.enabled ?? true
|
|
26658
|
+
enabled: def.enabled ?? true,
|
|
26659
|
+
...def.confirm !== void 0 && { confirm: def.confirm }
|
|
26325
26660
|
});
|
|
26326
26661
|
}
|
|
26327
26662
|
return entries;
|
|
@@ -26363,6 +26698,14 @@ function canonicalToKiroIdeHooks(config) {
|
|
|
26363
26698
|
}
|
|
26364
26699
|
return entries;
|
|
26365
26700
|
}
|
|
26701
|
+
/**
|
|
26702
|
+
* Copies the optional per-hook fields (`enabled`, `confirm`) back onto the
|
|
26703
|
+
* canonical definition.
|
|
26704
|
+
*/
|
|
26705
|
+
function copyOptionalHookFields({ entry, def }) {
|
|
26706
|
+
if (entry.enabled === false) def.enabled = false;
|
|
26707
|
+
if (entry.confirm !== void 0 && entry.confirm !== null) def.confirm = omitPrototypePollutionKeysDeep(entry.confirm);
|
|
26708
|
+
}
|
|
26366
26709
|
function kiroIdeHooksToCanonical(entries) {
|
|
26367
26710
|
const canonical = {};
|
|
26368
26711
|
for (const entry of entries) {
|
|
@@ -26386,7 +26729,10 @@ function kiroIdeHooksToCanonical(entries) {
|
|
|
26386
26729
|
if (entry.description !== void 0 && entry.description !== null) def.description = entry.description;
|
|
26387
26730
|
if (entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "") def.matcher = entry.matcher;
|
|
26388
26731
|
if (entry.timeout !== void 0 && entry.timeout !== null) def.timeout = entry.timeout;
|
|
26389
|
-
|
|
26732
|
+
copyOptionalHookFields({
|
|
26733
|
+
entry,
|
|
26734
|
+
def
|
|
26735
|
+
});
|
|
26390
26736
|
const list = lookupOwn({
|
|
26391
26737
|
record: canonical,
|
|
26392
26738
|
key: eventName
|
|
@@ -27393,10 +27739,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
27393
27739
|
//#endregion
|
|
27394
27740
|
//#region src/features/hooks/reasonix-hooks.ts
|
|
27395
27741
|
/**
|
|
27396
|
-
* Only
|
|
27397
|
-
* against the tool name); it is ignored on every other event.
|
|
27742
|
+
* Only the tool-scoped events honor the `match` field (an anchored regex
|
|
27743
|
+
* against the tool name); it is ignored on every other event. Mirrors
|
|
27744
|
+
* `UsesToolMatcher` in upstream `internal/hook/inspect.go`.
|
|
27398
27745
|
*/
|
|
27399
|
-
const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set([
|
|
27746
|
+
const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set([
|
|
27747
|
+
"PreToolUse",
|
|
27748
|
+
"PostToolUse",
|
|
27749
|
+
"PostToolUseFailure",
|
|
27750
|
+
"PermissionRequest"
|
|
27751
|
+
]);
|
|
27400
27752
|
const SUPPORTED_REASONIX_EVENTS = new Set(REASONIX_HOOK_EVENTS);
|
|
27401
27753
|
/**
|
|
27402
27754
|
* Convert canonical hooks config to the Reasonix `hooks` object.
|
|
@@ -27478,9 +27830,11 @@ function reasonixHooksToCanonical(hooks) {
|
|
|
27478
27830
|
* Reasonix hooks live in a Claude-Code-style but standalone JSON file —
|
|
27479
27831
|
* `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
|
|
27480
27832
|
* (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
|
|
27481
|
-
* All
|
|
27482
|
-
* PreToolUse/PostToolUse/
|
|
27483
|
-
* SubagentStop/PostLLMCall/
|
|
27833
|
+
* All thirteen upstream events have a clean canonical equivalent and are mapped:
|
|
27834
|
+
* PreToolUse/PostToolUse/PostToolUseFailure/PermissionRequest/UserPromptSubmit/
|
|
27835
|
+
* Stop/StopFailure plus SessionStart/SessionEnd/SubagentStop/PostLLMCall/
|
|
27836
|
+
* Notification/PreCompact (see REASONIX_HOOK_EVENTS).
|
|
27837
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
|
|
27484
27838
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
27485
27839
|
*/
|
|
27486
27840
|
var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
@@ -28306,6 +28660,23 @@ const PER_HOOK_ENABLED_TARGETS = /* @__PURE__ */ new Set([
|
|
|
28306
28660
|
"kiro-ide",
|
|
28307
28661
|
"zcode"
|
|
28308
28662
|
]);
|
|
28663
|
+
/** The targets whose hooks format carries the Kiro `confirm` prompt. */
|
|
28664
|
+
const PER_HOOK_CONFIRM_TARGETS = /* @__PURE__ */ new Set(["kiro-cli", "kiro-ide"]);
|
|
28665
|
+
/**
|
|
28666
|
+
* Names the shared events that carry a hook whose canonical definition
|
|
28667
|
+
* satisfies `predicate`, excluding events the target skips altogether (those
|
|
28668
|
+
* are already reported as skipped and produce no output, so warning about them
|
|
28669
|
+
* again would contradict that message).
|
|
28670
|
+
*/
|
|
28671
|
+
function emittedEventsWithHook(params) {
|
|
28672
|
+
const { factory, sharedHooks, effectiveHooks, predicate } = params;
|
|
28673
|
+
const skippedEvents = new Set(unsupportedEventNames({
|
|
28674
|
+
factory,
|
|
28675
|
+
sharedHooks,
|
|
28676
|
+
effectiveHooks
|
|
28677
|
+
}));
|
|
28678
|
+
return Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => predicate(def))).map(([event]) => event);
|
|
28679
|
+
}
|
|
28309
28680
|
const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
28310
28681
|
["amp", {
|
|
28311
28682
|
class: AmpHooks,
|
|
@@ -28615,7 +28986,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
28615
28986
|
supportsImport: true
|
|
28616
28987
|
},
|
|
28617
28988
|
supportedEvents: BOB_HOOK_EVENTS,
|
|
28618
|
-
supportedHookTypes: ["command"],
|
|
28989
|
+
supportedHookTypes: ["command", "http"],
|
|
28619
28990
|
supportsMatcher: true,
|
|
28620
28991
|
matcherEvents: ["preToolUse", "postToolUse"]
|
|
28621
28992
|
}],
|
|
@@ -28876,15 +29247,11 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
28876
29247
|
}
|
|
28877
29248
|
for (const [hookType, events] of unsupportedTypeToEvents) this.logger.warn(`Skipped ${hookType}-type hook(s) for ${this.toolTarget} (not supported): ${Array.from(events).join(", ")}`);
|
|
28878
29249
|
}
|
|
28879
|
-
|
|
28880
|
-
|
|
28881
|
-
|
|
28882
|
-
|
|
28883
|
-
|
|
28884
|
-
}));
|
|
28885
|
-
const eventsWithDisabledHooks = Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => def.enabled === false)).map(([event]) => event);
|
|
28886
|
-
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the kiro-cli / kiro-ide / zcode hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
|
|
28887
|
-
}
|
|
29250
|
+
this.warnAboutDroppedPerHookFields({
|
|
29251
|
+
factory,
|
|
29252
|
+
sharedHooks,
|
|
29253
|
+
effectiveHooks
|
|
29254
|
+
});
|
|
28888
29255
|
const eventsWithUnsupportedMatcher = unsupportedMatcherEventNames({
|
|
28889
29256
|
factory,
|
|
28890
29257
|
effectiveHooks
|
|
@@ -28914,6 +29281,33 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
28914
29281
|
if (auxiliaryFiles && auxiliaryFiles.length > 0) result.push(...auxiliaryFiles);
|
|
28915
29282
|
return result;
|
|
28916
29283
|
}
|
|
29284
|
+
/**
|
|
29285
|
+
* Warns about canonical per-hook fields that this target's hooks format
|
|
29286
|
+
* cannot express, so a hook does not silently change behavior on generation.
|
|
29287
|
+
* Only canonical definitions are considered: a tool-native field inside an
|
|
29288
|
+
* override block is passed through verbatim and honored by that tool.
|
|
29289
|
+
*/
|
|
29290
|
+
warnAboutDroppedPerHookFields(params) {
|
|
29291
|
+
const { factory, sharedHooks, effectiveHooks } = params;
|
|
29292
|
+
if (!PER_HOOK_ENABLED_TARGETS.has(this.toolTarget)) {
|
|
29293
|
+
const eventsWithDisabledHooks = emittedEventsWithHook({
|
|
29294
|
+
factory,
|
|
29295
|
+
sharedHooks,
|
|
29296
|
+
effectiveHooks,
|
|
29297
|
+
predicate: (def) => def.enabled === false
|
|
29298
|
+
});
|
|
29299
|
+
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the kiro-cli / kiro-ide / zcode hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
|
|
29300
|
+
}
|
|
29301
|
+
if (!PER_HOOK_CONFIRM_TARGETS.has(this.toolTarget)) {
|
|
29302
|
+
const eventsWithConfirmHooks = emittedEventsWithHook({
|
|
29303
|
+
factory,
|
|
29304
|
+
sharedHooks,
|
|
29305
|
+
effectiveHooks,
|
|
29306
|
+
predicate: (def) => def.confirm !== void 0
|
|
29307
|
+
});
|
|
29308
|
+
if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the kiro-cli / kiro-ide hooks formats support it): ${eventsWithConfirmHooks.join(", ")}`);
|
|
29309
|
+
}
|
|
29310
|
+
}
|
|
28917
29311
|
async convertToolFilesToRulesyncFiles(toolFiles) {
|
|
28918
29312
|
const hooks = toolFiles.filter((f) => f instanceof ToolHooks);
|
|
28919
29313
|
const logger = withToolTargetPrefix({
|
|
@@ -31429,6 +31823,50 @@ function parseBobMcpConfig({ fileContent, relativePath }) {
|
|
|
31429
31823
|
/** The `type` Bob IDE writes for a streamable HTTP server. */
|
|
31430
31824
|
const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
|
|
31431
31825
|
/**
|
|
31826
|
+
* The OAuth keys Bob reads flat on a server entry, next to `oauth` (a
|
|
31827
|
+
* boolean: `true` forces the flow on, `false` off, absent lets Bob detect it).
|
|
31828
|
+
* The canonical shape is Claude Code's nested `oauth: { clientId, ... }`
|
|
31829
|
+
* object, so the two are translated in both directions.
|
|
31830
|
+
* @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-oauth
|
|
31831
|
+
*/
|
|
31832
|
+
const BOB_FLAT_OAUTH_KEYS = [
|
|
31833
|
+
"clientId",
|
|
31834
|
+
"clientSecret",
|
|
31835
|
+
"scope"
|
|
31836
|
+
];
|
|
31837
|
+
/**
|
|
31838
|
+
* Flatten the canonical `oauth` object onto a Bob server entry: `oauth: true`
|
|
31839
|
+
* plus the string-valued keys Bob documents. A flat key the server already
|
|
31840
|
+
* carries (authored in Bob's own spelling) is kept over the nested one. Other
|
|
31841
|
+
* nested keys (`callbackPort`, `redirectUri`) have no Bob counterpart and are
|
|
31842
|
+
* dropped rather than nested under a key Bob reads as a boolean.
|
|
31843
|
+
*/
|
|
31844
|
+
function flattenOauthForBob({ oauth, converted }) {
|
|
31845
|
+
converted.oauth = true;
|
|
31846
|
+
for (const key of BOB_FLAT_OAUTH_KEYS) {
|
|
31847
|
+
const value = oauth[key];
|
|
31848
|
+
if (typeof value === "string" && converted[key] === void 0) converted[key] = value;
|
|
31849
|
+
}
|
|
31850
|
+
}
|
|
31851
|
+
/**
|
|
31852
|
+
* Reverse of {@link flattenOauthForBob}: gather Bob's flat OAuth keys into the
|
|
31853
|
+
* canonical `oauth` object. `oauth: true` always folds (even with no keys, so
|
|
31854
|
+
* the explicit enable survives the round-trip as an empty object); with no
|
|
31855
|
+
* `oauth` key at all the fold happens only when a flat key is present, since
|
|
31856
|
+
* that is what makes the entry an OAuth one for Bob; `oauth: false` and
|
|
31857
|
+
* anything else leave the entry as it is.
|
|
31858
|
+
*/
|
|
31859
|
+
function nestOauthFromBob(converted) {
|
|
31860
|
+
const flat = BOB_FLAT_OAUTH_KEYS.filter((key) => typeof converted[key] === "string");
|
|
31861
|
+
if (!(converted.oauth === true || converted.oauth === void 0 && flat.length > 0)) return;
|
|
31862
|
+
const oauth = {};
|
|
31863
|
+
for (const key of flat) {
|
|
31864
|
+
oauth[key] = converted[key];
|
|
31865
|
+
delete converted[key];
|
|
31866
|
+
}
|
|
31867
|
+
converted.oauth = oauth;
|
|
31868
|
+
}
|
|
31869
|
+
/**
|
|
31432
31870
|
* The remote transport Bob IDE reads a server as, spelled the way it writes
|
|
31433
31871
|
* it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
|
|
31434
31872
|
* `url` with no `type`. `http` is the canonical rulesync alias for streamable
|
|
@@ -31451,7 +31889,9 @@ function asBobRemoteType(stated, url) {
|
|
|
31451
31889
|
* `headers`, `timeout`, `alwaysAllow` and `disabled` pass through, as Bob
|
|
31452
31890
|
* documents all of them, with prototype-pollution keys dropped at every
|
|
31453
31891
|
* nesting level (Bob spreads `env` and `headers` into the process environment
|
|
31454
|
-
* and the HTTP requests).
|
|
31892
|
+
* and the HTTP requests). The canonical `oauth` object is flattened onto the
|
|
31893
|
+
* entry as `oauth: true` plus `clientId` / `clientSecret` / `scope`, the keys
|
|
31894
|
+
* Bob reads; a boolean `oauth` passes through as Bob's own on/off switch.
|
|
31455
31895
|
*
|
|
31456
31896
|
* Bob Shell documents the same file with an `httpURL` key instead of
|
|
31457
31897
|
* `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
|
|
@@ -31517,10 +31957,16 @@ function convertToBobFormat(mcpServers, logger) {
|
|
|
31517
31957
|
converted.command = command;
|
|
31518
31958
|
if (args.length > 0) converted.args = args;
|
|
31519
31959
|
}
|
|
31520
|
-
|
|
31960
|
+
const { oauth, ...passthrough } = rest;
|
|
31961
|
+
for (const [key, value] of Object.entries(passthrough)) {
|
|
31521
31962
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
31522
31963
|
converted[key] = omitPrototypePollutionKeysDeep(value);
|
|
31523
31964
|
}
|
|
31965
|
+
if (isRecord$1(oauth)) flattenOauthForBob({
|
|
31966
|
+
oauth,
|
|
31967
|
+
converted
|
|
31968
|
+
});
|
|
31969
|
+
else if (oauth !== void 0) converted.oauth = oauth;
|
|
31524
31970
|
result[serverName] = converted;
|
|
31525
31971
|
}
|
|
31526
31972
|
return result;
|
|
@@ -31530,9 +31976,10 @@ function convertToBobFormat(mcpServers, logger) {
|
|
|
31530
31976
|
* (`type: "streamable-http"` + `url`) is already canonical and passes through;
|
|
31531
31977
|
* the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
|
|
31532
31978
|
* `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
|
|
31533
|
-
* on the next generate.
|
|
31534
|
-
*
|
|
31535
|
-
*
|
|
31979
|
+
* on the next generate. Bob's flat OAuth keys are gathered back into the
|
|
31980
|
+
* canonical `oauth` object (see {@link nestOauthFromBob}). Every other field
|
|
31981
|
+
* passes through with its prototype-pollution keys dropped at every nesting
|
|
31982
|
+
* level, mirroring the generate side's handling of `env` / `headers`.
|
|
31536
31983
|
*/
|
|
31537
31984
|
function convertFromBobFormat(mcpServers) {
|
|
31538
31985
|
if (!isMcpServers(mcpServers)) return {};
|
|
@@ -31553,6 +32000,7 @@ function convertFromBobFormat(mcpServers) {
|
|
|
31553
32000
|
converted.url = httpURL;
|
|
31554
32001
|
converted.type = "http";
|
|
31555
32002
|
} else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
|
|
32003
|
+
nestOauthFromBob(converted);
|
|
31556
32004
|
result[serverName] = converted;
|
|
31557
32005
|
}
|
|
31558
32006
|
return result;
|
|
@@ -32577,7 +33025,10 @@ function asContinueRemoteType(stated, url) {
|
|
|
32577
33025
|
* file with a strict union — one server that matches neither shape (an
|
|
32578
33026
|
* unknown transport, a non-string `env` value) makes Continue drop every
|
|
32579
33027
|
* server in the file — so only the documented keys are emitted and anything
|
|
32580
|
-
* else
|
|
33028
|
+
* else is left out. The canonical keys a user would expect to take effect
|
|
33029
|
+
* (`cwd`, `timeout`, `oauth`) are dropped with a warning, since the JSON
|
|
33030
|
+
* schema has no place for them (the YAML block form does, but Rulesync writes
|
|
33031
|
+
* the JSON file); rulesync-only fields are dropped silently. `envFile` is
|
|
32581
33032
|
* accepted by Continue's schema but ignored with a warning at load time, so
|
|
32582
33033
|
* it is dropped here with a warning instead of being written as if it worked.
|
|
32583
33034
|
*
|
|
@@ -32613,6 +33064,17 @@ function convertToContinueFormat(mcpServers, logger) {
|
|
|
32613
33064
|
}
|
|
32614
33065
|
return result;
|
|
32615
33066
|
}
|
|
33067
|
+
/**
|
|
33068
|
+
* Warn for each canonical key Continue's JSON MCP schema cannot carry, so a
|
|
33069
|
+
* dropped `cwd`, `timeout` or `oauth` does not go unnoticed in a config that
|
|
33070
|
+
* otherwise looks complete.
|
|
33071
|
+
*/
|
|
33072
|
+
function warnUnsupportedContinueKeys({ serverName, serverConfig, keys, logger }) {
|
|
33073
|
+
for (const key of keys) {
|
|
33074
|
+
if (serverConfig[key] === void 0) continue;
|
|
33075
|
+
logger?.warn(`Continue's MCP config has no "${key}" field, so the ${key} of server ${quoteValueForWarning(serverName)} was not written.`);
|
|
33076
|
+
}
|
|
33077
|
+
}
|
|
32616
33078
|
function convertRemoteServer({ serverName, serverConfig, logger }) {
|
|
32617
33079
|
const url = resolveRemoteMcpUrl(serverConfig);
|
|
32618
33080
|
if (!url) {
|
|
@@ -32640,6 +33102,12 @@ function convertRemoteServer({ serverName, serverConfig, logger }) {
|
|
|
32640
33102
|
url
|
|
32641
33103
|
};
|
|
32642
33104
|
if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
|
|
33105
|
+
warnUnsupportedContinueKeys({
|
|
33106
|
+
serverName,
|
|
33107
|
+
serverConfig,
|
|
33108
|
+
keys: ["timeout", "oauth"],
|
|
33109
|
+
logger
|
|
33110
|
+
});
|
|
32643
33111
|
return converted;
|
|
32644
33112
|
}
|
|
32645
33113
|
function convertStdioServer({ serverName, serverConfig, logger }) {
|
|
@@ -32660,6 +33128,12 @@ function convertStdioServer({ serverName, serverConfig, logger }) {
|
|
|
32660
33128
|
if (args.length > 0) converted.args = args;
|
|
32661
33129
|
if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
|
|
32662
33130
|
if (typeof serverConfig.envFile === "string") logger?.warn(`Continue ignores "envFile" for MCP servers, so the envFile of server ${quoteValueForWarning(serverName)} was not written; put the variables in "env" instead.`);
|
|
33131
|
+
warnUnsupportedContinueKeys({
|
|
33132
|
+
serverName,
|
|
33133
|
+
serverConfig,
|
|
33134
|
+
keys: ["cwd", "timeout"],
|
|
33135
|
+
logger
|
|
33136
|
+
});
|
|
32663
33137
|
return converted;
|
|
32664
33138
|
}
|
|
32665
33139
|
/**
|
|
@@ -34635,28 +35109,60 @@ function resolveHermesTimeout(config) {
|
|
|
34635
35109
|
if (typeof config.networkTimeout === "number") return config.networkTimeout;
|
|
34636
35110
|
}
|
|
34637
35111
|
/**
|
|
34638
|
-
*
|
|
34639
|
-
*
|
|
34640
|
-
*
|
|
34641
|
-
* `
|
|
34642
|
-
*
|
|
34643
|
-
*
|
|
34644
|
-
|
|
34645
|
-
|
|
35112
|
+
* String-valued `oauth` keys Hermes reads (`tools/mcp_oauth.py`,
|
|
35113
|
+
* `tools/mcp_oauth_device.py`, v0.21.3): the callback overrides, the
|
|
35114
|
+
* pre-registered client, the CIMD document override, the DCR registration
|
|
35115
|
+
* fields, the token-request `user_agent`, the login `flow` (`device` for
|
|
35116
|
+
* RFC 8628) and the space-separated `scope` string sent on registration and
|
|
35117
|
+
* device authorization.
|
|
35118
|
+
*/
|
|
35119
|
+
const HERMES_OAUTH_STRING_KEYS = [
|
|
35120
|
+
"redirect_uri",
|
|
35121
|
+
"redirect_host",
|
|
35122
|
+
"client_id",
|
|
35123
|
+
"client_secret",
|
|
35124
|
+
"client_name",
|
|
35125
|
+
"client_metadata_url",
|
|
35126
|
+
"token_endpoint_auth_method",
|
|
35127
|
+
"application_type",
|
|
35128
|
+
"user_agent",
|
|
35129
|
+
"flow",
|
|
35130
|
+
"scope"
|
|
35131
|
+
];
|
|
35132
|
+
/**
|
|
35133
|
+
* Copies the `oauth` mapping of a server, keeping only the keys Hermes reads.
|
|
35134
|
+
*
|
|
35135
|
+
* Hermes reads a single `scope` string, never a `scopes` list; earlier Rulesync
|
|
35136
|
+
* versions wrote `scopes` regardless, which Hermes silently ignored. A `scopes`
|
|
35137
|
+
* list is therefore folded into `scope` (space-separated, the OAuth wire form)
|
|
35138
|
+
* when no `scope` is given, in both directions, so an authored list finally
|
|
35139
|
+
* reaches the authorization server and an old `config.yaml` imports cleanly.
|
|
34646
35140
|
*/
|
|
34647
35141
|
function copyHermesOauth(source) {
|
|
34648
35142
|
if (!isRecord$1(source)) return;
|
|
34649
35143
|
const oauth = {};
|
|
34650
|
-
for (const key of [
|
|
34651
|
-
|
|
34652
|
-
|
|
34653
|
-
|
|
34654
|
-
"client_secret"
|
|
34655
|
-
]) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
34656
|
-
if (typeof source.redirect_port === "number") oauth.redirect_port = source.redirect_port;
|
|
34657
|
-
if (isStringArray$2(source.scopes)) oauth.scopes = source.scopes;
|
|
35144
|
+
for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
35145
|
+
if (typeof oauth.scope !== "string" && isStringArray$2(source.scopes)) oauth.scope = source.scopes.join(" ");
|
|
35146
|
+
for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
|
|
35147
|
+
if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
|
|
34658
35148
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
34659
35149
|
}
|
|
35150
|
+
const HERMES_OPAQUE_MAPPING_KEYS = [
|
|
35151
|
+
"sampling",
|
|
35152
|
+
"elicitation",
|
|
35153
|
+
"identity_header",
|
|
35154
|
+
"lifecycle"
|
|
35155
|
+
];
|
|
35156
|
+
/**
|
|
35157
|
+
* Copies the advanced Hermes-recognized per-server fields that have no canonical
|
|
35158
|
+
* alias — `auth` (`oauth` for OAuth 2.1/PKCE), mTLS `client_cert` (string PEM
|
|
35159
|
+
* path, or `[cert, key]`/`[cert, key, password]` list) and `client_key`,
|
|
35160
|
+
* `connect_timeout` (seconds), `supports_parallel_tool_calls`, `protocol`,
|
|
35161
|
+
* `lazy`, `keepalive_interval`, `elicitation`, `trust`, and `identity_header` —
|
|
35162
|
+
* verbatim from `source` to `target`. Field names are identical on both sides
|
|
35163
|
+
* (the canonical `McpServerSchema` is a `looseObject`), so this serves export
|
|
35164
|
+
* and import alike. See the Hermes mcp-config-reference.
|
|
35165
|
+
*/
|
|
34660
35166
|
function copyHermesAdvancedFields(source, target) {
|
|
34661
35167
|
let copied = false;
|
|
34662
35168
|
if (typeof source.auth === "string") {
|
|
@@ -34688,6 +35194,14 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
34688
35194
|
target[key] = source[key];
|
|
34689
35195
|
copied = true;
|
|
34690
35196
|
}
|
|
35197
|
+
if (typeof source.protocol === "string") {
|
|
35198
|
+
target.protocol = source.protocol;
|
|
35199
|
+
copied = true;
|
|
35200
|
+
}
|
|
35201
|
+
if (typeof source.lazy === "boolean") {
|
|
35202
|
+
target.lazy = source.lazy;
|
|
35203
|
+
copied = true;
|
|
35204
|
+
}
|
|
34691
35205
|
if (typeof source.ssl_verify === "boolean" || typeof source.ssl_verify === "string") {
|
|
34692
35206
|
target.ssl_verify = source.ssl_verify;
|
|
34693
35207
|
copied = true;
|
|
@@ -34696,25 +35210,20 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
34696
35210
|
target.skip_preflight = source.skip_preflight;
|
|
34697
35211
|
copied = true;
|
|
34698
35212
|
}
|
|
34699
|
-
if (isPlainObject$1(source.sampling)) {
|
|
34700
|
-
target.sampling = omitPrototypePollutionKeys(structuredClone(source.sampling));
|
|
34701
|
-
copied = true;
|
|
34702
|
-
}
|
|
34703
35213
|
if (typeof source.keepalive_interval === "number") {
|
|
34704
35214
|
target.keepalive_interval = source.keepalive_interval;
|
|
34705
35215
|
copied = true;
|
|
34706
35216
|
}
|
|
34707
|
-
if (isPlainObject$1(source.elicitation)) {
|
|
34708
|
-
target.elicitation = omitPrototypePollutionKeys(structuredClone(source.elicitation));
|
|
34709
|
-
copied = true;
|
|
34710
|
-
}
|
|
34711
35217
|
if (typeof source.trust === "string") {
|
|
34712
35218
|
target.trust = source.trust;
|
|
34713
35219
|
copied = true;
|
|
34714
35220
|
}
|
|
34715
|
-
|
|
34716
|
-
|
|
34717
|
-
|
|
35221
|
+
for (const key of HERMES_OPAQUE_MAPPING_KEYS) {
|
|
35222
|
+
const mapping = source[key];
|
|
35223
|
+
if (isPlainObject$1(mapping)) {
|
|
35224
|
+
target[key] = omitPrototypePollutionKeys(structuredClone(mapping));
|
|
35225
|
+
copied = true;
|
|
35226
|
+
}
|
|
34718
35227
|
}
|
|
34719
35228
|
return copied;
|
|
34720
35229
|
}
|
|
@@ -35498,6 +36007,7 @@ function toKimiCodeServer({ name, server, logger }) {
|
|
|
35498
36007
|
"headers",
|
|
35499
36008
|
"bearerTokenEnvVar",
|
|
35500
36009
|
"enabled",
|
|
36010
|
+
"deferred",
|
|
35501
36011
|
"startupTimeoutMs",
|
|
35502
36012
|
"toolTimeoutMs",
|
|
35503
36013
|
"enabledTools",
|
|
@@ -36155,7 +36665,12 @@ const OpencodeMcpRemoteServerSchema = zod_mini.z.looseObject({
|
|
|
36155
36665
|
headers: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.string())),
|
|
36156
36666
|
enabled: zod_mini.z._default(zod_mini.z.boolean(), true)
|
|
36157
36667
|
});
|
|
36158
|
-
const OPENCODE_PASSTHROUGH_SERVER_FIELDS = [
|
|
36668
|
+
const OPENCODE_PASSTHROUGH_SERVER_FIELDS = [
|
|
36669
|
+
"timeout",
|
|
36670
|
+
"oauth",
|
|
36671
|
+
"protocol",
|
|
36672
|
+
"codemode"
|
|
36673
|
+
];
|
|
36159
36674
|
const OPENCODE_MCP_TRANSPORT_KEYS = [
|
|
36160
36675
|
...Object.keys(OpencodeMcpLocalServerSchema.def.shape),
|
|
36161
36676
|
...Object.keys(OpencodeMcpRemoteServerSchema.def.shape),
|
|
@@ -36297,7 +36812,7 @@ function warnAboutToggleDroppedKeys(serverName, serverConfig, logger) {
|
|
|
36297
36812
|
* - env -> environment
|
|
36298
36813
|
* - disabled -> enabled (inverted)
|
|
36299
36814
|
* - enabledTools/disabledTools -> top-level tools map (with server name prefix)
|
|
36300
|
-
* - OpenCode-supported extras (timeout, oauth) -> passed through verbatim
|
|
36815
|
+
* - OpenCode-supported extras (timeout, oauth, protocol, codemode) -> passed through verbatim
|
|
36301
36816
|
*/
|
|
36302
36817
|
function convertServerToOpencodeFormat(serverName, serverConfig, existingEntry, logger) {
|
|
36303
36818
|
const serverRecord = serverConfig;
|
|
@@ -39290,7 +39805,7 @@ var ToolPermissions = class extends ToolFile {
|
|
|
39290
39805
|
* builtin of that name). It lives in the shared Amp settings file:
|
|
39291
39806
|
* `.amp/settings.json` (project) and `~/.config/amp/settings.json` (global).
|
|
39292
39807
|
*
|
|
39293
|
-
* Reference: https://ampcode.com/
|
|
39808
|
+
* Reference: https://ampcode.com/docs/cli/settings ("amp.tools.disable").
|
|
39294
39809
|
*/
|
|
39295
39810
|
const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
|
|
39296
39811
|
/**
|
|
@@ -39301,19 +39816,26 @@ const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
|
|
|
39301
39816
|
* glob. Rules are evaluated **first-match-wins**. It lives in the same shared
|
|
39302
39817
|
* Amp settings file as `amp.tools.disable`.
|
|
39303
39818
|
*
|
|
39304
|
-
* `amp.permissions` is Amp's
|
|
39305
|
-
*
|
|
39306
|
-
* argument-specific `reject` rules (the simpler `amp.tools.disable` array
|
|
39307
|
-
* only disable whole tools).
|
|
39819
|
+
* `amp.permissions` is Amp's legacy/backwards-compat surface — it remains
|
|
39820
|
+
* functional in the shipped CLI and is the only place to express `allow`/`ask`
|
|
39821
|
+
* and argument-specific `reject` rules (the simpler `amp.tools.disable` array
|
|
39822
|
+
* can only disable whole tools). The web docs no longer describe it (the
|
|
39823
|
+
* permissions section only points at custom plugins), but the shipped CLI
|
|
39824
|
+
* still lists it in its built-in settings reference (`amp --help`) and
|
|
39825
|
+
* evaluates it through `amp permissions list|test`.
|
|
39308
39826
|
*
|
|
39309
|
-
* Reference: https://ampcode.com/
|
|
39827
|
+
* Reference: https://ampcode.com/docs/tools#permissions and
|
|
39828
|
+
* https://ampcode.com/docs/customize/plugins#example-plugin-permissions.
|
|
39310
39829
|
*/
|
|
39311
39830
|
const AMP_PERMISSIONS_KEY = "amp.permissions";
|
|
39312
39831
|
/**
|
|
39313
39832
|
* The `amp.guardedFiles.allowlist` array (file globs allowed without
|
|
39314
39833
|
* confirmation), `amp.dangerouslyAllowAll` boolean (disable all confirmation),
|
|
39315
39834
|
* and `amp.mcpPermissions` array — sibling settings authored through the `amp`
|
|
39316
|
-
* permissions override.
|
|
39835
|
+
* permissions override. Only `amp.mcpPermissions` is still on the web docs
|
|
39836
|
+
* (https://ampcode.com/docs/cli/settings); the other two, like
|
|
39837
|
+
* `amp.permissions`, survive only in the CLI's built-in settings reference
|
|
39838
|
+
* (see `AMP_PERMISSIONS_KEY`).
|
|
39317
39839
|
*/
|
|
39318
39840
|
const AMP_GUARDED_FILES_ALLOWLIST_KEY = "amp.guardedFiles.allowlist";
|
|
39319
39841
|
const AMP_DANGEROUSLY_ALLOW_ALL_KEY = "amp.dangerouslyAllowAll";
|
|
@@ -42642,6 +43164,8 @@ const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
|
|
|
42642
43164
|
const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_EXTENDABLE_BASELINE_PROFILES);
|
|
42643
43165
|
const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
|
|
42644
43166
|
const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
|
|
43167
|
+
const CODEX_RETIRED_APPROVAL_POLICY = "untrusted";
|
|
43168
|
+
const CODEX_DEPRECATED_APPROVAL_POLICY = "on-failure";
|
|
42645
43169
|
const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
|
|
42646
43170
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
42647
43171
|
const CODEX_GIT_WRITE_RULES = { ".git/**": "write" };
|
|
@@ -42765,6 +43289,10 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
42765
43289
|
domainsHadUnknown
|
|
42766
43290
|
});
|
|
42767
43291
|
const override = extractCodexcliOverride(table);
|
|
43292
|
+
if (override.approval_policy === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43293
|
+
warnWithFallback(void 0, `${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())} sets approval_policy = "${CODEX_RETIRED_APPROVAL_POLICY}", which was retired in Codex 0.149.0 and makes Codex refuse to start. It was not imported; remove it from the file, or mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
|
|
43294
|
+
delete override.approval_policy;
|
|
43295
|
+
}
|
|
42768
43296
|
if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
|
|
42769
43297
|
if (defaultProfile === CODEX_DANGER_FULL_ACCESS_BASELINE) override.base_permission_profile = CODEX_DANGER_FULL_ACCESS_BASELINE;
|
|
42770
43298
|
const result = Object.keys(override).length > 0 ? {
|
|
@@ -43053,6 +43581,10 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
|
|
|
43053
43581
|
continue;
|
|
43054
43582
|
}
|
|
43055
43583
|
if (value === void 0) continue;
|
|
43584
|
+
if (key === "approval_policy" && !isWritableApprovalPolicy({
|
|
43585
|
+
value,
|
|
43586
|
+
logger
|
|
43587
|
+
})) continue;
|
|
43056
43588
|
if (key === "sandbox_mode" || key === "sandbox_workspace_write") logger?.warn(`Codex CLI permission override key "${key}" is deprecated. Codex prioritizes the legacy sandbox settings over permission profiles when both are present, so it disables the generated "${RULESYNC_PROFILE_NAME}" permissions profile. Use "base_permission_profile" and the shared "permission" block instead.`);
|
|
43057
43589
|
const existingValue = existing[key];
|
|
43058
43590
|
patch[key] = isPlainObject(existingValue) && isPlainObject(value) ? {
|
|
@@ -43060,12 +43592,37 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
|
|
|
43060
43592
|
...value
|
|
43061
43593
|
} : value;
|
|
43062
43594
|
}
|
|
43595
|
+
fillCodexcliDefaults({
|
|
43596
|
+
existing,
|
|
43597
|
+
patch,
|
|
43598
|
+
logger
|
|
43599
|
+
});
|
|
43600
|
+
return patch;
|
|
43601
|
+
}
|
|
43602
|
+
function fillCodexcliDefaults({ existing, patch, logger }) {
|
|
43063
43603
|
const defaults = {
|
|
43064
43604
|
approval_policy: CODEX_DEFAULT_APPROVAL_POLICY,
|
|
43065
43605
|
approvals_reviewer: CODEX_DEFAULT_APPROVALS_REVIEWER
|
|
43066
43606
|
};
|
|
43067
|
-
for (const [key, value] of Object.entries(defaults))
|
|
43068
|
-
|
|
43607
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
43608
|
+
if (patch[key] !== void 0) continue;
|
|
43609
|
+
if (existing[key] === void 0) {
|
|
43610
|
+
patch[key] = value;
|
|
43611
|
+
continue;
|
|
43612
|
+
}
|
|
43613
|
+
if (key === "approval_policy" && existing[key] === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43614
|
+
logger?.warn(`The existing Codex CLI config.toml sets approval_policy = "${CODEX_RETIRED_APPROVAL_POLICY}", which was retired in Codex 0.149.0 and makes Codex refuse to start, so it was replaced with "${value}". To keep the strict behavior, mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
|
|
43615
|
+
patch[key] = value;
|
|
43616
|
+
}
|
|
43617
|
+
}
|
|
43618
|
+
}
|
|
43619
|
+
function isWritableApprovalPolicy({ value, logger }) {
|
|
43620
|
+
if (value === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43621
|
+
logger?.warn(`Codex CLI permission override "approval_policy": "${CODEX_RETIRED_APPROVAL_POLICY}" was retired in Codex 0.149.0 and makes Codex refuse to start, so it was not written. Remove it from the override; to keep the strict behavior, mark the project untrusted in your user config instead ([projects."<path>"] trust_level = "${CODEX_RETIRED_APPROVAL_POLICY}").`);
|
|
43622
|
+
return false;
|
|
43623
|
+
}
|
|
43624
|
+
if (value === CODEX_DEPRECATED_APPROVAL_POLICY) logger?.warn(`Codex CLI permission override "approval_policy": "${CODEX_DEPRECATED_APPROVAL_POLICY}" is deprecated; Codex reads it as "${CODEX_DEFAULT_APPROVAL_POLICY}". Use "${CODEX_DEFAULT_APPROVAL_POLICY}" instead.`);
|
|
43625
|
+
return true;
|
|
43069
43626
|
}
|
|
43070
43627
|
function extractCodexcliOverride(table) {
|
|
43071
43628
|
const override = {};
|
|
@@ -45158,6 +45715,27 @@ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
|
|
|
45158
45715
|
* @see https://github.com/langchain-ai/deepagents `config_manifest.py`
|
|
45159
45716
|
*/
|
|
45160
45717
|
const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
|
|
45718
|
+
/**
|
|
45719
|
+
* `[mcp]` holds dcode's per-server trust for the MCP servers a project's
|
|
45720
|
+
* `.mcp.json` / `.deepagents/.mcp.json` declares. `load_mcp_server_trust_lists`
|
|
45721
|
+
* reads it from the user-level `config.toml` only — never from a project file —
|
|
45722
|
+
* so the global adapter is the one place rulesync can author it.
|
|
45723
|
+
*/
|
|
45724
|
+
const MCP_TABLE_KEY = "mcp";
|
|
45725
|
+
/**
|
|
45726
|
+
* Keys lifted back into the override on import. `disabled_project_servers` is
|
|
45727
|
+
* the deny list: a name on it is always rejected, over any approval or trust.
|
|
45728
|
+
*/
|
|
45729
|
+
const DEEPAGENTS_MCP_KEYS = ["disabled_project_servers"];
|
|
45730
|
+
const MCP_DISABLED_PROJECT_SERVERS_KEY = "disabled_project_servers";
|
|
45731
|
+
/**
|
|
45732
|
+
* dcode's own store of the project servers the user approved at the prompt,
|
|
45733
|
+
* each bound to a repository or worktree and a definition fingerprint. It is
|
|
45734
|
+
* machine-local state and a way to pre-approve a project's servers, so a
|
|
45735
|
+
* repository's `.rulesync/permissions.jsonc` must not reach it: generate drops
|
|
45736
|
+
* the key here, and import never lifts it (`DEEPAGENTS_MCP_KEYS`).
|
|
45737
|
+
*/
|
|
45738
|
+
const MCP_APPROVALS_KEY = "enabled_project_server_approvals";
|
|
45161
45739
|
const ALLOW_ALL_SENTINEL = "all";
|
|
45162
45740
|
const RECOMMENDED_SENTINEL = "recommended";
|
|
45163
45741
|
const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
|
|
@@ -45230,7 +45808,9 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
|
|
|
45230
45808
|
* lifted back into the override on import. The same override carries
|
|
45231
45809
|
* `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
|
|
45232
45810
|
* which decide whether the Python in a checked-out project's
|
|
45233
|
-
* `.deepagents/extensions/` is imported into the agent process
|
|
45811
|
+
* `.deepagents/extensions/` is imported into the agent process, and
|
|
45812
|
+
* `[mcp].disabled_project_servers`, the names of project MCP servers dcode
|
|
45813
|
+
* always rejects.
|
|
45234
45814
|
*
|
|
45235
45815
|
* On **import** the allowlist comes back as `bash` `allow` rules named by the
|
|
45236
45816
|
* executable — skipping any entry dcode could not match in the first place, so
|
|
@@ -45328,17 +45908,9 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
|
|
|
45328
45908
|
if (Object.keys(shell).length > 0) settings[SHELL_TABLE_KEY] = shell;
|
|
45329
45909
|
else delete settings[SHELL_TABLE_KEY];
|
|
45330
45910
|
}
|
|
45331
|
-
|
|
45332
|
-
if (isPlainObject$1(startupOverride) && Object.keys(startupOverride).length > 0) mergeStartupOverride({
|
|
45911
|
+
mergeDeepagentsOverrides({
|
|
45333
45912
|
settings,
|
|
45334
|
-
|
|
45335
|
-
filePath,
|
|
45336
|
-
logger
|
|
45337
|
-
});
|
|
45338
|
-
const extensionsOverride = config.deepagents?.extensions;
|
|
45339
|
-
if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
|
|
45340
|
-
settings,
|
|
45341
|
-
extensionsOverride,
|
|
45913
|
+
override: config.deepagents,
|
|
45342
45914
|
filePath,
|
|
45343
45915
|
logger
|
|
45344
45916
|
});
|
|
@@ -45370,10 +45942,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
|
|
|
45370
45942
|
extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
|
|
45371
45943
|
selfPath
|
|
45372
45944
|
});
|
|
45945
|
+
const mcpOverride = liftMcpOverride({
|
|
45946
|
+
mcp: isPlainObject$1(settings[MCP_TABLE_KEY]) ? settings[MCP_TABLE_KEY] : {},
|
|
45947
|
+
selfPath
|
|
45948
|
+
});
|
|
45373
45949
|
const result = { ...config };
|
|
45374
45950
|
const deepagents = {};
|
|
45375
45951
|
if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
|
|
45376
45952
|
if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
|
|
45953
|
+
if (Object.keys(mcpOverride).length > 0) deepagents.mcp = mcpOverride;
|
|
45377
45954
|
if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
|
|
45378
45955
|
return RulesyncPermissions.fromImportedFileContent({
|
|
45379
45956
|
outputRoot: getDeepagentsRulesyncOutputRoot({
|
|
@@ -45481,7 +46058,7 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
|
|
|
45481
46058
|
* read — a wrapper rather than the bare value, so a legitimately falsy setting
|
|
45482
46059
|
* is not mistaken for a rejection.
|
|
45483
46060
|
*/
|
|
45484
|
-
function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
|
|
46061
|
+
function liftOverrideTable({ table, tableKey, keys, normalize, selfPath, describeRejection = `deepagents-cli falls back to its own default for a '[${tableKey}]' value it cannot read` }) {
|
|
45485
46062
|
const override = {};
|
|
45486
46063
|
const rejected = [];
|
|
45487
46064
|
for (const key of keys) {
|
|
@@ -45494,7 +46071,7 @@ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
|
|
|
45494
46071
|
if (normalized) override[key] = normalized.value;
|
|
45495
46072
|
else rejected.push(`${key} = ${JSON.stringify(value)}`);
|
|
45496
46073
|
}
|
|
45497
|
-
if (rejected.length > 0) warnWithFallback(void 0,
|
|
46074
|
+
if (rejected.length > 0) warnWithFallback(void 0, `${describeRejection}, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
|
|
45498
46075
|
return override;
|
|
45499
46076
|
}
|
|
45500
46077
|
/**
|
|
@@ -45542,6 +46119,37 @@ function liftExtensionsOverride({ extensions, selfPath }) {
|
|
|
45542
46119
|
});
|
|
45543
46120
|
}
|
|
45544
46121
|
/**
|
|
46122
|
+
* Read a `[mcp]` name list the way `_toml_str_list` reads it: a bare string
|
|
46123
|
+
* is split on commas, names are trimmed and empty ones dropped, and a
|
|
46124
|
+
* non-string element of a list is dropped while the names around it survive.
|
|
46125
|
+
* Anything else (a number, a table, a bool) is a value dcode cannot read as
|
|
46126
|
+
* names at all, so `null` says to leave it behind.
|
|
46127
|
+
*/
|
|
46128
|
+
function readMcpServerNames(value) {
|
|
46129
|
+
if (typeof value === "string") return value.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
46130
|
+
if (!Array.isArray(value)) return null;
|
|
46131
|
+
return value.filter((name) => typeof name === "string").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
46132
|
+
}
|
|
46133
|
+
/**
|
|
46134
|
+
* Lift the `[mcp]` keys rulesync models back into the `deepagents` override.
|
|
46135
|
+
* The deny list comes back as the array dcode resolves it to, so the
|
|
46136
|
+
* comma-separated string form it also accepts does not survive as one bogus
|
|
46137
|
+
* name in a permissions file the canonical schema types as `string[]`.
|
|
46138
|
+
*/
|
|
46139
|
+
function liftMcpOverride({ mcp, selfPath }) {
|
|
46140
|
+
return liftOverrideTable({
|
|
46141
|
+
table: mcp,
|
|
46142
|
+
tableKey: MCP_TABLE_KEY,
|
|
46143
|
+
keys: DEEPAGENTS_MCP_KEYS,
|
|
46144
|
+
normalize: ({ value }) => {
|
|
46145
|
+
const names = readMcpServerNames(value);
|
|
46146
|
+
return names ? { value: names } : null;
|
|
46147
|
+
},
|
|
46148
|
+
selfPath,
|
|
46149
|
+
describeRejection: `deepagents-cli treats a '[${MCP_TABLE_KEY}]' value it cannot read as a broken trust policy and rejects every project MCP server until it is fixed`
|
|
46150
|
+
});
|
|
46151
|
+
}
|
|
46152
|
+
/**
|
|
45545
46153
|
* Merge one block of the `deepagents` override into its `config.toml` table,
|
|
45546
46154
|
* preserving every other key of that table. A table that is not a table at all
|
|
45547
46155
|
* is something rulesync did not write and cannot merge into, so it is left
|
|
@@ -45590,6 +46198,38 @@ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, lo
|
|
|
45590
46198
|
if (unknownKeys.length === 0) return;
|
|
45591
46199
|
warnWithFallback(logger, `The deepagents ${tableKey} override wrote ${unknownKeys.join(", ")} into ${filePath} unchecked — rulesync does not know what those keys grant, and that file is your global deepagents-cli config.`);
|
|
45592
46200
|
}
|
|
46201
|
+
/** An override block with something in it to write. */
|
|
46202
|
+
function isSet(block) {
|
|
46203
|
+
return isPlainObject$1(block) && Object.keys(block).length > 0;
|
|
46204
|
+
}
|
|
46205
|
+
/**
|
|
46206
|
+
* Merge each block of the `deepagents` override into its `config.toml` table.
|
|
46207
|
+
* A block that is absent, not a table, or empty has nothing to write and is
|
|
46208
|
+
* skipped without touching the table it would have gone into.
|
|
46209
|
+
*/
|
|
46210
|
+
function mergeDeepagentsOverrides({ settings, override, filePath, logger }) {
|
|
46211
|
+
const startupOverride = override?.startup;
|
|
46212
|
+
if (isSet(startupOverride)) mergeStartupOverride({
|
|
46213
|
+
settings,
|
|
46214
|
+
startupOverride,
|
|
46215
|
+
filePath,
|
|
46216
|
+
logger
|
|
46217
|
+
});
|
|
46218
|
+
const extensionsOverride = override?.extensions;
|
|
46219
|
+
if (isSet(extensionsOverride)) mergeExtensionsOverride({
|
|
46220
|
+
settings,
|
|
46221
|
+
extensionsOverride,
|
|
46222
|
+
filePath,
|
|
46223
|
+
logger
|
|
46224
|
+
});
|
|
46225
|
+
const mcpOverride = override?.mcp;
|
|
46226
|
+
if (isSet(mcpOverride)) mergeMcpOverride({
|
|
46227
|
+
settings,
|
|
46228
|
+
mcpOverride,
|
|
46229
|
+
filePath,
|
|
46230
|
+
logger
|
|
46231
|
+
});
|
|
46232
|
+
}
|
|
45593
46233
|
/**
|
|
45594
46234
|
* Merge the `deepagents.startup` override into `[startup]`.
|
|
45595
46235
|
*/
|
|
@@ -45623,6 +46263,40 @@ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logge
|
|
|
45623
46263
|
});
|
|
45624
46264
|
}
|
|
45625
46265
|
/**
|
|
46266
|
+
* Merge the `deepagents.mcp` override into `[mcp]`. The approval store is
|
|
46267
|
+
* dropped (see `MCP_APPROVALS_KEY`); the deprecated flat
|
|
46268
|
+
* `enabled_project_servers` is not modeled but passes through like any other
|
|
46269
|
+
* unknown key, named by the unchecked-key warning — dcode ignores it anyway.
|
|
46270
|
+
*/
|
|
46271
|
+
function mergeMcpOverride({ settings, mcpOverride, filePath, logger }) {
|
|
46272
|
+
mergeOverrideTable({
|
|
46273
|
+
settings,
|
|
46274
|
+
tableKey: MCP_TABLE_KEY,
|
|
46275
|
+
override: mcpOverride,
|
|
46276
|
+
knownKeys: DEEPAGENTS_MCP_KEYS,
|
|
46277
|
+
isDroppedKey: (key) => key === MCP_APPROVALS_KEY,
|
|
46278
|
+
warnAboutRelaxations: warnAboutMcpRelaxations,
|
|
46279
|
+
filePath,
|
|
46280
|
+
logger
|
|
46281
|
+
});
|
|
46282
|
+
}
|
|
46283
|
+
/**
|
|
46284
|
+
* Warn when the `deepagents` mcp override re-enables project MCP servers the
|
|
46285
|
+
* machine's global config had rejected. The deny list is written as a whole,
|
|
46286
|
+
* so a shorter list from a repository's `.rulesync/permissions.jsonc` would
|
|
46287
|
+
* otherwise silently lift a rejection the user had put in place — the very
|
|
46288
|
+
* fail-open dcode's own env merge refuses to allow.
|
|
46289
|
+
*/
|
|
46290
|
+
function warnAboutMcpRelaxations({ override, previous, filePath, logger }) {
|
|
46291
|
+
if (override[MCP_APPROVALS_KEY] !== void 0) warnWithFallback(logger, `The deepagents mcp override's '${MCP_APPROVALS_KEY}' was not written to ${filePath}: dcode builds that approval store itself from the project MCP servers you accept at launch, so a permissions file cannot pre-approve them.`);
|
|
46292
|
+
const written = override[MCP_DISABLED_PROJECT_SERVERS_KEY];
|
|
46293
|
+
if (written === void 0 || written === null) return;
|
|
46294
|
+
const kept = new Set(readMcpServerNames(written) ?? []);
|
|
46295
|
+
const reEnabled = (readMcpServerNames(previous[MCP_DISABLED_PROJECT_SERVERS_KEY]) ?? []).filter((name) => !kept.has(name));
|
|
46296
|
+
if (reEnabled.length === 0) return;
|
|
46297
|
+
warnWithFallback(logger, `The deepagents mcp override removed ${reEnabled.map((name) => JSON.stringify(name)).join(", ")} from [${MCP_TABLE_KEY}].${MCP_DISABLED_PROJECT_SERVERS_KEY} in ${filePath}, which is your global deepagents-cli config: dcode no longer rejects ${reEnabled.length === 1 ? "that project MCP server" : "those project MCP servers"} outright, for every project on this machine, not just this one.`);
|
|
46298
|
+
}
|
|
46299
|
+
/**
|
|
45626
46300
|
* Name the value an override key replaces, so a setting the user had turned
|
|
45627
46301
|
* down is visible as such rather than reported as a bare new value.
|
|
45628
46302
|
*/
|
|
@@ -51400,6 +52074,18 @@ const TAKT_RUNTIME_TARGETS_KEY = "targets";
|
|
|
51400
52074
|
const TAKT_RUNTIME_AUTO_ROUTING_KEY = "auto_routing";
|
|
51401
52075
|
const TAKT_RUNTIME_PROFILE_KEY = "profile";
|
|
51402
52076
|
const TAKT_RUNTIME_OPTIONS_KEY = "options";
|
|
52077
|
+
const TAKT_RUNTIME_ASSIGNMENTS_KEY = "assignments";
|
|
52078
|
+
const TAKT_RUNTIME_LADDER_KEY = "ladder";
|
|
52079
|
+
const TAKT_RUNTIME_EXTENDS_KEY = "extends";
|
|
52080
|
+
const TAKT_RUNTIME_ROUTER_PROFILE_KEY = "router_profile";
|
|
52081
|
+
const TAKT_RUNTIME_POOLS_KEY = "pools";
|
|
52082
|
+
const TAKT_RUNTIME_CANDIDATES_KEY = "candidates";
|
|
52083
|
+
const TAKT_RUNTIME_FALLBACK_PROFILE_KEY = "fallback_profile";
|
|
52084
|
+
/** The one `targets` map that routes companion reviewers rather than agents. */
|
|
52085
|
+
const TAKT_RUNTIME_COMPANIONS_TARGET_KEY = "companions";
|
|
52086
|
+
const TAKT_RUNTIME_COMPANION_KEY = "companion";
|
|
52087
|
+
const TAKT_RUNTIME_ENABLED_KEY = "enabled";
|
|
52088
|
+
const TAKT_DEFAULT_COMPANION_ENABLED = false;
|
|
51403
52089
|
const TAKT_SECURITY_POLICIES = {
|
|
51404
52090
|
workflow_arpeggio: [
|
|
51405
52091
|
"custom_data_source_modules",
|
|
@@ -51653,21 +52339,156 @@ function runtimeProfiles(runtime) {
|
|
|
51653
52339
|
const profiles = provider[TAKT_RUNTIME_PROFILES_KEY];
|
|
51654
52340
|
return isPlainObject$1(profiles) ? profiles : {};
|
|
51655
52341
|
}
|
|
52342
|
+
/** A `targets` map (`personas`, `tags`, …) with at least one non-empty entry. */
|
|
52343
|
+
function hasTargetContent(targets) {
|
|
52344
|
+
return isPlainObject$1(targets) && Object.values(targets).some(hasEntries);
|
|
52345
|
+
}
|
|
52346
|
+
/** The profile names an assignment points at: `profile` plus every `ladder` rung. */
|
|
52347
|
+
function assignmentProfileNames(assignment) {
|
|
52348
|
+
if (!isPlainObject$1(assignment)) return [];
|
|
52349
|
+
const names = [];
|
|
52350
|
+
const profile = assignment[TAKT_RUNTIME_PROFILE_KEY];
|
|
52351
|
+
if (typeof profile === "string") names.push(profile);
|
|
52352
|
+
const ladder = assignment[TAKT_RUNTIME_LADDER_KEY];
|
|
52353
|
+
if (Array.isArray(ladder)) names.push(...ladder.filter((rung) => typeof rung === "string"));
|
|
52354
|
+
return names;
|
|
52355
|
+
}
|
|
52356
|
+
/** The assignments under a `targets` map, split by whether they route companions. */
|
|
52357
|
+
function targetAssignments(targets, { companions }) {
|
|
52358
|
+
if (!isPlainObject$1(targets)) return [];
|
|
52359
|
+
return Object.entries(targets).filter(([name]) => name === TAKT_RUNTIME_COMPANIONS_TARGET_KEY === companions).flatMap(([, targetMap]) => isPlainObject$1(targetMap) ? Object.values(targetMap) : []);
|
|
52360
|
+
}
|
|
52361
|
+
/**
|
|
52362
|
+
* Record the profiles a `defaults` + `targets` pair (the top-level provider
|
|
52363
|
+
* section or one named assignment set) reaches, keeping companion roots apart
|
|
52364
|
+
* from the roots agents use.
|
|
52365
|
+
*/
|
|
52366
|
+
function addAssignmentSetRoots({ set, companionRoots, agentRoots }) {
|
|
52367
|
+
for (const name of assignmentProfileNames(set[TAKT_RUNTIME_DEFAULTS_KEY])) agentRoots.add(name);
|
|
52368
|
+
const targets = set[TAKT_RUNTIME_TARGETS_KEY];
|
|
52369
|
+
for (const assignment of targetAssignments(targets, { companions: false })) for (const name of assignmentProfileNames(assignment)) agentRoots.add(name);
|
|
52370
|
+
for (const assignment of targetAssignments(targets, { companions: true })) for (const name of assignmentProfileNames(assignment)) companionRoots.add(name);
|
|
52371
|
+
}
|
|
52372
|
+
/** The profiles `auto_routing` reaches: the router plus every pool candidate/fallback. */
|
|
52373
|
+
function addAutoRoutingRoots(autoRouting, roots) {
|
|
52374
|
+
if (!isPlainObject$1(autoRouting)) return;
|
|
52375
|
+
const routerProfile = autoRouting[TAKT_RUNTIME_ROUTER_PROFILE_KEY];
|
|
52376
|
+
if (typeof routerProfile === "string") roots.add(routerProfile);
|
|
52377
|
+
const pools = autoRouting[TAKT_RUNTIME_POOLS_KEY];
|
|
52378
|
+
if (!isPlainObject$1(pools)) return;
|
|
52379
|
+
for (const pool of Object.values(pools)) {
|
|
52380
|
+
if (!isPlainObject$1(pool)) continue;
|
|
52381
|
+
const candidates = pool[TAKT_RUNTIME_CANDIDATES_KEY];
|
|
52382
|
+
for (const candidate of Array.isArray(candidates) ? candidates : []) for (const name of assignmentProfileNames(candidate)) roots.add(name);
|
|
52383
|
+
const fallback = pool[TAKT_RUNTIME_FALLBACK_PROFILE_KEY];
|
|
52384
|
+
if (typeof fallback === "string") roots.add(fallback);
|
|
52385
|
+
}
|
|
52386
|
+
}
|
|
52387
|
+
/** Every profile reachable from `roots` by following `extends`. */
|
|
52388
|
+
function profileClosure(profiles, roots) {
|
|
52389
|
+
const closure = /* @__PURE__ */ new Set();
|
|
52390
|
+
const visit = (name) => {
|
|
52391
|
+
if (closure.has(name)) return;
|
|
52392
|
+
closure.add(name);
|
|
52393
|
+
const profile = profiles[name];
|
|
52394
|
+
const parent = isPlainObject$1(profile) ? profile[TAKT_RUNTIME_EXTENDS_KEY] : void 0;
|
|
52395
|
+
if (typeof parent === "string") visit(parent);
|
|
52396
|
+
};
|
|
52397
|
+
for (const root of roots) visit(root);
|
|
52398
|
+
return closure;
|
|
52399
|
+
}
|
|
52400
|
+
/** The `targets` map minus its `companions` entry (`undefined` when not a map). */
|
|
52401
|
+
function withoutCompanionTargets(targets) {
|
|
52402
|
+
if (!isPlainObject$1(targets)) return;
|
|
52403
|
+
const { [TAKT_RUNTIME_COMPANIONS_TARGET_KEY]: _companions, ...remaining } = targets;
|
|
52404
|
+
return remaining;
|
|
52405
|
+
}
|
|
52406
|
+
/**
|
|
52407
|
+
* The named assignment sets with their companion targets removed; a set left
|
|
52408
|
+
* with neither `defaults` nor a non-empty `targets` map is dropped, and
|
|
52409
|
+
* `undefined` means none survived.
|
|
52410
|
+
*/
|
|
52411
|
+
function withoutCompanionAssignments(assignments) {
|
|
52412
|
+
if (!isPlainObject$1(assignments)) return;
|
|
52413
|
+
const remaining = {};
|
|
52414
|
+
for (const [name, set] of Object.entries(assignments)) {
|
|
52415
|
+
if (!isPlainObject$1(set)) continue;
|
|
52416
|
+
const defaults = set[TAKT_RUNTIME_DEFAULTS_KEY];
|
|
52417
|
+
const targets = withoutCompanionTargets(set[TAKT_RUNTIME_TARGETS_KEY]);
|
|
52418
|
+
const keepTargets = hasTargetContent(targets);
|
|
52419
|
+
if (defaults === void 0 && !keepTargets) continue;
|
|
52420
|
+
remaining[name] = {
|
|
52421
|
+
...defaults === void 0 ? {} : { [TAKT_RUNTIME_DEFAULTS_KEY]: defaults },
|
|
52422
|
+
...keepTargets ? { [TAKT_RUNTIME_TARGETS_KEY]: targets } : {}
|
|
52423
|
+
};
|
|
52424
|
+
}
|
|
52425
|
+
return Object.keys(remaining).length > 0 ? remaining : void 0;
|
|
52426
|
+
}
|
|
52427
|
+
/** Whether the section (or any of its named assignment sets) routes companions at all. */
|
|
52428
|
+
function hasCompanionTargets(section) {
|
|
52429
|
+
const routesCompanions = (set) => {
|
|
52430
|
+
const targets = isPlainObject$1(set) ? set[TAKT_RUNTIME_TARGETS_KEY] : void 0;
|
|
52431
|
+
return isPlainObject$1(targets) && targets[TAKT_RUNTIME_COMPANIONS_TARGET_KEY] !== void 0;
|
|
52432
|
+
};
|
|
52433
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52434
|
+
return routesCompanions(section) || isPlainObject$1(assignments) && Object.values(assignments).some(routesCompanions);
|
|
52435
|
+
}
|
|
52436
|
+
/**
|
|
52437
|
+
* The `provider:` section as Takt itself evaluates it, mirroring upstream's
|
|
52438
|
+
* `getEffectiveProviderSection`: unless `companion.enabled` is `true`
|
|
52439
|
+
* (companions are opt-in), the `targets.companions` maps — top-level and inside
|
|
52440
|
+
* every named assignment set — are dropped, along with each profile that only a
|
|
52441
|
+
* companion target reaches through `extends`. A profile an agent assignment
|
|
52442
|
+
* (`defaults`, the other `targets` maps, `auto_routing`) also reaches is kept.
|
|
52443
|
+
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/schema.ts
|
|
52444
|
+
*/
|
|
52445
|
+
function effectiveRuntimeProviderSection(runtime) {
|
|
52446
|
+
const section = runtime[TAKT_RUNTIME_PROVIDER_KEY];
|
|
52447
|
+
if (!isPlainObject$1(section)) return;
|
|
52448
|
+
const companion = runtime[TAKT_RUNTIME_COMPANION_KEY];
|
|
52449
|
+
if (((isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0) ?? TAKT_DEFAULT_COMPANION_ENABLED) === true || !hasCompanionTargets(section)) return section;
|
|
52450
|
+
const profiles = runtimeProfiles(runtime);
|
|
52451
|
+
const companionRoots = /* @__PURE__ */ new Set();
|
|
52452
|
+
const agentRoots = /* @__PURE__ */ new Set();
|
|
52453
|
+
addAssignmentSetRoots({
|
|
52454
|
+
set: section,
|
|
52455
|
+
companionRoots,
|
|
52456
|
+
agentRoots
|
|
52457
|
+
});
|
|
52458
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52459
|
+
for (const set of isPlainObject$1(assignments) ? Object.values(assignments) : []) if (isPlainObject$1(set)) addAssignmentSetRoots({
|
|
52460
|
+
set,
|
|
52461
|
+
companionRoots,
|
|
52462
|
+
agentRoots
|
|
52463
|
+
});
|
|
52464
|
+
addAutoRoutingRoots(section[TAKT_RUNTIME_AUTO_ROUTING_KEY], agentRoots);
|
|
52465
|
+
const companionClosure = profileClosure(profiles, companionRoots);
|
|
52466
|
+
const agentClosure = profileClosure(profiles, agentRoots);
|
|
52467
|
+
const effective = {
|
|
52468
|
+
...section,
|
|
52469
|
+
[TAKT_RUNTIME_PROFILES_KEY]: Object.fromEntries(Object.entries(profiles).filter(([name]) => !companionClosure.has(name) || agentClosure.has(name)))
|
|
52470
|
+
};
|
|
52471
|
+
for (const [key, value] of [[TAKT_RUNTIME_TARGETS_KEY, withoutCompanionTargets(section[TAKT_RUNTIME_TARGETS_KEY])], [TAKT_RUNTIME_ASSIGNMENTS_KEY, withoutCompanionAssignments(assignments)]]) if (value === void 0) delete effective[key];
|
|
52472
|
+
else effective[key] = value;
|
|
52473
|
+
return effective;
|
|
52474
|
+
}
|
|
51656
52475
|
/**
|
|
51657
52476
|
* Whether a parsed `runtime.yaml` puts Takt into runtime mode, mirroring
|
|
51658
|
-
* upstream's mode detection: the `provider:` section
|
|
51659
|
-
*
|
|
51660
|
-
*
|
|
51661
|
-
*
|
|
51662
|
-
*
|
|
52477
|
+
* upstream's mode detection: the effective `provider:` section (see
|
|
52478
|
+
* `effectiveRuntimeProviderSection` — companion-only routing does not count
|
|
52479
|
+
* while companions are disabled) must carry an actual assignment — a non-empty
|
|
52480
|
+
* `defaults`, `profiles` or `auto_routing`, a `targets` map with at least one
|
|
52481
|
+
* non-empty nested map, or a named assignment set with a non-empty `defaults`
|
|
52482
|
+
* or such a `targets` map. The file existing is not enough, and empty nested
|
|
52483
|
+
* maps (`defaults: {}`, `targets: { personas: {} }`) must not flip the mode.
|
|
51663
52484
|
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/mode.ts
|
|
51664
52485
|
*/
|
|
51665
52486
|
function isRuntimeModeActive(runtime) {
|
|
51666
|
-
const
|
|
51667
|
-
if (
|
|
51668
|
-
|
|
51669
|
-
const
|
|
51670
|
-
return
|
|
52487
|
+
const section = effectiveRuntimeProviderSection(runtime);
|
|
52488
|
+
if (section === void 0) return false;
|
|
52489
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52490
|
+
const hasAssignments = isPlainObject$1(assignments) && Object.values(assignments).some((set) => isPlainObject$1(set) && (hasEntries(set[TAKT_RUNTIME_DEFAULTS_KEY]) || hasTargetContent(set[TAKT_RUNTIME_TARGETS_KEY])));
|
|
52491
|
+
return hasEntries(section[TAKT_RUNTIME_DEFAULTS_KEY]) || hasEntries(section[TAKT_RUNTIME_PROFILES_KEY]) || hasEntries(section[TAKT_RUNTIME_AUTO_ROUTING_KEY]) || hasTargetContent(section[TAKT_RUNTIME_TARGETS_KEY]) || hasAssignments;
|
|
51671
52492
|
}
|
|
51672
52493
|
/** The provider named by the runtime profile Takt would use by default. */
|
|
51673
52494
|
function resolveRuntimeProvider(runtime) {
|
|
@@ -51690,10 +52511,13 @@ function resolveRuntimeProvider(runtime) {
|
|
|
51690
52511
|
* shape the `takt` override uses. Each profile's options belong to that
|
|
51691
52512
|
* profile's own provider; when several profiles name the same provider they are
|
|
51692
52513
|
* merged in document order, so a later profile wins on a colliding option key.
|
|
52514
|
+
* Profiles Takt itself ignores (companion-only ones while companions are
|
|
52515
|
+
* disabled) are skipped, as they never reach an agent.
|
|
51693
52516
|
*/
|
|
51694
52517
|
function collectRuntimeProviderOptions(runtime) {
|
|
51695
52518
|
const collected = {};
|
|
51696
|
-
|
|
52519
|
+
const profiles = effectiveRuntimeProviderSection(runtime)?.[TAKT_RUNTIME_PROFILES_KEY];
|
|
52520
|
+
for (const profile of Object.values(isPlainObject$1(profiles) ? profiles : {})) {
|
|
51697
52521
|
if (!isPlainObject$1(profile)) continue;
|
|
51698
52522
|
const providerName = profile[TAKT_RUNTIME_PROVIDER_KEY];
|
|
51699
52523
|
const options = profile[TAKT_RUNTIME_OPTIONS_KEY];
|
|
@@ -51732,13 +52556,20 @@ async function readTaktRuntimeFile({ filePath, logger }) {
|
|
|
51732
52556
|
};
|
|
51733
52557
|
}
|
|
51734
52558
|
}
|
|
52559
|
+
/** A map-valued key of a `provider:` section, or `undefined` when it is not a map. */
|
|
52560
|
+
function runtimeSectionMap(section, key) {
|
|
52561
|
+
return isPlainObject$1(section[key]) ? section[key] : void 0;
|
|
52562
|
+
}
|
|
51735
52563
|
/**
|
|
51736
52564
|
* Collapse the project and global `runtime.yaml` into the single document Takt
|
|
51737
|
-
* itself resolves against, matching upstream's loader: `profiles`
|
|
51738
|
-
* with the project definition of a same-named
|
|
51739
|
-
* while `defaults`, `targets` and `auto_routing` are
|
|
51740
|
-
* whole whenever it states them at all (`??`, so a
|
|
51741
|
-
* the global one rather than merging with it).
|
|
52565
|
+
* itself resolves against, matching upstream's loader: `profiles` and
|
|
52566
|
+
* `assignments` are unions with the project definition of a same-named entry
|
|
52567
|
+
* replacing the global one, while `defaults`, `targets` and `auto_routing` are
|
|
52568
|
+
* taken from the project file whole whenever it states them at all (`??`, so a
|
|
52569
|
+
* project `targets: {}` masks the global one rather than merging with it). The
|
|
52570
|
+
* top-level `companion.enabled` — which decides whether companion routing
|
|
52571
|
+
* counts for mode detection — is carried as upstream carries it: unset when
|
|
52572
|
+
* neither file states it, otherwise on only when neither file turns it off.
|
|
51742
52573
|
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/loader.ts
|
|
51743
52574
|
*
|
|
51744
52575
|
* Merging before mode detection matters in both directions: a project file
|
|
@@ -51754,14 +52585,15 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
|
|
|
51754
52585
|
const projectSection = sectionOf(project);
|
|
51755
52586
|
const globalSection = sectionOf(globalConfig);
|
|
51756
52587
|
const replaced = (key) => projectSection[key] ?? globalSection[key];
|
|
51757
|
-
const profilesOf = (section) => isPlainObject$1(section[TAKT_RUNTIME_PROFILES_KEY]) ? section[TAKT_RUNTIME_PROFILES_KEY] : void 0;
|
|
51758
|
-
const globalProfiles = profilesOf(globalSection);
|
|
51759
|
-
const projectProfiles = profilesOf(projectSection);
|
|
51760
52588
|
const provider = {};
|
|
51761
|
-
|
|
51762
|
-
|
|
51763
|
-
|
|
51764
|
-
|
|
52589
|
+
for (const key of [TAKT_RUNTIME_PROFILES_KEY, TAKT_RUNTIME_ASSIGNMENTS_KEY]) {
|
|
52590
|
+
const globalMap = runtimeSectionMap(globalSection, key);
|
|
52591
|
+
const projectMap = runtimeSectionMap(projectSection, key);
|
|
52592
|
+
if (globalMap !== void 0 || projectMap !== void 0) provider[key] = {
|
|
52593
|
+
...globalMap,
|
|
52594
|
+
...projectMap
|
|
52595
|
+
};
|
|
52596
|
+
}
|
|
51765
52597
|
for (const key of [
|
|
51766
52598
|
TAKT_RUNTIME_DEFAULTS_KEY,
|
|
51767
52599
|
TAKT_RUNTIME_TARGETS_KEY,
|
|
@@ -51770,7 +52602,17 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
|
|
|
51770
52602
|
const value = replaced(key);
|
|
51771
52603
|
if (value !== void 0) provider[key] = value;
|
|
51772
52604
|
}
|
|
51773
|
-
|
|
52605
|
+
const enabledOf = (config) => {
|
|
52606
|
+
const companion = config?.[TAKT_RUNTIME_COMPANION_KEY];
|
|
52607
|
+
return isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0;
|
|
52608
|
+
};
|
|
52609
|
+
const globalEnabled = enabledOf(globalConfig);
|
|
52610
|
+
const projectEnabled = enabledOf(project);
|
|
52611
|
+
const companion = globalEnabled === void 0 && projectEnabled === void 0 ? void 0 : { [TAKT_RUNTIME_ENABLED_KEY]: globalEnabled !== false && projectEnabled !== false };
|
|
52612
|
+
return {
|
|
52613
|
+
[TAKT_RUNTIME_PROVIDER_KEY]: provider,
|
|
52614
|
+
...companion === void 0 ? {} : { [TAKT_RUNTIME_COMPANION_KEY]: companion }
|
|
52615
|
+
};
|
|
51774
52616
|
}
|
|
51775
52617
|
/**
|
|
51776
52618
|
* The runtime provider configuration in force for a generate run: the project
|
|
@@ -57375,7 +58217,12 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
|
|
|
57375
58217
|
//#region src/features/skills/commandcode-skill.ts
|
|
57376
58218
|
const CommandcodeSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
57377
58219
|
name: zod_mini.z.string(),
|
|
57378
|
-
description: zod_mini.z.string()
|
|
58220
|
+
description: zod_mini.z.string(),
|
|
58221
|
+
license: zod_mini.z.optional(zod_mini.z.string()),
|
|
58222
|
+
compatibility: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})])),
|
|
58223
|
+
metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
58224
|
+
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
58225
|
+
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
|
|
57379
58226
|
});
|
|
57380
58227
|
/**
|
|
57381
58228
|
* Represents a Command Code skill directory.
|
|
@@ -57458,10 +58305,35 @@ var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
|
|
|
57458
58305
|
const settablePaths = CommandcodeSkill.getSettablePaths({ global });
|
|
57459
58306
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
57460
58307
|
const { name: _sectionName, description: _sectionDescription, ...commandcodeSection } = rulesyncFrontmatter.commandcode ?? {};
|
|
58308
|
+
const license = resolveLicense({
|
|
58309
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58310
|
+
section: commandcodeSection
|
|
58311
|
+
});
|
|
58312
|
+
const compatibility = resolveCompatibility({
|
|
58313
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58314
|
+
section: commandcodeSection
|
|
58315
|
+
});
|
|
58316
|
+
const metadata = resolveMetadata({
|
|
58317
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58318
|
+
section: commandcodeSection
|
|
58319
|
+
});
|
|
58320
|
+
const disableModelInvocation = resolveDisableModelInvocation({
|
|
58321
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58322
|
+
section: commandcodeSection
|
|
58323
|
+
});
|
|
58324
|
+
const userInvocable = resolveUserInvocable({
|
|
58325
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58326
|
+
section: commandcodeSection
|
|
58327
|
+
});
|
|
57461
58328
|
const commandcodeFrontmatter = {
|
|
57462
58329
|
...commandcodeSection,
|
|
57463
58330
|
name: rulesyncFrontmatter.name,
|
|
57464
|
-
description: rulesyncFrontmatter.description
|
|
58331
|
+
description: rulesyncFrontmatter.description,
|
|
58332
|
+
...license !== void 0 && { license },
|
|
58333
|
+
...compatibility !== void 0 && { compatibility },
|
|
58334
|
+
...metadata !== void 0 && { metadata },
|
|
58335
|
+
...disableModelInvocation !== void 0 && { "disable-model-invocation": disableModelInvocation },
|
|
58336
|
+
...userInvocable !== void 0 && { "user-invocable": userInvocable }
|
|
57465
58337
|
};
|
|
57466
58338
|
return new CommandcodeSkill({
|
|
57467
58339
|
outputRoot,
|
|
@@ -59409,7 +60281,8 @@ const GrokcliSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
59409
60281
|
name: zod_mini.z.string(),
|
|
59410
60282
|
description: zod_mini.z.string(),
|
|
59411
60283
|
"user-invocable": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
59412
|
-
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean())
|
|
60284
|
+
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
|
|
60285
|
+
paths: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
|
|
59413
60286
|
});
|
|
59414
60287
|
/**
|
|
59415
60288
|
* Represents a Grok Build skill directory.
|
|
@@ -59468,7 +60341,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
|
|
|
59468
60341
|
const frontmatter = this.getFrontmatter();
|
|
59469
60342
|
const grokcliSection = {
|
|
59470
60343
|
...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] },
|
|
59471
|
-
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] }
|
|
60344
|
+
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
|
|
60345
|
+
...frontmatter.paths !== void 0 && { paths: frontmatter.paths }
|
|
59472
60346
|
};
|
|
59473
60347
|
const rulesyncFrontmatter = {
|
|
59474
60348
|
name: frontmatter.name,
|
|
@@ -59503,7 +60377,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
|
|
|
59503
60377
|
name: rulesyncFrontmatter.name,
|
|
59504
60378
|
description: rulesyncFrontmatter.description,
|
|
59505
60379
|
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
59506
|
-
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
60380
|
+
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation },
|
|
60381
|
+
...grokcliSection?.paths !== void 0 && { paths: grokcliSection.paths }
|
|
59507
60382
|
};
|
|
59508
60383
|
return new GrokcliSkill({
|
|
59509
60384
|
outputRoot,
|
|
@@ -62154,7 +63029,10 @@ var WarpSkill = class WarpSkill extends ToolSkill {
|
|
|
62154
63029
|
}
|
|
62155
63030
|
}
|
|
62156
63031
|
static getSettablePaths({ global: _global = false } = {}) {
|
|
62157
|
-
return {
|
|
63032
|
+
return {
|
|
63033
|
+
relativeDirPath: WARP_SKILLS_DIR_PATH,
|
|
63034
|
+
importOnlySkillRoots: [AGENTSMD_SKILLS_DIR_PATH]
|
|
63035
|
+
};
|
|
62158
63036
|
}
|
|
62159
63037
|
getFrontmatter() {
|
|
62160
63038
|
return WarpSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
@@ -62218,12 +63096,14 @@ var WarpSkill = class WarpSkill extends ToolSkill {
|
|
|
62218
63096
|
return targets.includes("*") || targets.includes("warp");
|
|
62219
63097
|
}
|
|
62220
63098
|
/**
|
|
62221
|
-
* Commands are emitted into
|
|
62222
|
-
* (see `WarpCommand`), so a directory matching a current rulesync
|
|
62223
|
-
* slug is owned by the commands feature: it must not be imported as
|
|
62224
|
-
* skill nor deleted as an orphan skill.
|
|
63099
|
+
* Commands are emitted into the `.warp/skills/` tree as `<slug>/SKILL.md`
|
|
63100
|
+
* (see `WarpCommand`), so a directory there matching a current rulesync
|
|
63101
|
+
* command slug is owned by the commands feature: it must not be imported as
|
|
63102
|
+
* a skill nor deleted as an orphan skill. The import-only `.agents/skills/`
|
|
63103
|
+
* root never receives commands, so a same-named skill in it stays a skill.
|
|
62225
63104
|
*/
|
|
62226
|
-
static async isDirOwned({ dirName, inputRoots }) {
|
|
63105
|
+
static async isDirOwned({ relativeDirPath, dirName, inputRoots }) {
|
|
63106
|
+
if (relativeDirPath !== WARP_SKILLS_DIR_PATH) return true;
|
|
62227
63107
|
return !await rulesyncCommandSlugExists({
|
|
62228
63108
|
inputRoots,
|
|
62229
63109
|
dirName
|
|
@@ -62436,6 +63316,25 @@ const ZedSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
62436
63316
|
description: zod_mini.z.string(),
|
|
62437
63317
|
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean())
|
|
62438
63318
|
});
|
|
63319
|
+
const ZED = "Zed";
|
|
63320
|
+
const ZED_LOAD_FAILURE = "does not load the skill otherwise";
|
|
63321
|
+
/**
|
|
63322
|
+
* Collect the rules Zed enforces on a skill's frontmatter that the loose
|
|
63323
|
+
* schema above does not — `name` rules first, then `description`. Returned as
|
|
63324
|
+
* warnings rather than thrown: the canonical skill is shared with every other
|
|
63325
|
+
* target, so a name Zed rejects must not stop the generate run for the rest.
|
|
63326
|
+
*/
|
|
63327
|
+
function collectZedSkillViolations({ name, description }) {
|
|
63328
|
+
const violations = [];
|
|
63329
|
+
if (name.length === 0) violations.push("`name` must not be empty; Zed does not load a skill without one");
|
|
63330
|
+
else violations.push(...collectSkillNameViolations({
|
|
63331
|
+
name,
|
|
63332
|
+
authority: ZED,
|
|
63333
|
+
consequence: ZED_LOAD_FAILURE
|
|
63334
|
+
}));
|
|
63335
|
+
if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${ZED} loads the skill but warns past ${SKILL_DESCRIPTION_MAX_LENGTH}`);
|
|
63336
|
+
return violations;
|
|
63337
|
+
}
|
|
62439
63338
|
/**
|
|
62440
63339
|
* Represents a Zed agent skill directory.
|
|
62441
63340
|
* Skills are stored under .agents/skills/ (project) or ~/.agents/skills/ (global)
|
|
@@ -62504,9 +63403,10 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62504
63403
|
global: this.global
|
|
62505
63404
|
});
|
|
62506
63405
|
}
|
|
62507
|
-
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
63406
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false, logger }) {
|
|
62508
63407
|
const settablePaths = ZedSkill.getSettablePaths({ global });
|
|
62509
63408
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
63409
|
+
const dirName = rulesyncSkill.getDirName();
|
|
62510
63410
|
const zedSection = rulesyncFrontmatter.zed;
|
|
62511
63411
|
const resolvedDisableModelInvocation = resolveDisableModelInvocation({
|
|
62512
63412
|
rootFrontmatter: rulesyncFrontmatter,
|
|
@@ -62518,10 +63418,17 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62518
63418
|
...zedSection,
|
|
62519
63419
|
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
62520
63420
|
};
|
|
63421
|
+
ZedSkill.reportSkillViolations({
|
|
63422
|
+
outputRoot,
|
|
63423
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
63424
|
+
dirName,
|
|
63425
|
+
frontmatter: zedFrontmatter,
|
|
63426
|
+
logger
|
|
63427
|
+
});
|
|
62521
63428
|
return new ZedSkill({
|
|
62522
63429
|
outputRoot,
|
|
62523
63430
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
62524
|
-
dirName
|
|
63431
|
+
dirName,
|
|
62525
63432
|
frontmatter: zedFrontmatter,
|
|
62526
63433
|
body: rulesyncSkill.getBody(),
|
|
62527
63434
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
@@ -62529,6 +63436,18 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62529
63436
|
global
|
|
62530
63437
|
});
|
|
62531
63438
|
}
|
|
63439
|
+
/**
|
|
63440
|
+
* Warn about every rule Zed enforces on the skill about to be written. The
|
|
63441
|
+
* reported path includes `outputRoot` so a global-scope skill points at the
|
|
63442
|
+
* file under the home directory rather than a same-named project path.
|
|
63443
|
+
*/
|
|
63444
|
+
static reportSkillViolations({ outputRoot, relativeDirPath, dirName, frontmatter, logger }) {
|
|
63445
|
+
warnSkillViolations({
|
|
63446
|
+
skillPath: (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME),
|
|
63447
|
+
violations: collectZedSkillViolations(frontmatter),
|
|
63448
|
+
logger
|
|
63449
|
+
});
|
|
63450
|
+
}
|
|
62532
63451
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
62533
63452
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
62534
63453
|
return targets.includes("*") || targets.includes("zed");
|
|
@@ -64834,6 +65753,35 @@ const CommandcodeSubagentFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
64834
65753
|
showOutput: zod_mini.z.optional(zod_mini.z.boolean())
|
|
64835
65754
|
});
|
|
64836
65755
|
/**
|
|
65756
|
+
* Names Command Code's built-in agents own. The loader runs the frontmatter
|
|
65757
|
+
* `name` (or the file stem when it has none) through `sanitizeAgentName`,
|
|
65758
|
+
* which rewrites every character outside `[a-zA-Z0-9_-]` to `_`, lowercases
|
|
65759
|
+
* it, and skips the file outright when the result is one of these
|
|
65760
|
+
* (`isReservedAgentName`) — with no message. Verified in the `command-code`
|
|
65761
|
+
* 1.54.2 bundle.
|
|
65762
|
+
* @see https://commandcode.ai/docs/agents
|
|
65763
|
+
*/
|
|
65764
|
+
const COMMANDCODE_RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
|
|
65765
|
+
"explore",
|
|
65766
|
+
"plan",
|
|
65767
|
+
"review",
|
|
65768
|
+
"general"
|
|
65769
|
+
]);
|
|
65770
|
+
/** The name Command Code compares against its reserved list. */
|
|
65771
|
+
function toCommandcodeAgentName(name) {
|
|
65772
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
|
|
65773
|
+
}
|
|
65774
|
+
/**
|
|
65775
|
+
* The subagent is still generated under the reserved name — the name is the
|
|
65776
|
+
* user's to change, and a rename here would make it diverge from the other
|
|
65777
|
+
* targets — but Command Code would drop it without a visible word, so say so
|
|
65778
|
+
* now (the same courtesy `VibeSkill` extends to its reserved skill names).
|
|
65779
|
+
*/
|
|
65780
|
+
function warnAboutReservedAgentName({ name, relativeFilePath, logger }) {
|
|
65781
|
+
if (!COMMANDCODE_RESERVED_AGENT_NAMES.has(toCommandcodeAgentName(name))) return;
|
|
65782
|
+
logger?.warn(`Command Code subagent ${relativeFilePath}: the name ${quoteValueForWarning(name)} is reserved for a Command Code built-in agent, so Command Code ignores a custom agent by that name when it loads agents. Rename the subagent for it to be available in Command Code.`);
|
|
65783
|
+
}
|
|
65784
|
+
/**
|
|
64837
65785
|
* Command Code custom agent: a Markdown file with YAML frontmatter under
|
|
64838
65786
|
* `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
|
|
64839
65787
|
* body is the agent's system prompt.
|
|
@@ -64880,7 +65828,7 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
|
|
|
64880
65828
|
validate: true
|
|
64881
65829
|
});
|
|
64882
65830
|
}
|
|
64883
|
-
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
65831
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false, logger }) {
|
|
64884
65832
|
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
64885
65833
|
const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
|
|
64886
65834
|
const commandcodeSubagentFrontmatter = {
|
|
@@ -64888,6 +65836,11 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
|
|
|
64888
65836
|
description: rulesyncFrontmatter.description,
|
|
64889
65837
|
...commandcodeSection
|
|
64890
65838
|
};
|
|
65839
|
+
warnAboutReservedAgentName({
|
|
65840
|
+
name: commandcodeSubagentFrontmatter.name,
|
|
65841
|
+
relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
|
|
65842
|
+
logger
|
|
65843
|
+
});
|
|
64891
65844
|
const body = rulesyncSubagent.getBody();
|
|
64892
65845
|
const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
|
|
64893
65846
|
const paths = this.getSettablePaths({ global });
|
|
@@ -71260,8 +72213,12 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
71260
72213
|
* | `false` | none | not supported; the rule is dropped |
|
|
71261
72214
|
*
|
|
71262
72215
|
* The default being `true` is the opposite of Cursor, which the adapter was
|
|
71263
|
-
* modeled on
|
|
71264
|
-
*
|
|
72216
|
+
* modeled on. The shipped loader (`@tencent-ai/codebuddy-code` 2.151.0,
|
|
72217
|
+
* `dist/codebuddy.js`) is looser than the table — it resolves
|
|
72218
|
+
* `alwaysApply = fm.alwaysApply === true || (!hasPaths && fm.alwaysApply !== false)`,
|
|
72219
|
+
* so `paths` without `alwaysApply` is already MANUAL — but the explicit
|
|
72220
|
+
* `alwaysApply: false` is written anyway: it is correct under both readings
|
|
72221
|
+
* and the documented one is what a reader of the file goes by.
|
|
71265
72222
|
*
|
|
71266
72223
|
* @see https://www.codebuddy.ai/docs/cli/memory
|
|
71267
72224
|
*/
|
|
@@ -71336,9 +72293,9 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
71336
72293
|
});
|
|
71337
72294
|
}
|
|
71338
72295
|
const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
|
|
71339
|
-
const isAlways = this.frontmatter.alwaysApply !== false;
|
|
72296
|
+
const isAlways = this.frontmatter.alwaysApply === true || sourcePaths.length === 0 && this.frontmatter.alwaysApply !== false;
|
|
71340
72297
|
const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
|
|
71341
|
-
const alwaysApply = this.frontmatter.alwaysApply
|
|
72298
|
+
const alwaysApply = this.frontmatter.alwaysApply;
|
|
71342
72299
|
const rulesyncFrontmatter = {
|
|
71343
72300
|
targets,
|
|
71344
72301
|
root: false,
|
|
@@ -76029,6 +76986,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
76029
76986
|
collisionPolicy: "fold"
|
|
76030
76987
|
}
|
|
76031
76988
|
}],
|
|
76989
|
+
["pool", {
|
|
76990
|
+
class: PoolRule,
|
|
76991
|
+
meta: {
|
|
76992
|
+
extension: "md",
|
|
76993
|
+
supportsGlobal: true,
|
|
76994
|
+
ruleDiscoveryMode: "auto",
|
|
76995
|
+
collisionPolicy: "fold"
|
|
76996
|
+
}
|
|
76997
|
+
}],
|
|
76032
76998
|
["qwencode", {
|
|
76033
76999
|
class: QwencodeRule,
|
|
76034
77000
|
meta: {
|
|
@@ -76152,15 +77118,6 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
76152
77118
|
ruleDiscoveryMode: "auto"
|
|
76153
77119
|
}
|
|
76154
77120
|
}],
|
|
76155
|
-
["pool", {
|
|
76156
|
-
class: PoolRule,
|
|
76157
|
-
meta: {
|
|
76158
|
-
extension: "md",
|
|
76159
|
-
supportsGlobal: true,
|
|
76160
|
-
ruleDiscoveryMode: "auto",
|
|
76161
|
-
collisionPolicy: "fold"
|
|
76162
|
-
}
|
|
76163
|
-
}],
|
|
76164
77121
|
["dsh", {
|
|
76165
77122
|
class: DshRule,
|
|
76166
77123
|
meta: {
|
|
@@ -79442,6 +80399,18 @@ Object.defineProperty(exports, "CODEXCLI_DIR", {
|
|
|
79442
80399
|
return CODEXCLI_DIR;
|
|
79443
80400
|
}
|
|
79444
80401
|
});
|
|
80402
|
+
Object.defineProperty(exports, "COMMANDCODE_DIR", {
|
|
80403
|
+
enumerable: true,
|
|
80404
|
+
get: function() {
|
|
80405
|
+
return COMMANDCODE_DIR;
|
|
80406
|
+
}
|
|
80407
|
+
});
|
|
80408
|
+
Object.defineProperty(exports, "COMMANDCODE_SETTINGS_LOCAL_FILE_NAME", {
|
|
80409
|
+
enumerable: true,
|
|
80410
|
+
get: function() {
|
|
80411
|
+
return COMMANDCODE_SETTINGS_LOCAL_FILE_NAME;
|
|
80412
|
+
}
|
|
80413
|
+
});
|
|
79445
80414
|
Object.defineProperty(exports, "CONFLICTING_TARGET_PAIRS", {
|
|
79446
80415
|
enumerable: true,
|
|
79447
80416
|
get: function() {
|