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
|
@@ -515,6 +515,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
515
515
|
"musecode",
|
|
516
516
|
"opencode",
|
|
517
517
|
"pi",
|
|
518
|
+
"pool",
|
|
518
519
|
"qwencode",
|
|
519
520
|
"reasonix",
|
|
520
521
|
"replit",
|
|
@@ -528,7 +529,6 @@ const rulesProcessorToolTargetTuple = [
|
|
|
528
529
|
"zcode",
|
|
529
530
|
"zed",
|
|
530
531
|
"zoocode",
|
|
531
|
-
"pool",
|
|
532
532
|
"dsh"
|
|
533
533
|
];
|
|
534
534
|
const ignoreProcessorToolTargetTuple = [
|
|
@@ -4053,24 +4053,60 @@ const CONTROL_CHARS = [
|
|
|
4053
4053
|
const hasControlChars = (val) => CONTROL_CHARS.some((char) => val.includes(char));
|
|
4054
4054
|
const safeString = z.pipe(z.string(), z.custom((val) => typeof val === "string" && !hasControlChars(val), "must not contain newline, carriage return, or NUL characters"));
|
|
4055
4055
|
/**
|
|
4056
|
+
* All canonical hook types — the union of the `type` values accepted by every
|
|
4057
|
+
* supported tool:
|
|
4058
|
+
* - `command` — universal (Claude Code, Codex CLI, Qwen Code, Cursor, …)
|
|
4059
|
+
* - `prompt` — Claude Code, Codex CLI, Qwen Code, Copilot CLI, Cursor, Devin
|
|
4060
|
+
* - `http` — Claude Code, Qwen Code, Copilot CLI, Grok CLI
|
|
4061
|
+
* - `agent` — Claude Code, Codex CLI
|
|
4062
|
+
* - `mcp_tool` — Claude Code
|
|
4063
|
+
* - `function` — Qwen Code (internal Skill-system executor)
|
|
4064
|
+
*/
|
|
4065
|
+
const HOOK_TYPES = [
|
|
4066
|
+
"command",
|
|
4067
|
+
"prompt",
|
|
4068
|
+
"http",
|
|
4069
|
+
"agent",
|
|
4070
|
+
"mcp_tool",
|
|
4071
|
+
"function"
|
|
4072
|
+
];
|
|
4073
|
+
/**
|
|
4074
|
+
* One choice offered by a Kiro `confirm` prompt: `run` decides whether picking
|
|
4075
|
+
* it executes the hook command. The prompt text is written back into JSON,
|
|
4076
|
+
* never embedded in generated code, so it is a plain string.
|
|
4077
|
+
*/
|
|
4078
|
+
const HookConfirmOptionSchema = z.looseObject({
|
|
4079
|
+
id: z.string(),
|
|
4080
|
+
label: z.string(),
|
|
4081
|
+
run: z.boolean()
|
|
4082
|
+
});
|
|
4083
|
+
/**
|
|
4084
|
+
* Kiro's confirmation prompt shown before a `Stop`-trigger command hook runs.
|
|
4085
|
+
* `confirmCommand` (optional, inside the block) names a command whose JSON
|
|
4086
|
+
* stdout drives the prompt at run time: `{ "skip": true }` suppresses it, a
|
|
4087
|
+
* replacement `question` / `options` object overrides the static prompt, and
|
|
4088
|
+
* on failure or timeout the static prompt is used. Stored verbatim: Kiro is
|
|
4089
|
+
* the only consumer, and it owns the shape.
|
|
4090
|
+
* https://kiro.dev/docs/hooks/
|
|
4091
|
+
*/
|
|
4092
|
+
const HookConfirmSchema = z.looseObject({
|
|
4093
|
+
question: z.string(),
|
|
4094
|
+
options: z.array(HookConfirmOptionSchema),
|
|
4095
|
+
confirmCommand: z.optional(safeString)
|
|
4096
|
+
});
|
|
4097
|
+
/**
|
|
4056
4098
|
* Canonical hook definition.
|
|
4057
4099
|
* Used in .rulesync/hooks.jsonc and mapped to tool-specific formats.
|
|
4058
4100
|
*/
|
|
4059
4101
|
const HookDefinitionSchema = z.looseObject({
|
|
4060
4102
|
command: z.optional(safeString),
|
|
4061
|
-
type: z.optional(z.enum(
|
|
4062
|
-
"command",
|
|
4063
|
-
"prompt",
|
|
4064
|
-
"http",
|
|
4065
|
-
"agent",
|
|
4066
|
-
"mcp_tool",
|
|
4067
|
-
"function"
|
|
4068
|
-
])),
|
|
4103
|
+
type: z.optional(z.enum(HOOK_TYPES)),
|
|
4069
4104
|
url: z.optional(safeString),
|
|
4070
4105
|
timeout: z.optional(z.number()),
|
|
4071
4106
|
cacheTtl: z.optional(z.number().check(nonnegative())),
|
|
4072
4107
|
matcher: z.optional(safeString),
|
|
4073
4108
|
enabled: z.optional(z.boolean()),
|
|
4109
|
+
confirm: z.optional(HookConfirmSchema),
|
|
4074
4110
|
prompt: z.optional(safeString),
|
|
4075
4111
|
loop_limit: z.optional(z.nullable(z.number())),
|
|
4076
4112
|
name: z.optional(safeString),
|
|
@@ -4091,6 +4127,10 @@ const HookDefinitionSchema = z.looseObject({
|
|
|
4091
4127
|
model: z.optional(safeString),
|
|
4092
4128
|
args: z.optional(z.array(safeString)),
|
|
4093
4129
|
metadata: z.optional(z.looseObject({})),
|
|
4130
|
+
source: z.optional(z.looseObject({
|
|
4131
|
+
source: safeString,
|
|
4132
|
+
ref: z.optional(safeString)
|
|
4133
|
+
})),
|
|
4094
4134
|
if: z.optional(safeString),
|
|
4095
4135
|
commandWindows: z.optional(safeString),
|
|
4096
4136
|
additionalContextLimit: z.optional(z.int().check(nonnegative())),
|
|
@@ -4344,9 +4384,13 @@ const AMP_HOOK_EVENTS = [
|
|
|
4344
4384
|
* the platform the other one owns — see `generateClineHookScript` and
|
|
4345
4385
|
* `generateClineHookPowerShellScript`.
|
|
4346
4386
|
*
|
|
4347
|
-
* `
|
|
4348
|
-
*
|
|
4349
|
-
*
|
|
4387
|
+
* `TaskCancel` is the shape of canonical `stopCancelled` (as Grok CLI's
|
|
4388
|
+
* `StopCancelled` and Codex CLI's `Interrupt` are): both runtimes fire it
|
|
4389
|
+
* *instead of* `TaskComplete` when the run ends aborted — the VS Code
|
|
4390
|
+
* extension from `afterRun` when the result status is `aborted`, the SDK/CLI
|
|
4391
|
+
* as `agent_abort` — and neither lets the script cancel anything. `TaskResume`
|
|
4392
|
+
* has no canonical counterpart and stays unmapped rather than being
|
|
4393
|
+
* approximated by `sessionStart`, whose semantics differ.
|
|
4350
4394
|
*
|
|
4351
4395
|
* @see https://github.com/cline/cline/blob/main/apps/vscode/src/core/hooks/utils.ts
|
|
4352
4396
|
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/hooks/hook-file-config.ts
|
|
@@ -4360,7 +4404,8 @@ const CLINE_HOOK_EVENTS = [
|
|
|
4360
4404
|
"preCompact",
|
|
4361
4405
|
"notification",
|
|
4362
4406
|
"taskCompleted",
|
|
4363
|
-
"afterError"
|
|
4407
|
+
"afterError",
|
|
4408
|
+
"stopCancelled"
|
|
4364
4409
|
];
|
|
4365
4410
|
/**
|
|
4366
4411
|
* Hook events supported by GitHub Copilot (cloud coding agent).
|
|
@@ -4678,23 +4723,32 @@ const QWENCODE_HOOK_EVENTS = [
|
|
|
4678
4723
|
* Hook events supported by Reasonix.
|
|
4679
4724
|
*
|
|
4680
4725
|
* Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
|
|
4681
|
-
* (global)
|
|
4682
|
-
* `
|
|
4683
|
-
* `
|
|
4726
|
+
* (global) accepts the thirteen events declared in `internal/hook/hook.go`
|
|
4727
|
+
* (`var Events`): `PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
|
|
4728
|
+
* `PermissionRequest`, `UserPromptSubmit`, `Stop`, `StopFailure`,
|
|
4729
|
+
* `PostLLMCall`, `SessionStart`, `SessionEnd`, `SubagentStop`, `Notification`,
|
|
4730
|
+
* `PreCompact`. The zh-CN desktop hooks page documents only ten of them, so the
|
|
4731
|
+
* source file is the authoritative list. All thirteen have a clean canonical
|
|
4684
4732
|
* equivalent and are mapped: `PreToolUse`, `PostToolUse`,
|
|
4685
|
-
* `
|
|
4686
|
-
* `
|
|
4687
|
-
* `
|
|
4688
|
-
* `
|
|
4689
|
-
* `
|
|
4690
|
-
*
|
|
4733
|
+
* `PostToolUseFailure`, `PermissionRequest`, `UserPromptSubmit` ←
|
|
4734
|
+
* `beforeSubmitPrompt`, `Stop`, `StopFailure`, `SessionStart`, `SessionEnd`,
|
|
4735
|
+
* `SubagentStop`, `PostLLMCall` ← `postModelInvocation`, `Notification` ←
|
|
4736
|
+
* `notification`, and `PreCompact` ← `preCompact`.
|
|
4737
|
+
* `match` (Reasonix's matcher field name) is honored on the four tool-scoped
|
|
4738
|
+
* events (`PreToolUse`, `PostToolUse`, `PostToolUseFailure`,
|
|
4739
|
+
* `PermissionRequest` — `UsesToolMatcher` in `internal/hook/inspect.go`) and
|
|
4740
|
+
* ignored everywhere else.
|
|
4741
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
|
|
4691
4742
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
4692
4743
|
*/
|
|
4693
4744
|
const REASONIX_HOOK_EVENTS = [
|
|
4694
4745
|
"preToolUse",
|
|
4695
4746
|
"postToolUse",
|
|
4747
|
+
"postToolUseFailure",
|
|
4748
|
+
"permissionRequest",
|
|
4696
4749
|
"beforeSubmitPrompt",
|
|
4697
4750
|
"stop",
|
|
4751
|
+
"stopFailure",
|
|
4698
4752
|
"sessionStart",
|
|
4699
4753
|
"sessionEnd",
|
|
4700
4754
|
"subagentStop",
|
|
@@ -4740,8 +4794,8 @@ const GROKCLI_HOOK_EVENTS = [
|
|
|
4740
4794
|
* added in 0.32.0 (`TurnStarted`, `UserPromptQueued`, `TaskStarted`,
|
|
4741
4795
|
* `SessionHeartbeat`), none of which is mapped onto a canonical rulesync event
|
|
4742
4796
|
* here. `Interrupt` (fires instead of `Stop` when the user interrupts a turn)
|
|
4743
|
-
* does have a canonical shape — `stopCancelled`, which Grok CLI
|
|
4744
|
-
* map — but Kimi keeps it native-only for now, since an existing `kimi-code`
|
|
4797
|
+
* does have a canonical shape — `stopCancelled`, which Grok CLI, Codex CLI and
|
|
4798
|
+
* Cline map — but Kimi keeps it native-only for now, since an existing `kimi-code`
|
|
4745
4799
|
* override addressing it by name would otherwise double up with a canonical
|
|
4746
4800
|
* `stopCancelled` block; folding it in is a follow-up. All six are listed in
|
|
4747
4801
|
* `KIMI_CODE_NATIVE_HOOK_EVENTS` so a per-tool `kimi-code` override can
|
|
@@ -4851,10 +4905,12 @@ const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONIC
|
|
|
4851
4905
|
*
|
|
4852
4906
|
* Bob reads `hooks` from `.bob/settings.json` (project) and
|
|
4853
4907
|
* `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
|
|
4854
|
-
*
|
|
4855
|
-
*
|
|
4856
|
-
*
|
|
4908
|
+
* seven events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
|
|
4909
|
+
* PreCompact, PostCompact (both added by Bob Shell 2.0.3) and Stop. Handlers
|
|
4910
|
+
* are `command` or `https` hooks, and `matcher` (a regex over the tool name)
|
|
4911
|
+
* applies to the two tool events.
|
|
4857
4912
|
*
|
|
4913
|
+
* @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
|
|
4858
4914
|
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
4859
4915
|
*/
|
|
4860
4916
|
const BOB_HOOK_EVENTS = [
|
|
@@ -4862,6 +4918,8 @@ const BOB_HOOK_EVENTS = [
|
|
|
4862
4918
|
"beforeSubmitPrompt",
|
|
4863
4919
|
"preToolUse",
|
|
4864
4920
|
"postToolUse",
|
|
4921
|
+
"preCompact",
|
|
4922
|
+
"postCompact",
|
|
4865
4923
|
"stop"
|
|
4866
4924
|
];
|
|
4867
4925
|
const CANONICAL_TO_BOB_EVENT_NAMES = {
|
|
@@ -4869,6 +4927,8 @@ const CANONICAL_TO_BOB_EVENT_NAMES = {
|
|
|
4869
4927
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
4870
4928
|
preToolUse: "PreToolUse",
|
|
4871
4929
|
postToolUse: "PostToolUse",
|
|
4930
|
+
preCompact: "PreCompact",
|
|
4931
|
+
postCompact: "PostCompact",
|
|
4872
4932
|
stop: "Stop"
|
|
4873
4933
|
};
|
|
4874
4934
|
const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
@@ -5030,8 +5090,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
5030
5090
|
/**
|
|
5031
5091
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
5032
5092
|
*
|
|
5033
|
-
* Hermes validates hook events against a fixed `VALID_HOOKS` set —
|
|
5034
|
-
* of v0.
|
|
5093
|
+
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 39 entries as
|
|
5094
|
+
* of v0.21.3 (`v2026.9.14`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
|
|
5035
5095
|
* full list. Only the events with a clean 1:1 canonical equivalent are mapped
|
|
5036
5096
|
* here. All other native events round-trip through `hermesagent.hooks`.
|
|
5037
5097
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
@@ -5055,11 +5115,13 @@ const HERMESAGENT_HOOK_EVENTS = [
|
|
|
5055
5115
|
* observers, `gateway_platform_event` and `transform_api_error_classification`.
|
|
5056
5116
|
* Shell hooks are gated on the same set.
|
|
5057
5117
|
*
|
|
5058
|
-
* Verified against the tag `v2026.
|
|
5059
|
-
* entries, grown from 23 at `v2026.8.3` (v0.20.0)
|
|
5060
|
-
*
|
|
5118
|
+
* Verified against the tag `v2026.9.14` (v0.21.3): `VALID_HOOKS` holds 39
|
|
5119
|
+
* entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
|
|
5120
|
+
* (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`.
|
|
5121
|
+
* This list holds 38 of them — see
|
|
5122
|
+
* {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
|
|
5061
5123
|
*
|
|
5062
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5124
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py
|
|
5063
5125
|
*/
|
|
5064
5126
|
const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
5065
5127
|
"pre_tool_call",
|
|
@@ -5085,8 +5147,10 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5085
5147
|
"subagent_start",
|
|
5086
5148
|
"subagent_stop",
|
|
5087
5149
|
"pre_gateway_dispatch",
|
|
5150
|
+
"agent_loop_stopped",
|
|
5088
5151
|
"pre_approval_request",
|
|
5089
5152
|
"post_approval_response",
|
|
5153
|
+
"on_room_member_activity",
|
|
5090
5154
|
"pre_transcription",
|
|
5091
5155
|
"kanban_task_claimed",
|
|
5092
5156
|
"kanban_task_completed",
|
|
@@ -5109,8 +5173,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
5109
5173
|
* rulesync only ever writes shell hooks, so these are excluded from
|
|
5110
5174
|
* {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
|
|
5111
5175
|
*
|
|
5112
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5113
|
-
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.
|
|
5176
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
|
|
5177
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/shell_hooks.py — `_parse_hooks_block`
|
|
5114
5178
|
*/
|
|
5115
5179
|
const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
|
|
5116
5180
|
/**
|
|
@@ -5411,7 +5475,8 @@ const CANONICAL_TO_CLINE_EVENT_NAMES = {
|
|
|
5411
5475
|
preCompact: "PreCompact",
|
|
5412
5476
|
notification: "Notification",
|
|
5413
5477
|
taskCompleted: "TaskComplete",
|
|
5414
|
-
afterError: "TaskError"
|
|
5478
|
+
afterError: "TaskError",
|
|
5479
|
+
stopCancelled: "TaskCancel"
|
|
5415
5480
|
};
|
|
5416
5481
|
/**
|
|
5417
5482
|
* Map canonical camelCase event names to Copilot camelCase.
|
|
@@ -5729,8 +5794,11 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
5729
5794
|
const CANONICAL_TO_REASONIX_EVENT_NAMES = {
|
|
5730
5795
|
preToolUse: "PreToolUse",
|
|
5731
5796
|
postToolUse: "PostToolUse",
|
|
5797
|
+
postToolUseFailure: "PostToolUseFailure",
|
|
5798
|
+
permissionRequest: "PermissionRequest",
|
|
5732
5799
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
5733
5800
|
stop: "Stop",
|
|
5801
|
+
stopFailure: "StopFailure",
|
|
5734
5802
|
sessionStart: "SessionStart",
|
|
5735
5803
|
sessionEnd: "SessionEnd",
|
|
5736
5804
|
subagentStop: "SubagentStop",
|
|
@@ -7280,13 +7348,30 @@ const DeepagentsExtensionsOverrideSchema = z.looseObject({
|
|
|
7280
7348
|
]))
|
|
7281
7349
|
});
|
|
7282
7350
|
/**
|
|
7351
|
+
* The `[mcp]` table of `~/.deepagents/config.toml`, where dcode keeps its
|
|
7352
|
+
* per-server trust for the MCP servers a project's `.mcp.json` /
|
|
7353
|
+
* `.deepagents/.mcp.json` declares. dcode reads these lists from the user-level
|
|
7354
|
+
* file only — never from a project file — which is exactly why the global
|
|
7355
|
+
* permissions adapter is the place to author them.
|
|
7356
|
+
*
|
|
7357
|
+
* `disabled_project_servers` names project MCP servers dcode always rejects;
|
|
7358
|
+
* a rejection wins over any approval or trust. The sibling
|
|
7359
|
+
* `enabled_project_server_approvals` is the fingerprint-bound approval store
|
|
7360
|
+
* the interactive prompt fills in, and `enabled_project_servers` is a
|
|
7361
|
+
* deprecated flat allowlist dcode ignores, so neither has a key here.
|
|
7362
|
+
*
|
|
7363
|
+
* @see https://docs.langchain.com/oss/deepagents/code/configuration
|
|
7364
|
+
*/
|
|
7365
|
+
const DeepagentsMcpOverrideSchema = z.looseObject({ disabled_project_servers: z.optional(z.array(z.string())) });
|
|
7366
|
+
/**
|
|
7283
7367
|
* The `[shell].allow_list` array itself is rulesync-owned, driven by the
|
|
7284
7368
|
* shared `permission.bash` block, so it has no key here.
|
|
7285
7369
|
*/
|
|
7286
7370
|
const DeepagentsPermissionsOverrideSchema = z.looseObject({
|
|
7287
7371
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
7288
7372
|
startup: z.optional(DeepagentsStartupOverrideSchema),
|
|
7289
|
-
extensions: z.optional(DeepagentsExtensionsOverrideSchema)
|
|
7373
|
+
extensions: z.optional(DeepagentsExtensionsOverrideSchema),
|
|
7374
|
+
mcp: z.optional(DeepagentsMcpOverrideSchema)
|
|
7290
7375
|
});
|
|
7291
7376
|
/**
|
|
7292
7377
|
* The actions Junie's allowlist accepts. Verified against the shipped Junie
|
|
@@ -7604,11 +7689,15 @@ const KiroPermissionsOverrideSchema = z.looseObject({
|
|
|
7604
7689
|
});
|
|
7605
7690
|
/**
|
|
7606
7691
|
* Codex CLI's approval-workflow policy. Serialized as a kebab-case string in
|
|
7607
|
-
* `.codex/config.toml`. `on-failure` is a
|
|
7608
|
-
* Codex still
|
|
7609
|
-
*
|
|
7610
|
-
*
|
|
7692
|
+
* `.codex/config.toml`. `on-failure` is a deprecated alias for `on-request`
|
|
7693
|
+
* that Codex still reads, so it stays accepted (generate warns) so existing
|
|
7694
|
+
* configs round-trip. `untrusted` was retired in Codex 0.149.0 and makes
|
|
7695
|
+
* Codex refuse to start; it stays in the enum only so an existing
|
|
7696
|
+
* `.rulesync/permissions.jsonc` still parses — generate warns and does not
|
|
7697
|
+
* write it, and import does not lift it. The granular table form
|
|
7698
|
+
* (`{ granular = { … } }`) is modeled separately in the override union.
|
|
7611
7699
|
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
7700
|
+
* @see https://github.com/openai/codex/pull/39630
|
|
7612
7701
|
*/
|
|
7613
7702
|
const CodexApprovalPolicySchema = z.enum([
|
|
7614
7703
|
"untrusted",
|
|
@@ -7678,11 +7767,12 @@ const CodexBasePermissionProfileSchema = z.enum(CODEX_BASE_PERMISSION_PROFILES);
|
|
|
7678
7767
|
* selected directly via `default_permissions` and skips the managed profile
|
|
7679
7768
|
* entirely — canonical filesystem/network rules are ignored in that mode.
|
|
7680
7769
|
* Defaults to `:workspace` when unspecified.
|
|
7681
|
-
* - `approval_policy` — `
|
|
7770
|
+
* - `approval_policy` — `on-request` (deprecated alias `on-failure`) |
|
|
7682
7771
|
* `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
|
|
7683
7772
|
* schema has required fields that are brittle to model as typed keys).
|
|
7684
7773
|
* Defaults to `on-request` when neither the override nor the existing
|
|
7685
|
-
* config sets it.
|
|
7774
|
+
* config sets it. `untrusted` (retired in Codex 0.149.0) is still parsed
|
|
7775
|
+
* but skipped with a warning on generate, since Codex refuses to start on it.
|
|
7686
7776
|
* - `sandbox_mode` — **deprecated.** `read-only` | `workspace-write` |
|
|
7687
7777
|
* `danger-full-access`, with the sibling `sandbox_workspace_write` table
|
|
7688
7778
|
* (`network_access`, `writable_roots`, …). Codex has superseded the classic
|
|
@@ -9568,7 +9658,8 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9568
9658
|
})),
|
|
9569
9659
|
grokcli: z.optional(z.looseObject({
|
|
9570
9660
|
"disable-model-invocation": z.optional(z.boolean()),
|
|
9571
|
-
"user-invocable": z.optional(z.boolean())
|
|
9661
|
+
"user-invocable": z.optional(z.boolean()),
|
|
9662
|
+
paths: z.optional(z.union([z.string(), z.array(z.string())]))
|
|
9572
9663
|
})),
|
|
9573
9664
|
dsh: z.optional(z.looseObject({
|
|
9574
9665
|
whenToUse: z.optional(z.string()),
|
|
@@ -9600,7 +9691,13 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9600
9691
|
"user-invocable": z.optional(z.boolean()),
|
|
9601
9692
|
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
9602
9693
|
})),
|
|
9603
|
-
commandcode: z.optional(z.looseObject({
|
|
9694
|
+
commandcode: z.optional(z.looseObject({
|
|
9695
|
+
license: z.optional(z.string()),
|
|
9696
|
+
compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
|
|
9697
|
+
metadata: z.optional(z.looseObject({})),
|
|
9698
|
+
"disable-model-invocation": z.optional(z.boolean()),
|
|
9699
|
+
"user-invocable": z.optional(z.boolean())
|
|
9700
|
+
})),
|
|
9604
9701
|
cortexcode: z.optional(z.looseObject({})),
|
|
9605
9702
|
continue: z.optional(z.looseObject({})),
|
|
9606
9703
|
tabnine: z.optional(z.looseObject({})),
|
|
@@ -9789,8 +9886,32 @@ var RulesyncSubagent = class RulesyncSubagent extends RulesyncFile {
|
|
|
9789
9886
|
});
|
|
9790
9887
|
}
|
|
9791
9888
|
};
|
|
9792
|
-
|
|
9793
|
-
|
|
9889
|
+
const SKILL_DESCRIPTION_MAX_LENGTH = 1024;
|
|
9890
|
+
const SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
9891
|
+
/**
|
|
9892
|
+
* Check a skill `name` against the length and character rules, phrased for the
|
|
9893
|
+
* `authority` that enforces them (e.g. "the Agent Skills spec" or "Zed"). When
|
|
9894
|
+
* that authority does something with a bad name beyond flagging it — Zed
|
|
9895
|
+
* refuses to load the skill — `consequence` names it so the warning says what
|
|
9896
|
+
* the author is about to lose. An empty name is the caller's concern: what it
|
|
9897
|
+
* means differs per consumer.
|
|
9898
|
+
*/
|
|
9899
|
+
function collectSkillNameViolations({ name, authority, consequence }) {
|
|
9900
|
+
const violations = [];
|
|
9901
|
+
if (name.length > 64) violations.push(`\`name\` is ${name.length} characters; ${authority} allows at most 64${consequence === void 0 ? "" : ` and ${consequence}`}`);
|
|
9902
|
+
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}`}`);
|
|
9903
|
+
return violations;
|
|
9904
|
+
}
|
|
9905
|
+
/**
|
|
9906
|
+
* Report each violation as a warning prefixed with the skill file it concerns.
|
|
9907
|
+
* The path is shown POSIX-style and stripped of control characters so a hostile
|
|
9908
|
+
* directory name cannot forge log lines; `warnWithFallback` keeps the report
|
|
9909
|
+
* visible when the caller has no logger to hand.
|
|
9910
|
+
*/
|
|
9911
|
+
function warnSkillViolations({ skillPath, violations, logger }) {
|
|
9912
|
+
const shownPath = stripControlCharacters(toPosixPath(skillPath));
|
|
9913
|
+
for (const violation of violations) warnWithFallback(logger, `${shownPath}: ${violation}`);
|
|
9914
|
+
}
|
|
9794
9915
|
/**
|
|
9795
9916
|
* Whether a directory on disk can be addressed by its name.
|
|
9796
9917
|
*
|
|
@@ -9844,7 +9965,7 @@ async function getLocalSkillDirNames(sourceTree) {
|
|
|
9844
9965
|
*
|
|
9845
9966
|
* The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
|
|
9846
9967
|
* default that applies to every tool supporting the flag (claudecode, copilot,
|
|
9847
|
-
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh). Each tool's own section may override that
|
|
9968
|
+
* copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that
|
|
9848
9969
|
* default with a per-target value. A defined section value (including `false`)
|
|
9849
9970
|
* always wins over the root default.
|
|
9850
9971
|
*
|
|
@@ -9863,7 +9984,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
|
9863
9984
|
*
|
|
9864
9985
|
* The rulesync skill frontmatter exposes a root-level `user-invocable` default
|
|
9865
9986
|
* that applies to every tool supporting the flag (claudecode, copilot,
|
|
9866
|
-
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh). Each tool's own section may override that default with a
|
|
9987
|
+
* copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that default with a
|
|
9867
9988
|
* per-target value. A defined section value (including `false`) always wins
|
|
9868
9989
|
* over the root default.
|
|
9869
9990
|
*
|
|
@@ -10614,10 +10735,13 @@ const AmpCheckFrontmatterSchema = z.looseObject({
|
|
|
10614
10735
|
/**
|
|
10615
10736
|
* Represents an Amp code review check.
|
|
10616
10737
|
*
|
|
10617
|
-
* Amp
|
|
10618
|
-
* scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks
|
|
10619
|
-
*
|
|
10620
|
-
*
|
|
10738
|
+
* Amp reads code review checks as Markdown files with YAML frontmatter,
|
|
10739
|
+
* scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`;
|
|
10740
|
+
* Amp also reads `~/.config/agents/checks/`, which rulesync does not write).
|
|
10741
|
+
* Since the docs rebuild the supported runner is the official
|
|
10742
|
+
* `@amp/review-checks` plugin (`amp review` is deprecated); existing checks in
|
|
10743
|
+
* this format continue to apply.
|
|
10744
|
+
* @see https://ampcode.com/docs/review
|
|
10621
10745
|
*/
|
|
10622
10746
|
var AmpCheck = class AmpCheck extends ToolCheck {
|
|
10623
10747
|
frontmatter;
|
|
@@ -15431,6 +15555,7 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
|
|
|
15431
15555
|
const COMMANDCODE_DIR = ".commandcode";
|
|
15432
15556
|
const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
|
|
15433
15557
|
const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
|
|
15558
|
+
const COMMANDCODE_SETTINGS_LOCAL_FILE_NAME = "settings.local.json";
|
|
15434
15559
|
const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
|
|
15435
15560
|
const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
|
|
15436
15561
|
const COMMANDCODE_COMMANDS_DIR_PATH = join(COMMANDCODE_DIR, "commands");
|
|
@@ -16856,10 +16981,8 @@ const AgentsSkillsSkillFrontmatterSchema = z.looseObject({
|
|
|
16856
16981
|
metadata: z.optional(z.looseObject({})),
|
|
16857
16982
|
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
16858
16983
|
});
|
|
16859
|
-
const NAME_MAX_LENGTH = 64;
|
|
16860
|
-
const DESCRIPTION_MAX_LENGTH = 1024;
|
|
16861
16984
|
const COMPATIBILITY_MAX_LENGTH = 500;
|
|
16862
|
-
const
|
|
16985
|
+
const AGENT_SKILLS_SPEC = "the Agent Skills spec";
|
|
16863
16986
|
/**
|
|
16864
16987
|
* Placeholder for an object that has already been encoded once in the same
|
|
16865
16988
|
* value. YAML anchors let one document reference the same node repeatedly, and
|
|
@@ -17023,12 +17146,14 @@ function collectAgentSkillViolations({ frontmatter, dirName, sourceAllowedTools
|
|
|
17023
17146
|
const { name, description } = frontmatter;
|
|
17024
17147
|
if (name.length === 0) violations.push("`name` is required and must not be empty");
|
|
17025
17148
|
else {
|
|
17026
|
-
|
|
17027
|
-
|
|
17149
|
+
violations.push(...collectSkillNameViolations({
|
|
17150
|
+
name,
|
|
17151
|
+
authority: AGENT_SKILLS_SPEC
|
|
17152
|
+
}));
|
|
17028
17153
|
if (name !== dirName) violations.push(`\`name\` "${name}" must match its parent directory name "${dirName}"; conformant clients require them to be equal`);
|
|
17029
17154
|
}
|
|
17030
17155
|
if (description.length === 0) violations.push(EMPTY_SKILL_DESCRIPTION_VIOLATION);
|
|
17031
|
-
else if (description.length >
|
|
17156
|
+
else if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${AGENT_SKILLS_SPEC} allows at most ${SKILL_DESCRIPTION_MAX_LENGTH}`);
|
|
17032
17157
|
const { compatibility } = frontmatter;
|
|
17033
17158
|
if (typeof compatibility !== "string" && compatibility !== void 0) violations.push("`compatibility` must be a string; the Agent Skills spec does not allow a mapping here");
|
|
17034
17159
|
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}`);
|
|
@@ -17146,16 +17271,19 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
|
|
|
17146
17271
|
* directory rather than a same-named project path.
|
|
17147
17272
|
*/
|
|
17148
17273
|
static reportSpecViolations({ outputRoot, relativeDirPath, dirName, frontmatter, sourceAllowedTools, logger }) {
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17274
|
+
warnSkillViolations({
|
|
17275
|
+
skillPath: agentSkillFilePath({
|
|
17276
|
+
outputRoot,
|
|
17277
|
+
relativeDirPath,
|
|
17278
|
+
dirName
|
|
17279
|
+
}),
|
|
17280
|
+
violations: collectAgentSkillViolations({
|
|
17281
|
+
frontmatter,
|
|
17282
|
+
dirName,
|
|
17283
|
+
sourceAllowedTools
|
|
17284
|
+
}),
|
|
17285
|
+
logger
|
|
17153
17286
|
});
|
|
17154
|
-
for (const violation of collectAgentSkillViolations({
|
|
17155
|
-
frontmatter,
|
|
17156
|
-
dirName,
|
|
17157
|
-
sourceAllowedTools
|
|
17158
|
-
})) warnWithFallback(logger, `${stripControlCharacters(toPosixPath(skillPath))}: ${violation}`);
|
|
17159
17287
|
}
|
|
17160
17288
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
17161
17289
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
@@ -17795,6 +17923,15 @@ const KiloCommandFrontmatterSchema = z.looseObject({
|
|
|
17795
17923
|
model: z.optional(z.string()),
|
|
17796
17924
|
variant: z.optional(z.string())
|
|
17797
17925
|
});
|
|
17926
|
+
/**
|
|
17927
|
+
* Command names Kilo keeps for itself. Its loader throws on a custom command
|
|
17928
|
+
* file named `goal.md` in either the project or the global directory ("The
|
|
17929
|
+
* /goal command is reserved for session goals. Rename the custom command."),
|
|
17930
|
+
* and a throw there aborts the whole command scan — every other custom
|
|
17931
|
+
* command becomes unreachable too, not only the reserved one.
|
|
17932
|
+
* https://github.com/Kilo-Org/kilocode/blob/v7.6.2/packages/opencode/src/command/index.ts
|
|
17933
|
+
*/
|
|
17934
|
+
const KILO_RESERVED_COMMAND_NAMES = /* @__PURE__ */ new Set(["goal"]);
|
|
17798
17935
|
var KiloCommand = class KiloCommand extends ToolCommand {
|
|
17799
17936
|
frontmatter;
|
|
17800
17937
|
body;
|
|
@@ -17837,6 +17974,20 @@ var KiloCommand = class KiloCommand extends ToolCommand {
|
|
|
17837
17974
|
validate: true
|
|
17838
17975
|
});
|
|
17839
17976
|
}
|
|
17977
|
+
/**
|
|
17978
|
+
* Refuse to write a command Kilo reserves (see
|
|
17979
|
+
* {@link KILO_RESERVED_COMMAND_NAMES}). The name Kilo loads is the file
|
|
17980
|
+
* stem, and the processor calls this on the flattened path, so `goal.md`
|
|
17981
|
+
* is refused at any nesting depth under the basename naming and only at the
|
|
17982
|
+
* top level under the path naming, where `git/goal.md` becomes `git-goal`.
|
|
17983
|
+
* Import is untouched: a `goal.md` already in `.kilo/commands/` is read as
|
|
17984
|
+
* it is, since the reservation is Kilo's to enforce on its own files.
|
|
17985
|
+
*/
|
|
17986
|
+
static getWriteBlockReason({ rulesyncCommand }) {
|
|
17987
|
+
const stem = basename(rulesyncCommand.getRelativeFilePath(), ".md");
|
|
17988
|
+
if (!KILO_RESERVED_COMMAND_NAMES.has(stem)) return null;
|
|
17989
|
+
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\`.`;
|
|
17990
|
+
}
|
|
17840
17991
|
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
|
|
17841
17992
|
const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
|
|
17842
17993
|
const kiloFields = rulesyncFrontmatter.kilo ?? {};
|
|
@@ -20059,6 +20210,14 @@ var CommandsProcessor = class extends FeatureProcessor {
|
|
|
20059
20210
|
if (!factory.class.isTargetedByRulesyncCommand(rulesyncCommand)) return null;
|
|
20060
20211
|
const originalRelativePath = rulesyncCommand.getRelativeFilePath();
|
|
20061
20212
|
const commandToConvert = factory.meta.supportsSubdirectory ? rulesyncCommand : this.flattenRelativeFilePath(rulesyncCommand);
|
|
20213
|
+
const writeBlockReason = factory.class.getWriteBlockReason?.({
|
|
20214
|
+
rulesyncCommand: commandToConvert,
|
|
20215
|
+
global: this.global
|
|
20216
|
+
});
|
|
20217
|
+
if (writeBlockReason !== void 0 && writeBlockReason !== null) {
|
|
20218
|
+
this.logger.warn(`Skipping command ${quoteForLog(originalRelativePath)} for '${this.toolTarget}': ${writeBlockReason}`);
|
|
20219
|
+
return null;
|
|
20220
|
+
}
|
|
20062
20221
|
if (!factory.meta.supportsSubdirectory) {
|
|
20063
20222
|
const flattenedPath = commandToConvert.getRelativeFilePath();
|
|
20064
20223
|
const firstOrigin = flattenedPathOrigins.get(flattenedPath);
|
|
@@ -20645,6 +20804,18 @@ function isToolMatcherEntry(x) {
|
|
|
20645
20804
|
return true;
|
|
20646
20805
|
}
|
|
20647
20806
|
/**
|
|
20807
|
+
* Convert a canonical `timeout` (seconds) into the tool's unit.
|
|
20808
|
+
*/
|
|
20809
|
+
function emitTimeout({ timeout, converterConfig }) {
|
|
20810
|
+
return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? Math.round(timeout * 1e3) : timeout;
|
|
20811
|
+
}
|
|
20812
|
+
/**
|
|
20813
|
+
* Convert a tool `timeout` back into the canonical unit (seconds).
|
|
20814
|
+
*/
|
|
20815
|
+
function importTimeout({ timeout, converterConfig }) {
|
|
20816
|
+
return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? timeout / 1e3 : timeout;
|
|
20817
|
+
}
|
|
20818
|
+
/**
|
|
20648
20819
|
* Filter the shared canonical hooks to the supported events and merge tool overrides on top.
|
|
20649
20820
|
*/
|
|
20650
20821
|
function buildEffectiveHooks$1({ config, toolOverrideHooks, supportedEvents }) {
|
|
@@ -20785,6 +20956,7 @@ const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
|
20785
20956
|
const isEmittableArray = ({ value }) => isStringArray$1(value);
|
|
20786
20957
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
20787
20958
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
20959
|
+
const isEmittableObject = ({ value }) => isPlainObject$1(value);
|
|
20788
20960
|
/**
|
|
20789
20961
|
* The canonical schema of one hook field, looked up by name. Read off the
|
|
20790
20962
|
* schema's own shape rather than by parsing a one-field object, so a `canonical`
|
|
@@ -20819,11 +20991,17 @@ const isImportableNumber = ({ value, canonical }) => Number.isFinite(value) && s
|
|
|
20819
20991
|
value,
|
|
20820
20992
|
canonical
|
|
20821
20993
|
});
|
|
20994
|
+
const isImportableObject = ({ value, canonical }) => isPlainObject$1(value) && satisfiesCanonicalField({
|
|
20995
|
+
value,
|
|
20996
|
+
canonical
|
|
20997
|
+
});
|
|
20822
20998
|
/**
|
|
20823
20999
|
* Say which rule the value broke, so the warning names the actual constraint
|
|
20824
21000
|
* rather than asserting a canonical rejection that may not be the reason. A
|
|
20825
21001
|
* closed enum lists its members; a rule carrying its own message (the
|
|
20826
|
-
* control-character check behind `safeString`) reuses it.
|
|
21002
|
+
* control-character check behind `safeString`) reuses it. An object field
|
|
21003
|
+
* names the offending key, since zod's generic "Invalid input" would not tell
|
|
21004
|
+
* the reader which key is missing or malformed.
|
|
20827
21005
|
*/
|
|
20828
21006
|
function describeScalarConstraint({ canonical, value }) {
|
|
20829
21007
|
const schema = canonicalFieldSchema(canonical);
|
|
@@ -20831,13 +21009,22 @@ function describeScalarConstraint({ canonical, value }) {
|
|
|
20831
21009
|
if (result === void 0 || result.success) return `it is not a value this field carries through.`;
|
|
20832
21010
|
const issue = result.error.issues[0];
|
|
20833
21011
|
if (issue === void 0) return `it is not a value the canonical "${canonical}" field accepts.`;
|
|
20834
|
-
return `it does not satisfy the canonical "${canonical}" field: ${issue.message}.`;
|
|
21012
|
+
return `it does not satisfy the canonical "${canonical}" field${issue.path.length > 0 ? ` at ${quoteValueForWarning(issue.path.map(String).join("."))}` : ""}: ${issue.message}.`;
|
|
20835
21013
|
}
|
|
20836
21014
|
const describeInvalidScalar = ({ tool, canonical, value }) => `Dropping "${tool}" (${quoteValueForWarning(value)}) while importing a hook: ${describeScalarConstraint({
|
|
20837
21015
|
canonical,
|
|
20838
21016
|
value
|
|
20839
21017
|
})} Importing it would fail validation on the next run.`;
|
|
20840
21018
|
const describeInvalidArray = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a list of strings without newline, carriage return or NUL characters.`;
|
|
21019
|
+
/**
|
|
21020
|
+
* An object is quoted by its offending key rather than its value: the payload
|
|
21021
|
+
* can be arbitrarily large, and the missing or malformed key is what the
|
|
21022
|
+
* reader needs to fix.
|
|
21023
|
+
*/
|
|
21024
|
+
const describeInvalidObject = ({ tool, canonical, value }) => isPlainObject$1(value) ? `Dropping "${tool}" while importing a hook: ${describeScalarConstraint({
|
|
21025
|
+
canonical,
|
|
21026
|
+
value
|
|
21027
|
+
})} Importing it would fail validation on the next run.` : `Dropping "${tool}" while importing a hook: it must be an object.`;
|
|
20841
21028
|
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.`;
|
|
20842
21029
|
/**
|
|
20843
21030
|
* Check a value against the shape its field documents. A string field also
|
|
@@ -20900,6 +21087,72 @@ function isSupportedHookType({ type, converterConfig }) {
|
|
|
20900
21087
|
return converterConfig.supportedHookTypes?.has(type ?? "command") ?? true;
|
|
20901
21088
|
}
|
|
20902
21089
|
/**
|
|
21090
|
+
* Whether a keyword field applies to this hook, warning about the reason when
|
|
21091
|
+
* it does not. Shared by both directions: the field is unusable on the tool
|
|
21092
|
+
* side either way, so the value is dropped rather than carried into a config
|
|
21093
|
+
* the tool would ignore or refuse.
|
|
21094
|
+
*/
|
|
21095
|
+
function isKeywordFieldApplicable({ spec, hookType, eventName, direction, warn }) {
|
|
21096
|
+
const { canonical, tool, commandOnly, events } = spec;
|
|
21097
|
+
const field = direction === "generated" ? canonical : tool;
|
|
21098
|
+
if (!isFieldApplicable({
|
|
21099
|
+
commandOnly,
|
|
21100
|
+
hookType
|
|
21101
|
+
})) {
|
|
21102
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on "command" hooks only, so it is not ${direction}.`);
|
|
21103
|
+
return false;
|
|
21104
|
+
}
|
|
21105
|
+
if (events !== void 0 && !events.has(eventName)) {
|
|
21106
|
+
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}.`);
|
|
21107
|
+
return false;
|
|
21108
|
+
}
|
|
21109
|
+
return true;
|
|
21110
|
+
}
|
|
21111
|
+
/**
|
|
21112
|
+
* Emit the keyword passthrough fields: a canonical `true` becomes the tool's
|
|
21113
|
+
* keyword, while `false` is omitted as the tool's own default. A `true` the
|
|
21114
|
+
* tool would not honour on this hook is warned about, like the other kinds;
|
|
21115
|
+
* a `false` there is dropped silently, since omitting it changes nothing.
|
|
21116
|
+
*/
|
|
21117
|
+
function emitKeywordFields({ def, hookType, eventName, fields, warn }) {
|
|
21118
|
+
const emitted = {};
|
|
21119
|
+
for (const spec of fields) {
|
|
21120
|
+
if (def[spec.canonical] !== true) continue;
|
|
21121
|
+
if (isKeywordFieldApplicable({
|
|
21122
|
+
spec,
|
|
21123
|
+
hookType,
|
|
21124
|
+
eventName,
|
|
21125
|
+
direction: "generated",
|
|
21126
|
+
warn
|
|
21127
|
+
})) emitted[spec.tool] = spec.trueValue;
|
|
21128
|
+
}
|
|
21129
|
+
return emitted;
|
|
21130
|
+
}
|
|
21131
|
+
/**
|
|
21132
|
+
* Import the keyword passthrough fields back into canonical booleans,
|
|
21133
|
+
* reversing {@link emitKeywordFields}. Only the two documented keywords are
|
|
21134
|
+
* read; anything else is warned about and dropped, so a typo in a hand-written
|
|
21135
|
+
* tool file never lands in `.rulesync/hooks.*` as a boolean it did not mean.
|
|
21136
|
+
*/
|
|
21137
|
+
function importKeywordFields({ h, hookType, eventName, fields, warn }) {
|
|
21138
|
+
const imported = {};
|
|
21139
|
+
for (const spec of fields) {
|
|
21140
|
+
const value = h[spec.tool];
|
|
21141
|
+
if (value === void 0) continue;
|
|
21142
|
+
if (!isKeywordFieldApplicable({
|
|
21143
|
+
spec,
|
|
21144
|
+
hookType,
|
|
21145
|
+
eventName,
|
|
21146
|
+
direction: "imported",
|
|
21147
|
+
warn
|
|
21148
|
+
})) continue;
|
|
21149
|
+
if (value === spec.trueValue) imported[spec.canonical] = true;
|
|
21150
|
+
else if (spec.falseValue !== void 0 && value === spec.falseValue) imported[spec.canonical] = false;
|
|
21151
|
+
else warn?.(`Dropping "${spec.tool}" from an imported "${hookType}" hook on "${eventName}": ${quoteValueForWarning(value)} is not a value this tool documents for it.`);
|
|
21152
|
+
}
|
|
21153
|
+
return imported;
|
|
21154
|
+
}
|
|
21155
|
+
/**
|
|
20903
21156
|
* Emit every per-hook passthrough kind for one canonical definition.
|
|
20904
21157
|
*/
|
|
20905
21158
|
function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, warn }) {
|
|
@@ -20912,6 +21165,13 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
|
|
|
20912
21165
|
isValid: isBooleanValue,
|
|
20913
21166
|
warn
|
|
20914
21167
|
}),
|
|
21168
|
+
...emitKeywordFields({
|
|
21169
|
+
def,
|
|
21170
|
+
hookType,
|
|
21171
|
+
eventName,
|
|
21172
|
+
fields: converterConfig.keywordPassthroughFields ?? [],
|
|
21173
|
+
warn
|
|
21174
|
+
}),
|
|
20915
21175
|
...emitPassthroughFields({
|
|
20916
21176
|
def,
|
|
20917
21177
|
hookType,
|
|
@@ -20943,6 +21203,14 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
|
|
|
20943
21203
|
fields: converterConfig.recordPassthroughFields ?? [],
|
|
20944
21204
|
isValid: isEmittableRecord,
|
|
20945
21205
|
warn
|
|
21206
|
+
}),
|
|
21207
|
+
...emitPassthroughFields({
|
|
21208
|
+
def,
|
|
21209
|
+
hookType,
|
|
21210
|
+
eventName,
|
|
21211
|
+
fields: converterConfig.objectPassthroughFields ?? [],
|
|
21212
|
+
isValid: isEmittableObject,
|
|
21213
|
+
warn
|
|
20946
21214
|
})
|
|
20947
21215
|
};
|
|
20948
21216
|
}
|
|
@@ -20966,9 +21234,12 @@ function buildToolHooks({ defs, eventName, converterConfig, warn }) {
|
|
|
20966
21234
|
converterConfig,
|
|
20967
21235
|
warn
|
|
20968
21236
|
}),
|
|
20969
|
-
type: hookType,
|
|
21237
|
+
type: converterConfig.hookTypeNames?.[hookType] ?? hookType,
|
|
20970
21238
|
...command !== void 0 && command !== null && { command },
|
|
20971
|
-
...def.timeout !== void 0 && def.timeout !== null && { timeout:
|
|
21239
|
+
...def.timeout !== void 0 && def.timeout !== null && { timeout: emitTimeout({
|
|
21240
|
+
timeout: def.timeout,
|
|
21241
|
+
converterConfig
|
|
21242
|
+
}) },
|
|
20972
21243
|
...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
|
|
20973
21244
|
...emitTypePayloadFields({
|
|
20974
21245
|
def,
|
|
@@ -21090,6 +21361,20 @@ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
|
|
|
21090
21361
|
function isImportedHookType(value) {
|
|
21091
21362
|
return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
|
|
21092
21363
|
}
|
|
21364
|
+
/**
|
|
21365
|
+
* The canonical hook type a tool-side `type` value denotes: the tool's own
|
|
21366
|
+
* spelling from {@link ToolHooksConverterConfig.hookTypeNames} when it has
|
|
21367
|
+
* one, otherwise the value itself when it is a canonical type, and `command`
|
|
21368
|
+
* for anything else (the same coercion as before the renames existed).
|
|
21369
|
+
*/
|
|
21370
|
+
function resolveImportedHookType({ value, converterConfig }) {
|
|
21371
|
+
const names = converterConfig.hookTypeNames;
|
|
21372
|
+
if (names !== void 0 && typeof value === "string") {
|
|
21373
|
+
for (const [canonical, tool] of Object.entries(names)) if (tool === value) return canonical;
|
|
21374
|
+
if (Object.hasOwn(names, value)) return "command";
|
|
21375
|
+
}
|
|
21376
|
+
return isImportedHookType(value) ? value : "command";
|
|
21377
|
+
}
|
|
21093
21378
|
function isStringRecord(value) {
|
|
21094
21379
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
21095
21380
|
return Object.values(value).every((v) => typeof v === "string");
|
|
@@ -21215,7 +21500,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
21215
21500
|
* Import every per-hook passthrough kind for one tool hook record, reversing
|
|
21216
21501
|
* the emit side in {@link buildToolHooks}.
|
|
21217
21502
|
*/
|
|
21218
|
-
function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
21503
|
+
function importAllPassthroughFields({ h, hookType, eventName, converterConfig, warn }) {
|
|
21219
21504
|
return {
|
|
21220
21505
|
...importPassthroughFields({
|
|
21221
21506
|
h,
|
|
@@ -21224,6 +21509,13 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
|
21224
21509
|
isValid: isBooleanValue,
|
|
21225
21510
|
warn
|
|
21226
21511
|
}),
|
|
21512
|
+
...importKeywordFields({
|
|
21513
|
+
h,
|
|
21514
|
+
hookType,
|
|
21515
|
+
eventName,
|
|
21516
|
+
fields: converterConfig.keywordPassthroughFields ?? [],
|
|
21517
|
+
warn
|
|
21518
|
+
}),
|
|
21227
21519
|
...importPassthroughFields({
|
|
21228
21520
|
h,
|
|
21229
21521
|
hookType,
|
|
@@ -21255,14 +21547,25 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
|
|
|
21255
21547
|
isValid: isEmittableRecord,
|
|
21256
21548
|
describeInvalid: describeInvalidRecord,
|
|
21257
21549
|
warn
|
|
21550
|
+
}),
|
|
21551
|
+
...importPassthroughFields({
|
|
21552
|
+
h,
|
|
21553
|
+
hookType,
|
|
21554
|
+
fields: converterConfig.objectPassthroughFields ?? [],
|
|
21555
|
+
isValid: isImportableObject,
|
|
21556
|
+
describeInvalid: describeInvalidObject,
|
|
21557
|
+
warn
|
|
21258
21558
|
})
|
|
21259
21559
|
};
|
|
21260
21560
|
}
|
|
21261
21561
|
/**
|
|
21262
21562
|
* Convert a single tool hook record into a canonical hook definition.
|
|
21263
21563
|
*/
|
|
21264
|
-
function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
21265
|
-
const hookType =
|
|
21564
|
+
function toolHookToCanonical({ h, rawEntry, eventName, converterConfig, warn }) {
|
|
21565
|
+
const hookType = resolveImportedHookType({
|
|
21566
|
+
value: h.type,
|
|
21567
|
+
converterConfig
|
|
21568
|
+
});
|
|
21266
21569
|
const command = importCanonicalString({
|
|
21267
21570
|
value: stripCommandPrefix({
|
|
21268
21571
|
command: h.command,
|
|
@@ -21271,7 +21574,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
|
21271
21574
|
canonical: "command",
|
|
21272
21575
|
warn
|
|
21273
21576
|
});
|
|
21274
|
-
const timeout = typeof h.timeout === "number" ?
|
|
21577
|
+
const timeout = typeof h.timeout === "number" ? importTimeout({
|
|
21578
|
+
timeout: h.timeout,
|
|
21579
|
+
converterConfig
|
|
21580
|
+
}) : void 0;
|
|
21275
21581
|
const prompt = importCanonicalString({
|
|
21276
21582
|
value: h.prompt,
|
|
21277
21583
|
canonical: "prompt",
|
|
@@ -21303,6 +21609,7 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
|
|
|
21303
21609
|
...importAllPassthroughFields({
|
|
21304
21610
|
h,
|
|
21305
21611
|
hookType,
|
|
21612
|
+
eventName,
|
|
21306
21613
|
converterConfig,
|
|
21307
21614
|
warn
|
|
21308
21615
|
}),
|
|
@@ -21389,7 +21696,7 @@ function describeHookSkipReason({ h, rawEntry, hookType, converterConfig }) {
|
|
|
21389
21696
|
/**
|
|
21390
21697
|
* Convert a single tool matcher entry into canonical hook definitions.
|
|
21391
21698
|
*/
|
|
21392
|
-
function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
21699
|
+
function toolMatcherEntryToCanonical({ rawEntry, eventName, converterConfig, warn }) {
|
|
21393
21700
|
const hookDefs = rawEntry.hooks ?? [];
|
|
21394
21701
|
const groupSkipReason = describeGroupSkipReason({
|
|
21395
21702
|
rawEntry,
|
|
@@ -21404,7 +21711,10 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
|
21404
21711
|
const skipReason = describeHookSkipReason({
|
|
21405
21712
|
h,
|
|
21406
21713
|
rawEntry,
|
|
21407
|
-
hookType:
|
|
21714
|
+
hookType: resolveImportedHookType({
|
|
21715
|
+
value: h.type,
|
|
21716
|
+
converterConfig
|
|
21717
|
+
}),
|
|
21408
21718
|
converterConfig
|
|
21409
21719
|
});
|
|
21410
21720
|
if (skipReason !== void 0) {
|
|
@@ -21414,6 +21724,7 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
|
|
|
21414
21724
|
definitions.push(toolHookToCanonical({
|
|
21415
21725
|
h,
|
|
21416
21726
|
rawEntry,
|
|
21727
|
+
eventName,
|
|
21417
21728
|
converterConfig,
|
|
21418
21729
|
warn
|
|
21419
21730
|
}));
|
|
@@ -21461,6 +21772,7 @@ function toolHooksToCanonical({ hooks, converterConfig, logger }) {
|
|
|
21461
21772
|
if (!isToolMatcherEntry(rawEntry)) continue;
|
|
21462
21773
|
defs.push(...toolMatcherEntryToCanonical({
|
|
21463
21774
|
rawEntry,
|
|
21775
|
+
eventName,
|
|
21464
21776
|
converterConfig,
|
|
21465
21777
|
warn
|
|
21466
21778
|
}));
|
|
@@ -21889,6 +22201,7 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
|
|
|
21889
22201
|
"beforeSubmitPrompt"
|
|
21890
22202
|
]),
|
|
21891
22203
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
22204
|
+
timeoutUnit: "milliseconds",
|
|
21892
22205
|
arrayPassthroughFields: [{
|
|
21893
22206
|
canonical: "args",
|
|
21894
22207
|
tool: "args"
|
|
@@ -22487,9 +22800,12 @@ const BOB_SPEC = {
|
|
|
22487
22800
|
noMatcherEvents: /* @__PURE__ */ new Set([
|
|
22488
22801
|
"sessionStart",
|
|
22489
22802
|
"beforeSubmitPrompt",
|
|
22803
|
+
"preCompact",
|
|
22804
|
+
"postCompact",
|
|
22490
22805
|
"stop"
|
|
22491
22806
|
]),
|
|
22492
|
-
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
22807
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command", "http"]),
|
|
22808
|
+
hookTypeNames: { http: "https" },
|
|
22493
22809
|
wildcardMatcherMeansAll: true
|
|
22494
22810
|
}
|
|
22495
22811
|
};
|
|
@@ -22500,10 +22816,13 @@ const BOB_SPEC = {
|
|
|
22500
22816
|
* `<project>/.bob/settings.json` (project scope) and
|
|
22501
22817
|
* `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
|
|
22502
22818
|
* `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
|
|
22503
|
-
* "command": "...", "timeout"?: <seconds> }] }] }
|
|
22504
|
-
*
|
|
22505
|
-
*
|
|
22819
|
+
* "command": "...", "timeout"?: <seconds> }] }] }`, where a handler may
|
|
22820
|
+
* instead be `{ "type": "https", "url": "...", "timeout"?: <seconds> }`. The
|
|
22821
|
+
* file also holds settings rulesync does not own, so generation merges the
|
|
22822
|
+
* `hooks` key into it (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting
|
|
22823
|
+
* the file.
|
|
22506
22824
|
*
|
|
22825
|
+
* @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
|
|
22507
22826
|
* @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
|
|
22508
22827
|
*/
|
|
22509
22828
|
var BobHooks = class extends SettingsJsonHooks {
|
|
@@ -23933,6 +24252,11 @@ const CORTEXCODE_SPEC = {
|
|
|
23933
24252
|
booleanPassthroughFields: [{
|
|
23934
24253
|
canonical: "enabled",
|
|
23935
24254
|
tool: "enabled"
|
|
24255
|
+
}],
|
|
24256
|
+
objectPassthroughFields: [{
|
|
24257
|
+
canonical: "source",
|
|
24258
|
+
tool: "source",
|
|
24259
|
+
commandOnly: true
|
|
23936
24260
|
}]
|
|
23937
24261
|
}
|
|
23938
24262
|
};
|
|
@@ -23943,8 +24267,9 @@ const CORTEXCODE_SPEC = {
|
|
|
23943
24267
|
* (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
|
|
23944
24268
|
* scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
|
|
23945
24269
|
* "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
|
|
23946
|
-
* <seconds>, "enabled"?: <boolean
|
|
23947
|
-
* settings rulesync does not own, so generation
|
|
24270
|
+
* <seconds>, "enabled"?: <boolean>, "source"?: { "source", "ref"? } }] }] }`.
|
|
24271
|
+
* The project file also holds settings rulesync does not own, so generation
|
|
24272
|
+
* merges the `hooks` key into
|
|
23948
24273
|
* either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
|
|
23949
24274
|
* neither file is removed wholesale (hooks.json sits in the CLI-owned
|
|
23950
24275
|
* `~/.snowflake/cortex/` tree).
|
|
@@ -25068,7 +25393,15 @@ const GOOSE_CONVERTER_CONFIG = {
|
|
|
25068
25393
|
toolToCanonicalEventNames: GOOSE_TO_CANONICAL_EVENT_NAMES,
|
|
25069
25394
|
projectDirVar: "",
|
|
25070
25395
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
25071
|
-
wildcardMatcherMeansAll: true
|
|
25396
|
+
wildcardMatcherMeansAll: true,
|
|
25397
|
+
keywordPassthroughFields: [{
|
|
25398
|
+
canonical: "failClosed",
|
|
25399
|
+
tool: "on_failure",
|
|
25400
|
+
trueValue: "block",
|
|
25401
|
+
falseValue: "allow",
|
|
25402
|
+
commandOnly: true,
|
|
25403
|
+
events: /* @__PURE__ */ new Set(["preToolUse"])
|
|
25404
|
+
}]
|
|
25072
25405
|
};
|
|
25073
25406
|
/**
|
|
25074
25407
|
* Represents a Goose lifecycle hooks file.
|
|
@@ -25377,7 +25710,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
25377
25710
|
for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
|
|
25378
25711
|
if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
|
|
25379
25712
|
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.`);
|
|
25380
|
-
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.
|
|
25713
|
+
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.`);
|
|
25381
25714
|
setHermesHookEntries({
|
|
25382
25715
|
result,
|
|
25383
25716
|
event: nativeEvent,
|
|
@@ -26257,7 +26590,8 @@ const KiroIdeHookEntrySchema = z.looseObject({
|
|
|
26257
26590
|
matcher: z.optional(z.string()),
|
|
26258
26591
|
action: z.optional(KiroIdeHookActionSchema),
|
|
26259
26592
|
timeout: z.optional(z.number()),
|
|
26260
|
-
enabled: z.optional(z.boolean())
|
|
26593
|
+
enabled: z.optional(z.boolean()),
|
|
26594
|
+
confirm: z.optional(HookConfirmSchema)
|
|
26261
26595
|
});
|
|
26262
26596
|
const KiroIdeHooksFileSchema = z.looseObject({
|
|
26263
26597
|
version: z.optional(z.string()),
|
|
@@ -26296,7 +26630,8 @@ function buildKiroIdeEntriesForEvent(trigger, definitions) {
|
|
|
26296
26630
|
...def.matcher !== void 0 && def.matcher !== null && def.matcher !== "" && { matcher: def.matcher },
|
|
26297
26631
|
action,
|
|
26298
26632
|
...def.timeout !== void 0 && def.timeout !== null && def.timeout >= 0 && { timeout: def.timeout },
|
|
26299
|
-
enabled: def.enabled ?? true
|
|
26633
|
+
enabled: def.enabled ?? true,
|
|
26634
|
+
...def.confirm !== void 0 && { confirm: def.confirm }
|
|
26300
26635
|
});
|
|
26301
26636
|
}
|
|
26302
26637
|
return entries;
|
|
@@ -26338,6 +26673,14 @@ function canonicalToKiroIdeHooks(config) {
|
|
|
26338
26673
|
}
|
|
26339
26674
|
return entries;
|
|
26340
26675
|
}
|
|
26676
|
+
/**
|
|
26677
|
+
* Copies the optional per-hook fields (`enabled`, `confirm`) back onto the
|
|
26678
|
+
* canonical definition.
|
|
26679
|
+
*/
|
|
26680
|
+
function copyOptionalHookFields({ entry, def }) {
|
|
26681
|
+
if (entry.enabled === false) def.enabled = false;
|
|
26682
|
+
if (entry.confirm !== void 0 && entry.confirm !== null) def.confirm = omitPrototypePollutionKeysDeep(entry.confirm);
|
|
26683
|
+
}
|
|
26341
26684
|
function kiroIdeHooksToCanonical(entries) {
|
|
26342
26685
|
const canonical = {};
|
|
26343
26686
|
for (const entry of entries) {
|
|
@@ -26361,7 +26704,10 @@ function kiroIdeHooksToCanonical(entries) {
|
|
|
26361
26704
|
if (entry.description !== void 0 && entry.description !== null) def.description = entry.description;
|
|
26362
26705
|
if (entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "") def.matcher = entry.matcher;
|
|
26363
26706
|
if (entry.timeout !== void 0 && entry.timeout !== null) def.timeout = entry.timeout;
|
|
26364
|
-
|
|
26707
|
+
copyOptionalHookFields({
|
|
26708
|
+
entry,
|
|
26709
|
+
def
|
|
26710
|
+
});
|
|
26365
26711
|
const list = lookupOwn({
|
|
26366
26712
|
record: canonical,
|
|
26367
26713
|
key: eventName
|
|
@@ -27368,10 +27714,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
|
|
|
27368
27714
|
//#endregion
|
|
27369
27715
|
//#region src/features/hooks/reasonix-hooks.ts
|
|
27370
27716
|
/**
|
|
27371
|
-
* Only
|
|
27372
|
-
* against the tool name); it is ignored on every other event.
|
|
27717
|
+
* Only the tool-scoped events honor the `match` field (an anchored regex
|
|
27718
|
+
* against the tool name); it is ignored on every other event. Mirrors
|
|
27719
|
+
* `UsesToolMatcher` in upstream `internal/hook/inspect.go`.
|
|
27373
27720
|
*/
|
|
27374
|
-
const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set([
|
|
27721
|
+
const REASONIX_MATCHER_EVENTS = /* @__PURE__ */ new Set([
|
|
27722
|
+
"PreToolUse",
|
|
27723
|
+
"PostToolUse",
|
|
27724
|
+
"PostToolUseFailure",
|
|
27725
|
+
"PermissionRequest"
|
|
27726
|
+
]);
|
|
27375
27727
|
const SUPPORTED_REASONIX_EVENTS = new Set(REASONIX_HOOK_EVENTS);
|
|
27376
27728
|
/**
|
|
27377
27729
|
* Convert canonical hooks config to the Reasonix `hooks` object.
|
|
@@ -27453,9 +27805,11 @@ function reasonixHooksToCanonical(hooks) {
|
|
|
27453
27805
|
* Reasonix hooks live in a Claude-Code-style but standalone JSON file —
|
|
27454
27806
|
* `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
|
|
27455
27807
|
* (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
|
|
27456
|
-
* All
|
|
27457
|
-
* PreToolUse/PostToolUse/
|
|
27458
|
-
* SubagentStop/PostLLMCall/
|
|
27808
|
+
* All thirteen upstream events have a clean canonical equivalent and are mapped:
|
|
27809
|
+
* PreToolUse/PostToolUse/PostToolUseFailure/PermissionRequest/UserPromptSubmit/
|
|
27810
|
+
* Stop/StopFailure plus SessionStart/SessionEnd/SubagentStop/PostLLMCall/
|
|
27811
|
+
* Notification/PreCompact (see REASONIX_HOOK_EVENTS).
|
|
27812
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/internal/hook/hook.go
|
|
27459
27813
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
27460
27814
|
*/
|
|
27461
27815
|
var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
@@ -28281,6 +28635,23 @@ const PER_HOOK_ENABLED_TARGETS = /* @__PURE__ */ new Set([
|
|
|
28281
28635
|
"kiro-ide",
|
|
28282
28636
|
"zcode"
|
|
28283
28637
|
]);
|
|
28638
|
+
/** The targets whose hooks format carries the Kiro `confirm` prompt. */
|
|
28639
|
+
const PER_HOOK_CONFIRM_TARGETS = /* @__PURE__ */ new Set(["kiro-cli", "kiro-ide"]);
|
|
28640
|
+
/**
|
|
28641
|
+
* Names the shared events that carry a hook whose canonical definition
|
|
28642
|
+
* satisfies `predicate`, excluding events the target skips altogether (those
|
|
28643
|
+
* are already reported as skipped and produce no output, so warning about them
|
|
28644
|
+
* again would contradict that message).
|
|
28645
|
+
*/
|
|
28646
|
+
function emittedEventsWithHook(params) {
|
|
28647
|
+
const { factory, sharedHooks, effectiveHooks, predicate } = params;
|
|
28648
|
+
const skippedEvents = new Set(unsupportedEventNames({
|
|
28649
|
+
factory,
|
|
28650
|
+
sharedHooks,
|
|
28651
|
+
effectiveHooks
|
|
28652
|
+
}));
|
|
28653
|
+
return Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => predicate(def))).map(([event]) => event);
|
|
28654
|
+
}
|
|
28284
28655
|
const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
28285
28656
|
["amp", {
|
|
28286
28657
|
class: AmpHooks,
|
|
@@ -28590,7 +28961,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
28590
28961
|
supportsImport: true
|
|
28591
28962
|
},
|
|
28592
28963
|
supportedEvents: BOB_HOOK_EVENTS,
|
|
28593
|
-
supportedHookTypes: ["command"],
|
|
28964
|
+
supportedHookTypes: ["command", "http"],
|
|
28594
28965
|
supportsMatcher: true,
|
|
28595
28966
|
matcherEvents: ["preToolUse", "postToolUse"]
|
|
28596
28967
|
}],
|
|
@@ -28851,15 +29222,11 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
28851
29222
|
}
|
|
28852
29223
|
for (const [hookType, events] of unsupportedTypeToEvents) this.logger.warn(`Skipped ${hookType}-type hook(s) for ${this.toolTarget} (not supported): ${Array.from(events).join(", ")}`);
|
|
28853
29224
|
}
|
|
28854
|
-
|
|
28855
|
-
|
|
28856
|
-
|
|
28857
|
-
|
|
28858
|
-
|
|
28859
|
-
}));
|
|
28860
|
-
const eventsWithDisabledHooks = Object.entries(sharedHooks).filter(([event, defs]) => !skippedEvents.has(event) && defs.some((def) => def.enabled === false)).map(([event]) => event);
|
|
28861
|
-
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(", ")}`);
|
|
28862
|
-
}
|
|
29225
|
+
this.warnAboutDroppedPerHookFields({
|
|
29226
|
+
factory,
|
|
29227
|
+
sharedHooks,
|
|
29228
|
+
effectiveHooks
|
|
29229
|
+
});
|
|
28863
29230
|
const eventsWithUnsupportedMatcher = unsupportedMatcherEventNames({
|
|
28864
29231
|
factory,
|
|
28865
29232
|
effectiveHooks
|
|
@@ -28889,6 +29256,33 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
28889
29256
|
if (auxiliaryFiles && auxiliaryFiles.length > 0) result.push(...auxiliaryFiles);
|
|
28890
29257
|
return result;
|
|
28891
29258
|
}
|
|
29259
|
+
/**
|
|
29260
|
+
* Warns about canonical per-hook fields that this target's hooks format
|
|
29261
|
+
* cannot express, so a hook does not silently change behavior on generation.
|
|
29262
|
+
* Only canonical definitions are considered: a tool-native field inside an
|
|
29263
|
+
* override block is passed through verbatim and honored by that tool.
|
|
29264
|
+
*/
|
|
29265
|
+
warnAboutDroppedPerHookFields(params) {
|
|
29266
|
+
const { factory, sharedHooks, effectiveHooks } = params;
|
|
29267
|
+
if (!PER_HOOK_ENABLED_TARGETS.has(this.toolTarget)) {
|
|
29268
|
+
const eventsWithDisabledHooks = emittedEventsWithHook({
|
|
29269
|
+
factory,
|
|
29270
|
+
sharedHooks,
|
|
29271
|
+
effectiveHooks,
|
|
29272
|
+
predicate: (def) => def.enabled === false
|
|
29273
|
+
});
|
|
29274
|
+
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(", ")}`);
|
|
29275
|
+
}
|
|
29276
|
+
if (!PER_HOOK_CONFIRM_TARGETS.has(this.toolTarget)) {
|
|
29277
|
+
const eventsWithConfirmHooks = emittedEventsWithHook({
|
|
29278
|
+
factory,
|
|
29279
|
+
sharedHooks,
|
|
29280
|
+
effectiveHooks,
|
|
29281
|
+
predicate: (def) => def.confirm !== void 0
|
|
29282
|
+
});
|
|
29283
|
+
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(", ")}`);
|
|
29284
|
+
}
|
|
29285
|
+
}
|
|
28892
29286
|
async convertToolFilesToRulesyncFiles(toolFiles) {
|
|
28893
29287
|
const hooks = toolFiles.filter((f) => f instanceof ToolHooks);
|
|
28894
29288
|
const logger = withToolTargetPrefix({
|
|
@@ -31404,6 +31798,50 @@ function parseBobMcpConfig({ fileContent, relativePath }) {
|
|
|
31404
31798
|
/** The `type` Bob IDE writes for a streamable HTTP server. */
|
|
31405
31799
|
const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
|
|
31406
31800
|
/**
|
|
31801
|
+
* The OAuth keys Bob reads flat on a server entry, next to `oauth` (a
|
|
31802
|
+
* boolean: `true` forces the flow on, `false` off, absent lets Bob detect it).
|
|
31803
|
+
* The canonical shape is Claude Code's nested `oauth: { clientId, ... }`
|
|
31804
|
+
* object, so the two are translated in both directions.
|
|
31805
|
+
* @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-oauth
|
|
31806
|
+
*/
|
|
31807
|
+
const BOB_FLAT_OAUTH_KEYS = [
|
|
31808
|
+
"clientId",
|
|
31809
|
+
"clientSecret",
|
|
31810
|
+
"scope"
|
|
31811
|
+
];
|
|
31812
|
+
/**
|
|
31813
|
+
* Flatten the canonical `oauth` object onto a Bob server entry: `oauth: true`
|
|
31814
|
+
* plus the string-valued keys Bob documents. A flat key the server already
|
|
31815
|
+
* carries (authored in Bob's own spelling) is kept over the nested one. Other
|
|
31816
|
+
* nested keys (`callbackPort`, `redirectUri`) have no Bob counterpart and are
|
|
31817
|
+
* dropped rather than nested under a key Bob reads as a boolean.
|
|
31818
|
+
*/
|
|
31819
|
+
function flattenOauthForBob({ oauth, converted }) {
|
|
31820
|
+
converted.oauth = true;
|
|
31821
|
+
for (const key of BOB_FLAT_OAUTH_KEYS) {
|
|
31822
|
+
const value = oauth[key];
|
|
31823
|
+
if (typeof value === "string" && converted[key] === void 0) converted[key] = value;
|
|
31824
|
+
}
|
|
31825
|
+
}
|
|
31826
|
+
/**
|
|
31827
|
+
* Reverse of {@link flattenOauthForBob}: gather Bob's flat OAuth keys into the
|
|
31828
|
+
* canonical `oauth` object. `oauth: true` always folds (even with no keys, so
|
|
31829
|
+
* the explicit enable survives the round-trip as an empty object); with no
|
|
31830
|
+
* `oauth` key at all the fold happens only when a flat key is present, since
|
|
31831
|
+
* that is what makes the entry an OAuth one for Bob; `oauth: false` and
|
|
31832
|
+
* anything else leave the entry as it is.
|
|
31833
|
+
*/
|
|
31834
|
+
function nestOauthFromBob(converted) {
|
|
31835
|
+
const flat = BOB_FLAT_OAUTH_KEYS.filter((key) => typeof converted[key] === "string");
|
|
31836
|
+
if (!(converted.oauth === true || converted.oauth === void 0 && flat.length > 0)) return;
|
|
31837
|
+
const oauth = {};
|
|
31838
|
+
for (const key of flat) {
|
|
31839
|
+
oauth[key] = converted[key];
|
|
31840
|
+
delete converted[key];
|
|
31841
|
+
}
|
|
31842
|
+
converted.oauth = oauth;
|
|
31843
|
+
}
|
|
31844
|
+
/**
|
|
31407
31845
|
* The remote transport Bob IDE reads a server as, spelled the way it writes
|
|
31408
31846
|
* it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
|
|
31409
31847
|
* `url` with no `type`. `http` is the canonical rulesync alias for streamable
|
|
@@ -31426,7 +31864,9 @@ function asBobRemoteType(stated, url) {
|
|
|
31426
31864
|
* `headers`, `timeout`, `alwaysAllow` and `disabled` pass through, as Bob
|
|
31427
31865
|
* documents all of them, with prototype-pollution keys dropped at every
|
|
31428
31866
|
* nesting level (Bob spreads `env` and `headers` into the process environment
|
|
31429
|
-
* and the HTTP requests).
|
|
31867
|
+
* and the HTTP requests). The canonical `oauth` object is flattened onto the
|
|
31868
|
+
* entry as `oauth: true` plus `clientId` / `clientSecret` / `scope`, the keys
|
|
31869
|
+
* Bob reads; a boolean `oauth` passes through as Bob's own on/off switch.
|
|
31430
31870
|
*
|
|
31431
31871
|
* Bob Shell documents the same file with an `httpURL` key instead of
|
|
31432
31872
|
* `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
|
|
@@ -31492,10 +31932,16 @@ function convertToBobFormat(mcpServers, logger) {
|
|
|
31492
31932
|
converted.command = command;
|
|
31493
31933
|
if (args.length > 0) converted.args = args;
|
|
31494
31934
|
}
|
|
31495
|
-
|
|
31935
|
+
const { oauth, ...passthrough } = rest;
|
|
31936
|
+
for (const [key, value] of Object.entries(passthrough)) {
|
|
31496
31937
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
31497
31938
|
converted[key] = omitPrototypePollutionKeysDeep(value);
|
|
31498
31939
|
}
|
|
31940
|
+
if (isRecord$1(oauth)) flattenOauthForBob({
|
|
31941
|
+
oauth,
|
|
31942
|
+
converted
|
|
31943
|
+
});
|
|
31944
|
+
else if (oauth !== void 0) converted.oauth = oauth;
|
|
31499
31945
|
result[serverName] = converted;
|
|
31500
31946
|
}
|
|
31501
31947
|
return result;
|
|
@@ -31505,9 +31951,10 @@ function convertToBobFormat(mcpServers, logger) {
|
|
|
31505
31951
|
* (`type: "streamable-http"` + `url`) is already canonical and passes through;
|
|
31506
31952
|
* the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
|
|
31507
31953
|
* `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
|
|
31508
|
-
* on the next generate.
|
|
31509
|
-
*
|
|
31510
|
-
*
|
|
31954
|
+
* on the next generate. Bob's flat OAuth keys are gathered back into the
|
|
31955
|
+
* canonical `oauth` object (see {@link nestOauthFromBob}). Every other field
|
|
31956
|
+
* passes through with its prototype-pollution keys dropped at every nesting
|
|
31957
|
+
* level, mirroring the generate side's handling of `env` / `headers`.
|
|
31511
31958
|
*/
|
|
31512
31959
|
function convertFromBobFormat(mcpServers) {
|
|
31513
31960
|
if (!isMcpServers(mcpServers)) return {};
|
|
@@ -31528,6 +31975,7 @@ function convertFromBobFormat(mcpServers) {
|
|
|
31528
31975
|
converted.url = httpURL;
|
|
31529
31976
|
converted.type = "http";
|
|
31530
31977
|
} else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
|
|
31978
|
+
nestOauthFromBob(converted);
|
|
31531
31979
|
result[serverName] = converted;
|
|
31532
31980
|
}
|
|
31533
31981
|
return result;
|
|
@@ -32552,7 +33000,10 @@ function asContinueRemoteType(stated, url) {
|
|
|
32552
33000
|
* file with a strict union — one server that matches neither shape (an
|
|
32553
33001
|
* unknown transport, a non-string `env` value) makes Continue drop every
|
|
32554
33002
|
* server in the file — so only the documented keys are emitted and anything
|
|
32555
|
-
* else
|
|
33003
|
+
* else is left out. The canonical keys a user would expect to take effect
|
|
33004
|
+
* (`cwd`, `timeout`, `oauth`) are dropped with a warning, since the JSON
|
|
33005
|
+
* schema has no place for them (the YAML block form does, but Rulesync writes
|
|
33006
|
+
* the JSON file); rulesync-only fields are dropped silently. `envFile` is
|
|
32556
33007
|
* accepted by Continue's schema but ignored with a warning at load time, so
|
|
32557
33008
|
* it is dropped here with a warning instead of being written as if it worked.
|
|
32558
33009
|
*
|
|
@@ -32588,6 +33039,17 @@ function convertToContinueFormat(mcpServers, logger) {
|
|
|
32588
33039
|
}
|
|
32589
33040
|
return result;
|
|
32590
33041
|
}
|
|
33042
|
+
/**
|
|
33043
|
+
* Warn for each canonical key Continue's JSON MCP schema cannot carry, so a
|
|
33044
|
+
* dropped `cwd`, `timeout` or `oauth` does not go unnoticed in a config that
|
|
33045
|
+
* otherwise looks complete.
|
|
33046
|
+
*/
|
|
33047
|
+
function warnUnsupportedContinueKeys({ serverName, serverConfig, keys, logger }) {
|
|
33048
|
+
for (const key of keys) {
|
|
33049
|
+
if (serverConfig[key] === void 0) continue;
|
|
33050
|
+
logger?.warn(`Continue's MCP config has no "${key}" field, so the ${key} of server ${quoteValueForWarning(serverName)} was not written.`);
|
|
33051
|
+
}
|
|
33052
|
+
}
|
|
32591
33053
|
function convertRemoteServer({ serverName, serverConfig, logger }) {
|
|
32592
33054
|
const url = resolveRemoteMcpUrl(serverConfig);
|
|
32593
33055
|
if (!url) {
|
|
@@ -32615,6 +33077,12 @@ function convertRemoteServer({ serverName, serverConfig, logger }) {
|
|
|
32615
33077
|
url
|
|
32616
33078
|
};
|
|
32617
33079
|
if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
|
|
33080
|
+
warnUnsupportedContinueKeys({
|
|
33081
|
+
serverName,
|
|
33082
|
+
serverConfig,
|
|
33083
|
+
keys: ["timeout", "oauth"],
|
|
33084
|
+
logger
|
|
33085
|
+
});
|
|
32618
33086
|
return converted;
|
|
32619
33087
|
}
|
|
32620
33088
|
function convertStdioServer({ serverName, serverConfig, logger }) {
|
|
@@ -32635,6 +33103,12 @@ function convertStdioServer({ serverName, serverConfig, logger }) {
|
|
|
32635
33103
|
if (args.length > 0) converted.args = args;
|
|
32636
33104
|
if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
|
|
32637
33105
|
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.`);
|
|
33106
|
+
warnUnsupportedContinueKeys({
|
|
33107
|
+
serverName,
|
|
33108
|
+
serverConfig,
|
|
33109
|
+
keys: ["cwd", "timeout"],
|
|
33110
|
+
logger
|
|
33111
|
+
});
|
|
32638
33112
|
return converted;
|
|
32639
33113
|
}
|
|
32640
33114
|
/**
|
|
@@ -34610,28 +35084,60 @@ function resolveHermesTimeout(config) {
|
|
|
34610
35084
|
if (typeof config.networkTimeout === "number") return config.networkTimeout;
|
|
34611
35085
|
}
|
|
34612
35086
|
/**
|
|
34613
|
-
*
|
|
34614
|
-
*
|
|
34615
|
-
*
|
|
34616
|
-
* `
|
|
34617
|
-
*
|
|
34618
|
-
*
|
|
34619
|
-
|
|
34620
|
-
|
|
35087
|
+
* String-valued `oauth` keys Hermes reads (`tools/mcp_oauth.py`,
|
|
35088
|
+
* `tools/mcp_oauth_device.py`, v0.21.3): the callback overrides, the
|
|
35089
|
+
* pre-registered client, the CIMD document override, the DCR registration
|
|
35090
|
+
* fields, the token-request `user_agent`, the login `flow` (`device` for
|
|
35091
|
+
* RFC 8628) and the space-separated `scope` string sent on registration and
|
|
35092
|
+
* device authorization.
|
|
35093
|
+
*/
|
|
35094
|
+
const HERMES_OAUTH_STRING_KEYS = [
|
|
35095
|
+
"redirect_uri",
|
|
35096
|
+
"redirect_host",
|
|
35097
|
+
"client_id",
|
|
35098
|
+
"client_secret",
|
|
35099
|
+
"client_name",
|
|
35100
|
+
"client_metadata_url",
|
|
35101
|
+
"token_endpoint_auth_method",
|
|
35102
|
+
"application_type",
|
|
35103
|
+
"user_agent",
|
|
35104
|
+
"flow",
|
|
35105
|
+
"scope"
|
|
35106
|
+
];
|
|
35107
|
+
/**
|
|
35108
|
+
* Copies the `oauth` mapping of a server, keeping only the keys Hermes reads.
|
|
35109
|
+
*
|
|
35110
|
+
* Hermes reads a single `scope` string, never a `scopes` list; earlier Rulesync
|
|
35111
|
+
* versions wrote `scopes` regardless, which Hermes silently ignored. A `scopes`
|
|
35112
|
+
* list is therefore folded into `scope` (space-separated, the OAuth wire form)
|
|
35113
|
+
* when no `scope` is given, in both directions, so an authored list finally
|
|
35114
|
+
* reaches the authorization server and an old `config.yaml` imports cleanly.
|
|
34621
35115
|
*/
|
|
34622
35116
|
function copyHermesOauth(source) {
|
|
34623
35117
|
if (!isRecord$1(source)) return;
|
|
34624
35118
|
const oauth = {};
|
|
34625
|
-
for (const key of [
|
|
34626
|
-
|
|
34627
|
-
|
|
34628
|
-
|
|
34629
|
-
"client_secret"
|
|
34630
|
-
]) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
34631
|
-
if (typeof source.redirect_port === "number") oauth.redirect_port = source.redirect_port;
|
|
34632
|
-
if (isStringArray$2(source.scopes)) oauth.scopes = source.scopes;
|
|
35119
|
+
for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
35120
|
+
if (typeof oauth.scope !== "string" && isStringArray$2(source.scopes)) oauth.scope = source.scopes.join(" ");
|
|
35121
|
+
for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
|
|
35122
|
+
if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
|
|
34633
35123
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
34634
35124
|
}
|
|
35125
|
+
const HERMES_OPAQUE_MAPPING_KEYS = [
|
|
35126
|
+
"sampling",
|
|
35127
|
+
"elicitation",
|
|
35128
|
+
"identity_header",
|
|
35129
|
+
"lifecycle"
|
|
35130
|
+
];
|
|
35131
|
+
/**
|
|
35132
|
+
* Copies the advanced Hermes-recognized per-server fields that have no canonical
|
|
35133
|
+
* alias — `auth` (`oauth` for OAuth 2.1/PKCE), mTLS `client_cert` (string PEM
|
|
35134
|
+
* path, or `[cert, key]`/`[cert, key, password]` list) and `client_key`,
|
|
35135
|
+
* `connect_timeout` (seconds), `supports_parallel_tool_calls`, `protocol`,
|
|
35136
|
+
* `lazy`, `keepalive_interval`, `elicitation`, `trust`, and `identity_header` —
|
|
35137
|
+
* verbatim from `source` to `target`. Field names are identical on both sides
|
|
35138
|
+
* (the canonical `McpServerSchema` is a `looseObject`), so this serves export
|
|
35139
|
+
* and import alike. See the Hermes mcp-config-reference.
|
|
35140
|
+
*/
|
|
34635
35141
|
function copyHermesAdvancedFields(source, target) {
|
|
34636
35142
|
let copied = false;
|
|
34637
35143
|
if (typeof source.auth === "string") {
|
|
@@ -34663,6 +35169,14 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
34663
35169
|
target[key] = source[key];
|
|
34664
35170
|
copied = true;
|
|
34665
35171
|
}
|
|
35172
|
+
if (typeof source.protocol === "string") {
|
|
35173
|
+
target.protocol = source.protocol;
|
|
35174
|
+
copied = true;
|
|
35175
|
+
}
|
|
35176
|
+
if (typeof source.lazy === "boolean") {
|
|
35177
|
+
target.lazy = source.lazy;
|
|
35178
|
+
copied = true;
|
|
35179
|
+
}
|
|
34666
35180
|
if (typeof source.ssl_verify === "boolean" || typeof source.ssl_verify === "string") {
|
|
34667
35181
|
target.ssl_verify = source.ssl_verify;
|
|
34668
35182
|
copied = true;
|
|
@@ -34671,25 +35185,20 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
34671
35185
|
target.skip_preflight = source.skip_preflight;
|
|
34672
35186
|
copied = true;
|
|
34673
35187
|
}
|
|
34674
|
-
if (isPlainObject$1(source.sampling)) {
|
|
34675
|
-
target.sampling = omitPrototypePollutionKeys(structuredClone(source.sampling));
|
|
34676
|
-
copied = true;
|
|
34677
|
-
}
|
|
34678
35188
|
if (typeof source.keepalive_interval === "number") {
|
|
34679
35189
|
target.keepalive_interval = source.keepalive_interval;
|
|
34680
35190
|
copied = true;
|
|
34681
35191
|
}
|
|
34682
|
-
if (isPlainObject$1(source.elicitation)) {
|
|
34683
|
-
target.elicitation = omitPrototypePollutionKeys(structuredClone(source.elicitation));
|
|
34684
|
-
copied = true;
|
|
34685
|
-
}
|
|
34686
35192
|
if (typeof source.trust === "string") {
|
|
34687
35193
|
target.trust = source.trust;
|
|
34688
35194
|
copied = true;
|
|
34689
35195
|
}
|
|
34690
|
-
|
|
34691
|
-
|
|
34692
|
-
|
|
35196
|
+
for (const key of HERMES_OPAQUE_MAPPING_KEYS) {
|
|
35197
|
+
const mapping = source[key];
|
|
35198
|
+
if (isPlainObject$1(mapping)) {
|
|
35199
|
+
target[key] = omitPrototypePollutionKeys(structuredClone(mapping));
|
|
35200
|
+
copied = true;
|
|
35201
|
+
}
|
|
34693
35202
|
}
|
|
34694
35203
|
return copied;
|
|
34695
35204
|
}
|
|
@@ -35473,6 +35982,7 @@ function toKimiCodeServer({ name, server, logger }) {
|
|
|
35473
35982
|
"headers",
|
|
35474
35983
|
"bearerTokenEnvVar",
|
|
35475
35984
|
"enabled",
|
|
35985
|
+
"deferred",
|
|
35476
35986
|
"startupTimeoutMs",
|
|
35477
35987
|
"toolTimeoutMs",
|
|
35478
35988
|
"enabledTools",
|
|
@@ -36130,7 +36640,12 @@ const OpencodeMcpRemoteServerSchema = z.looseObject({
|
|
|
36130
36640
|
headers: z.optional(z.record(z.string(), z.string())),
|
|
36131
36641
|
enabled: z._default(z.boolean(), true)
|
|
36132
36642
|
});
|
|
36133
|
-
const OPENCODE_PASSTHROUGH_SERVER_FIELDS = [
|
|
36643
|
+
const OPENCODE_PASSTHROUGH_SERVER_FIELDS = [
|
|
36644
|
+
"timeout",
|
|
36645
|
+
"oauth",
|
|
36646
|
+
"protocol",
|
|
36647
|
+
"codemode"
|
|
36648
|
+
];
|
|
36134
36649
|
const OPENCODE_MCP_TRANSPORT_KEYS = [
|
|
36135
36650
|
...Object.keys(OpencodeMcpLocalServerSchema.def.shape),
|
|
36136
36651
|
...Object.keys(OpencodeMcpRemoteServerSchema.def.shape),
|
|
@@ -36272,7 +36787,7 @@ function warnAboutToggleDroppedKeys(serverName, serverConfig, logger) {
|
|
|
36272
36787
|
* - env -> environment
|
|
36273
36788
|
* - disabled -> enabled (inverted)
|
|
36274
36789
|
* - enabledTools/disabledTools -> top-level tools map (with server name prefix)
|
|
36275
|
-
* - OpenCode-supported extras (timeout, oauth) -> passed through verbatim
|
|
36790
|
+
* - OpenCode-supported extras (timeout, oauth, protocol, codemode) -> passed through verbatim
|
|
36276
36791
|
*/
|
|
36277
36792
|
function convertServerToOpencodeFormat(serverName, serverConfig, existingEntry, logger) {
|
|
36278
36793
|
const serverRecord = serverConfig;
|
|
@@ -39265,7 +39780,7 @@ var ToolPermissions = class extends ToolFile {
|
|
|
39265
39780
|
* builtin of that name). It lives in the shared Amp settings file:
|
|
39266
39781
|
* `.amp/settings.json` (project) and `~/.config/amp/settings.json` (global).
|
|
39267
39782
|
*
|
|
39268
|
-
* Reference: https://ampcode.com/
|
|
39783
|
+
* Reference: https://ampcode.com/docs/cli/settings ("amp.tools.disable").
|
|
39269
39784
|
*/
|
|
39270
39785
|
const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
|
|
39271
39786
|
/**
|
|
@@ -39276,19 +39791,26 @@ const AMP_TOOLS_DISABLE_KEY = "amp.tools.disable";
|
|
|
39276
39791
|
* glob. Rules are evaluated **first-match-wins**. It lives in the same shared
|
|
39277
39792
|
* Amp settings file as `amp.tools.disable`.
|
|
39278
39793
|
*
|
|
39279
|
-
* `amp.permissions` is Amp's
|
|
39280
|
-
*
|
|
39281
|
-
* argument-specific `reject` rules (the simpler `amp.tools.disable` array
|
|
39282
|
-
* only disable whole tools).
|
|
39794
|
+
* `amp.permissions` is Amp's legacy/backwards-compat surface — it remains
|
|
39795
|
+
* functional in the shipped CLI and is the only place to express `allow`/`ask`
|
|
39796
|
+
* and argument-specific `reject` rules (the simpler `amp.tools.disable` array
|
|
39797
|
+
* can only disable whole tools). The web docs no longer describe it (the
|
|
39798
|
+
* permissions section only points at custom plugins), but the shipped CLI
|
|
39799
|
+
* still lists it in its built-in settings reference (`amp --help`) and
|
|
39800
|
+
* evaluates it through `amp permissions list|test`.
|
|
39283
39801
|
*
|
|
39284
|
-
* Reference: https://ampcode.com/
|
|
39802
|
+
* Reference: https://ampcode.com/docs/tools#permissions and
|
|
39803
|
+
* https://ampcode.com/docs/customize/plugins#example-plugin-permissions.
|
|
39285
39804
|
*/
|
|
39286
39805
|
const AMP_PERMISSIONS_KEY = "amp.permissions";
|
|
39287
39806
|
/**
|
|
39288
39807
|
* The `amp.guardedFiles.allowlist` array (file globs allowed without
|
|
39289
39808
|
* confirmation), `amp.dangerouslyAllowAll` boolean (disable all confirmation),
|
|
39290
39809
|
* and `amp.mcpPermissions` array — sibling settings authored through the `amp`
|
|
39291
|
-
* permissions override.
|
|
39810
|
+
* permissions override. Only `amp.mcpPermissions` is still on the web docs
|
|
39811
|
+
* (https://ampcode.com/docs/cli/settings); the other two, like
|
|
39812
|
+
* `amp.permissions`, survive only in the CLI's built-in settings reference
|
|
39813
|
+
* (see `AMP_PERMISSIONS_KEY`).
|
|
39292
39814
|
*/
|
|
39293
39815
|
const AMP_GUARDED_FILES_ALLOWLIST_KEY = "amp.guardedFiles.allowlist";
|
|
39294
39816
|
const AMP_DANGEROUSLY_ALLOW_ALL_KEY = "amp.dangerouslyAllowAll";
|
|
@@ -42617,6 +43139,8 @@ const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
|
|
|
42617
43139
|
const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_EXTENDABLE_BASELINE_PROFILES);
|
|
42618
43140
|
const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
|
|
42619
43141
|
const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
|
|
43142
|
+
const CODEX_RETIRED_APPROVAL_POLICY = "untrusted";
|
|
43143
|
+
const CODEX_DEPRECATED_APPROVAL_POLICY = "on-failure";
|
|
42620
43144
|
const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
|
|
42621
43145
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
42622
43146
|
const CODEX_GIT_WRITE_RULES = { ".git/**": "write" };
|
|
@@ -42740,6 +43264,10 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
42740
43264
|
domainsHadUnknown
|
|
42741
43265
|
});
|
|
42742
43266
|
const override = extractCodexcliOverride(table);
|
|
43267
|
+
if (override.approval_policy === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43268
|
+
warnWithFallback(void 0, `${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}").`);
|
|
43269
|
+
delete override.approval_policy;
|
|
43270
|
+
}
|
|
42743
43271
|
if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
|
|
42744
43272
|
if (defaultProfile === CODEX_DANGER_FULL_ACCESS_BASELINE) override.base_permission_profile = CODEX_DANGER_FULL_ACCESS_BASELINE;
|
|
42745
43273
|
const result = Object.keys(override).length > 0 ? {
|
|
@@ -43028,6 +43556,10 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
|
|
|
43028
43556
|
continue;
|
|
43029
43557
|
}
|
|
43030
43558
|
if (value === void 0) continue;
|
|
43559
|
+
if (key === "approval_policy" && !isWritableApprovalPolicy({
|
|
43560
|
+
value,
|
|
43561
|
+
logger
|
|
43562
|
+
})) continue;
|
|
43031
43563
|
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.`);
|
|
43032
43564
|
const existingValue = existing[key];
|
|
43033
43565
|
patch[key] = isPlainObject(existingValue) && isPlainObject(value) ? {
|
|
@@ -43035,12 +43567,37 @@ function computeCodexcliOverridePatch({ existing, override, logger }) {
|
|
|
43035
43567
|
...value
|
|
43036
43568
|
} : value;
|
|
43037
43569
|
}
|
|
43570
|
+
fillCodexcliDefaults({
|
|
43571
|
+
existing,
|
|
43572
|
+
patch,
|
|
43573
|
+
logger
|
|
43574
|
+
});
|
|
43575
|
+
return patch;
|
|
43576
|
+
}
|
|
43577
|
+
function fillCodexcliDefaults({ existing, patch, logger }) {
|
|
43038
43578
|
const defaults = {
|
|
43039
43579
|
approval_policy: CODEX_DEFAULT_APPROVAL_POLICY,
|
|
43040
43580
|
approvals_reviewer: CODEX_DEFAULT_APPROVALS_REVIEWER
|
|
43041
43581
|
};
|
|
43042
|
-
for (const [key, value] of Object.entries(defaults))
|
|
43043
|
-
|
|
43582
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
43583
|
+
if (patch[key] !== void 0) continue;
|
|
43584
|
+
if (existing[key] === void 0) {
|
|
43585
|
+
patch[key] = value;
|
|
43586
|
+
continue;
|
|
43587
|
+
}
|
|
43588
|
+
if (key === "approval_policy" && existing[key] === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43589
|
+
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}").`);
|
|
43590
|
+
patch[key] = value;
|
|
43591
|
+
}
|
|
43592
|
+
}
|
|
43593
|
+
}
|
|
43594
|
+
function isWritableApprovalPolicy({ value, logger }) {
|
|
43595
|
+
if (value === CODEX_RETIRED_APPROVAL_POLICY) {
|
|
43596
|
+
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}").`);
|
|
43597
|
+
return false;
|
|
43598
|
+
}
|
|
43599
|
+
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.`);
|
|
43600
|
+
return true;
|
|
43044
43601
|
}
|
|
43045
43602
|
function extractCodexcliOverride(table) {
|
|
43046
43603
|
const override = {};
|
|
@@ -45133,6 +45690,27 @@ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
|
|
|
45133
45690
|
* @see https://github.com/langchain-ai/deepagents `config_manifest.py`
|
|
45134
45691
|
*/
|
|
45135
45692
|
const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
|
|
45693
|
+
/**
|
|
45694
|
+
* `[mcp]` holds dcode's per-server trust for the MCP servers a project's
|
|
45695
|
+
* `.mcp.json` / `.deepagents/.mcp.json` declares. `load_mcp_server_trust_lists`
|
|
45696
|
+
* reads it from the user-level `config.toml` only — never from a project file —
|
|
45697
|
+
* so the global adapter is the one place rulesync can author it.
|
|
45698
|
+
*/
|
|
45699
|
+
const MCP_TABLE_KEY = "mcp";
|
|
45700
|
+
/**
|
|
45701
|
+
* Keys lifted back into the override on import. `disabled_project_servers` is
|
|
45702
|
+
* the deny list: a name on it is always rejected, over any approval or trust.
|
|
45703
|
+
*/
|
|
45704
|
+
const DEEPAGENTS_MCP_KEYS = ["disabled_project_servers"];
|
|
45705
|
+
const MCP_DISABLED_PROJECT_SERVERS_KEY = "disabled_project_servers";
|
|
45706
|
+
/**
|
|
45707
|
+
* dcode's own store of the project servers the user approved at the prompt,
|
|
45708
|
+
* each bound to a repository or worktree and a definition fingerprint. It is
|
|
45709
|
+
* machine-local state and a way to pre-approve a project's servers, so a
|
|
45710
|
+
* repository's `.rulesync/permissions.jsonc` must not reach it: generate drops
|
|
45711
|
+
* the key here, and import never lifts it (`DEEPAGENTS_MCP_KEYS`).
|
|
45712
|
+
*/
|
|
45713
|
+
const MCP_APPROVALS_KEY = "enabled_project_server_approvals";
|
|
45136
45714
|
const ALLOW_ALL_SENTINEL = "all";
|
|
45137
45715
|
const RECOMMENDED_SENTINEL = "recommended";
|
|
45138
45716
|
const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
|
|
@@ -45205,7 +45783,9 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
|
|
|
45205
45783
|
* lifted back into the override on import. The same override carries
|
|
45206
45784
|
* `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
|
|
45207
45785
|
* which decide whether the Python in a checked-out project's
|
|
45208
|
-
* `.deepagents/extensions/` is imported into the agent process
|
|
45786
|
+
* `.deepagents/extensions/` is imported into the agent process, and
|
|
45787
|
+
* `[mcp].disabled_project_servers`, the names of project MCP servers dcode
|
|
45788
|
+
* always rejects.
|
|
45209
45789
|
*
|
|
45210
45790
|
* On **import** the allowlist comes back as `bash` `allow` rules named by the
|
|
45211
45791
|
* executable — skipping any entry dcode could not match in the first place, so
|
|
@@ -45303,17 +45883,9 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
|
|
|
45303
45883
|
if (Object.keys(shell).length > 0) settings[SHELL_TABLE_KEY] = shell;
|
|
45304
45884
|
else delete settings[SHELL_TABLE_KEY];
|
|
45305
45885
|
}
|
|
45306
|
-
|
|
45307
|
-
if (isPlainObject$1(startupOverride) && Object.keys(startupOverride).length > 0) mergeStartupOverride({
|
|
45886
|
+
mergeDeepagentsOverrides({
|
|
45308
45887
|
settings,
|
|
45309
|
-
|
|
45310
|
-
filePath,
|
|
45311
|
-
logger
|
|
45312
|
-
});
|
|
45313
|
-
const extensionsOverride = config.deepagents?.extensions;
|
|
45314
|
-
if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
|
|
45315
|
-
settings,
|
|
45316
|
-
extensionsOverride,
|
|
45888
|
+
override: config.deepagents,
|
|
45317
45889
|
filePath,
|
|
45318
45890
|
logger
|
|
45319
45891
|
});
|
|
@@ -45345,10 +45917,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
|
|
|
45345
45917
|
extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
|
|
45346
45918
|
selfPath
|
|
45347
45919
|
});
|
|
45920
|
+
const mcpOverride = liftMcpOverride({
|
|
45921
|
+
mcp: isPlainObject$1(settings[MCP_TABLE_KEY]) ? settings[MCP_TABLE_KEY] : {},
|
|
45922
|
+
selfPath
|
|
45923
|
+
});
|
|
45348
45924
|
const result = { ...config };
|
|
45349
45925
|
const deepagents = {};
|
|
45350
45926
|
if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
|
|
45351
45927
|
if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
|
|
45928
|
+
if (Object.keys(mcpOverride).length > 0) deepagents.mcp = mcpOverride;
|
|
45352
45929
|
if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
|
|
45353
45930
|
return RulesyncPermissions.fromImportedFileContent({
|
|
45354
45931
|
outputRoot: getDeepagentsRulesyncOutputRoot({
|
|
@@ -45456,7 +46033,7 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
|
|
|
45456
46033
|
* read — a wrapper rather than the bare value, so a legitimately falsy setting
|
|
45457
46034
|
* is not mistaken for a rejection.
|
|
45458
46035
|
*/
|
|
45459
|
-
function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
|
|
46036
|
+
function liftOverrideTable({ table, tableKey, keys, normalize, selfPath, describeRejection = `deepagents-cli falls back to its own default for a '[${tableKey}]' value it cannot read` }) {
|
|
45460
46037
|
const override = {};
|
|
45461
46038
|
const rejected = [];
|
|
45462
46039
|
for (const key of keys) {
|
|
@@ -45469,7 +46046,7 @@ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
|
|
|
45469
46046
|
if (normalized) override[key] = normalized.value;
|
|
45470
46047
|
else rejected.push(`${key} = ${JSON.stringify(value)}`);
|
|
45471
46048
|
}
|
|
45472
|
-
if (rejected.length > 0) warnWithFallback(void 0,
|
|
46049
|
+
if (rejected.length > 0) warnWithFallback(void 0, `${describeRejection}, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
|
|
45473
46050
|
return override;
|
|
45474
46051
|
}
|
|
45475
46052
|
/**
|
|
@@ -45517,6 +46094,37 @@ function liftExtensionsOverride({ extensions, selfPath }) {
|
|
|
45517
46094
|
});
|
|
45518
46095
|
}
|
|
45519
46096
|
/**
|
|
46097
|
+
* Read a `[mcp]` name list the way `_toml_str_list` reads it: a bare string
|
|
46098
|
+
* is split on commas, names are trimmed and empty ones dropped, and a
|
|
46099
|
+
* non-string element of a list is dropped while the names around it survive.
|
|
46100
|
+
* Anything else (a number, a table, a bool) is a value dcode cannot read as
|
|
46101
|
+
* names at all, so `null` says to leave it behind.
|
|
46102
|
+
*/
|
|
46103
|
+
function readMcpServerNames(value) {
|
|
46104
|
+
if (typeof value === "string") return value.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
46105
|
+
if (!Array.isArray(value)) return null;
|
|
46106
|
+
return value.filter((name) => typeof name === "string").map((name) => name.trim()).filter((name) => name.length > 0);
|
|
46107
|
+
}
|
|
46108
|
+
/**
|
|
46109
|
+
* Lift the `[mcp]` keys rulesync models back into the `deepagents` override.
|
|
46110
|
+
* The deny list comes back as the array dcode resolves it to, so the
|
|
46111
|
+
* comma-separated string form it also accepts does not survive as one bogus
|
|
46112
|
+
* name in a permissions file the canonical schema types as `string[]`.
|
|
46113
|
+
*/
|
|
46114
|
+
function liftMcpOverride({ mcp, selfPath }) {
|
|
46115
|
+
return liftOverrideTable({
|
|
46116
|
+
table: mcp,
|
|
46117
|
+
tableKey: MCP_TABLE_KEY,
|
|
46118
|
+
keys: DEEPAGENTS_MCP_KEYS,
|
|
46119
|
+
normalize: ({ value }) => {
|
|
46120
|
+
const names = readMcpServerNames(value);
|
|
46121
|
+
return names ? { value: names } : null;
|
|
46122
|
+
},
|
|
46123
|
+
selfPath,
|
|
46124
|
+
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`
|
|
46125
|
+
});
|
|
46126
|
+
}
|
|
46127
|
+
/**
|
|
45520
46128
|
* Merge one block of the `deepagents` override into its `config.toml` table,
|
|
45521
46129
|
* preserving every other key of that table. A table that is not a table at all
|
|
45522
46130
|
* is something rulesync did not write and cannot merge into, so it is left
|
|
@@ -45565,6 +46173,38 @@ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, lo
|
|
|
45565
46173
|
if (unknownKeys.length === 0) return;
|
|
45566
46174
|
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.`);
|
|
45567
46175
|
}
|
|
46176
|
+
/** An override block with something in it to write. */
|
|
46177
|
+
function isSet(block) {
|
|
46178
|
+
return isPlainObject$1(block) && Object.keys(block).length > 0;
|
|
46179
|
+
}
|
|
46180
|
+
/**
|
|
46181
|
+
* Merge each block of the `deepagents` override into its `config.toml` table.
|
|
46182
|
+
* A block that is absent, not a table, or empty has nothing to write and is
|
|
46183
|
+
* skipped without touching the table it would have gone into.
|
|
46184
|
+
*/
|
|
46185
|
+
function mergeDeepagentsOverrides({ settings, override, filePath, logger }) {
|
|
46186
|
+
const startupOverride = override?.startup;
|
|
46187
|
+
if (isSet(startupOverride)) mergeStartupOverride({
|
|
46188
|
+
settings,
|
|
46189
|
+
startupOverride,
|
|
46190
|
+
filePath,
|
|
46191
|
+
logger
|
|
46192
|
+
});
|
|
46193
|
+
const extensionsOverride = override?.extensions;
|
|
46194
|
+
if (isSet(extensionsOverride)) mergeExtensionsOverride({
|
|
46195
|
+
settings,
|
|
46196
|
+
extensionsOverride,
|
|
46197
|
+
filePath,
|
|
46198
|
+
logger
|
|
46199
|
+
});
|
|
46200
|
+
const mcpOverride = override?.mcp;
|
|
46201
|
+
if (isSet(mcpOverride)) mergeMcpOverride({
|
|
46202
|
+
settings,
|
|
46203
|
+
mcpOverride,
|
|
46204
|
+
filePath,
|
|
46205
|
+
logger
|
|
46206
|
+
});
|
|
46207
|
+
}
|
|
45568
46208
|
/**
|
|
45569
46209
|
* Merge the `deepagents.startup` override into `[startup]`.
|
|
45570
46210
|
*/
|
|
@@ -45598,6 +46238,40 @@ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logge
|
|
|
45598
46238
|
});
|
|
45599
46239
|
}
|
|
45600
46240
|
/**
|
|
46241
|
+
* Merge the `deepagents.mcp` override into `[mcp]`. The approval store is
|
|
46242
|
+
* dropped (see `MCP_APPROVALS_KEY`); the deprecated flat
|
|
46243
|
+
* `enabled_project_servers` is not modeled but passes through like any other
|
|
46244
|
+
* unknown key, named by the unchecked-key warning — dcode ignores it anyway.
|
|
46245
|
+
*/
|
|
46246
|
+
function mergeMcpOverride({ settings, mcpOverride, filePath, logger }) {
|
|
46247
|
+
mergeOverrideTable({
|
|
46248
|
+
settings,
|
|
46249
|
+
tableKey: MCP_TABLE_KEY,
|
|
46250
|
+
override: mcpOverride,
|
|
46251
|
+
knownKeys: DEEPAGENTS_MCP_KEYS,
|
|
46252
|
+
isDroppedKey: (key) => key === MCP_APPROVALS_KEY,
|
|
46253
|
+
warnAboutRelaxations: warnAboutMcpRelaxations,
|
|
46254
|
+
filePath,
|
|
46255
|
+
logger
|
|
46256
|
+
});
|
|
46257
|
+
}
|
|
46258
|
+
/**
|
|
46259
|
+
* Warn when the `deepagents` mcp override re-enables project MCP servers the
|
|
46260
|
+
* machine's global config had rejected. The deny list is written as a whole,
|
|
46261
|
+
* so a shorter list from a repository's `.rulesync/permissions.jsonc` would
|
|
46262
|
+
* otherwise silently lift a rejection the user had put in place — the very
|
|
46263
|
+
* fail-open dcode's own env merge refuses to allow.
|
|
46264
|
+
*/
|
|
46265
|
+
function warnAboutMcpRelaxations({ override, previous, filePath, logger }) {
|
|
46266
|
+
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.`);
|
|
46267
|
+
const written = override[MCP_DISABLED_PROJECT_SERVERS_KEY];
|
|
46268
|
+
if (written === void 0 || written === null) return;
|
|
46269
|
+
const kept = new Set(readMcpServerNames(written) ?? []);
|
|
46270
|
+
const reEnabled = (readMcpServerNames(previous[MCP_DISABLED_PROJECT_SERVERS_KEY]) ?? []).filter((name) => !kept.has(name));
|
|
46271
|
+
if (reEnabled.length === 0) return;
|
|
46272
|
+
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.`);
|
|
46273
|
+
}
|
|
46274
|
+
/**
|
|
45601
46275
|
* Name the value an override key replaces, so a setting the user had turned
|
|
45602
46276
|
* down is visible as such rather than reported as a bare new value.
|
|
45603
46277
|
*/
|
|
@@ -51375,6 +52049,18 @@ const TAKT_RUNTIME_TARGETS_KEY = "targets";
|
|
|
51375
52049
|
const TAKT_RUNTIME_AUTO_ROUTING_KEY = "auto_routing";
|
|
51376
52050
|
const TAKT_RUNTIME_PROFILE_KEY = "profile";
|
|
51377
52051
|
const TAKT_RUNTIME_OPTIONS_KEY = "options";
|
|
52052
|
+
const TAKT_RUNTIME_ASSIGNMENTS_KEY = "assignments";
|
|
52053
|
+
const TAKT_RUNTIME_LADDER_KEY = "ladder";
|
|
52054
|
+
const TAKT_RUNTIME_EXTENDS_KEY = "extends";
|
|
52055
|
+
const TAKT_RUNTIME_ROUTER_PROFILE_KEY = "router_profile";
|
|
52056
|
+
const TAKT_RUNTIME_POOLS_KEY = "pools";
|
|
52057
|
+
const TAKT_RUNTIME_CANDIDATES_KEY = "candidates";
|
|
52058
|
+
const TAKT_RUNTIME_FALLBACK_PROFILE_KEY = "fallback_profile";
|
|
52059
|
+
/** The one `targets` map that routes companion reviewers rather than agents. */
|
|
52060
|
+
const TAKT_RUNTIME_COMPANIONS_TARGET_KEY = "companions";
|
|
52061
|
+
const TAKT_RUNTIME_COMPANION_KEY = "companion";
|
|
52062
|
+
const TAKT_RUNTIME_ENABLED_KEY = "enabled";
|
|
52063
|
+
const TAKT_DEFAULT_COMPANION_ENABLED = false;
|
|
51378
52064
|
const TAKT_SECURITY_POLICIES = {
|
|
51379
52065
|
workflow_arpeggio: [
|
|
51380
52066
|
"custom_data_source_modules",
|
|
@@ -51628,21 +52314,156 @@ function runtimeProfiles(runtime) {
|
|
|
51628
52314
|
const profiles = provider[TAKT_RUNTIME_PROFILES_KEY];
|
|
51629
52315
|
return isPlainObject$1(profiles) ? profiles : {};
|
|
51630
52316
|
}
|
|
52317
|
+
/** A `targets` map (`personas`, `tags`, …) with at least one non-empty entry. */
|
|
52318
|
+
function hasTargetContent(targets) {
|
|
52319
|
+
return isPlainObject$1(targets) && Object.values(targets).some(hasEntries);
|
|
52320
|
+
}
|
|
52321
|
+
/** The profile names an assignment points at: `profile` plus every `ladder` rung. */
|
|
52322
|
+
function assignmentProfileNames(assignment) {
|
|
52323
|
+
if (!isPlainObject$1(assignment)) return [];
|
|
52324
|
+
const names = [];
|
|
52325
|
+
const profile = assignment[TAKT_RUNTIME_PROFILE_KEY];
|
|
52326
|
+
if (typeof profile === "string") names.push(profile);
|
|
52327
|
+
const ladder = assignment[TAKT_RUNTIME_LADDER_KEY];
|
|
52328
|
+
if (Array.isArray(ladder)) names.push(...ladder.filter((rung) => typeof rung === "string"));
|
|
52329
|
+
return names;
|
|
52330
|
+
}
|
|
52331
|
+
/** The assignments under a `targets` map, split by whether they route companions. */
|
|
52332
|
+
function targetAssignments(targets, { companions }) {
|
|
52333
|
+
if (!isPlainObject$1(targets)) return [];
|
|
52334
|
+
return Object.entries(targets).filter(([name]) => name === TAKT_RUNTIME_COMPANIONS_TARGET_KEY === companions).flatMap(([, targetMap]) => isPlainObject$1(targetMap) ? Object.values(targetMap) : []);
|
|
52335
|
+
}
|
|
52336
|
+
/**
|
|
52337
|
+
* Record the profiles a `defaults` + `targets` pair (the top-level provider
|
|
52338
|
+
* section or one named assignment set) reaches, keeping companion roots apart
|
|
52339
|
+
* from the roots agents use.
|
|
52340
|
+
*/
|
|
52341
|
+
function addAssignmentSetRoots({ set, companionRoots, agentRoots }) {
|
|
52342
|
+
for (const name of assignmentProfileNames(set[TAKT_RUNTIME_DEFAULTS_KEY])) agentRoots.add(name);
|
|
52343
|
+
const targets = set[TAKT_RUNTIME_TARGETS_KEY];
|
|
52344
|
+
for (const assignment of targetAssignments(targets, { companions: false })) for (const name of assignmentProfileNames(assignment)) agentRoots.add(name);
|
|
52345
|
+
for (const assignment of targetAssignments(targets, { companions: true })) for (const name of assignmentProfileNames(assignment)) companionRoots.add(name);
|
|
52346
|
+
}
|
|
52347
|
+
/** The profiles `auto_routing` reaches: the router plus every pool candidate/fallback. */
|
|
52348
|
+
function addAutoRoutingRoots(autoRouting, roots) {
|
|
52349
|
+
if (!isPlainObject$1(autoRouting)) return;
|
|
52350
|
+
const routerProfile = autoRouting[TAKT_RUNTIME_ROUTER_PROFILE_KEY];
|
|
52351
|
+
if (typeof routerProfile === "string") roots.add(routerProfile);
|
|
52352
|
+
const pools = autoRouting[TAKT_RUNTIME_POOLS_KEY];
|
|
52353
|
+
if (!isPlainObject$1(pools)) return;
|
|
52354
|
+
for (const pool of Object.values(pools)) {
|
|
52355
|
+
if (!isPlainObject$1(pool)) continue;
|
|
52356
|
+
const candidates = pool[TAKT_RUNTIME_CANDIDATES_KEY];
|
|
52357
|
+
for (const candidate of Array.isArray(candidates) ? candidates : []) for (const name of assignmentProfileNames(candidate)) roots.add(name);
|
|
52358
|
+
const fallback = pool[TAKT_RUNTIME_FALLBACK_PROFILE_KEY];
|
|
52359
|
+
if (typeof fallback === "string") roots.add(fallback);
|
|
52360
|
+
}
|
|
52361
|
+
}
|
|
52362
|
+
/** Every profile reachable from `roots` by following `extends`. */
|
|
52363
|
+
function profileClosure(profiles, roots) {
|
|
52364
|
+
const closure = /* @__PURE__ */ new Set();
|
|
52365
|
+
const visit = (name) => {
|
|
52366
|
+
if (closure.has(name)) return;
|
|
52367
|
+
closure.add(name);
|
|
52368
|
+
const profile = profiles[name];
|
|
52369
|
+
const parent = isPlainObject$1(profile) ? profile[TAKT_RUNTIME_EXTENDS_KEY] : void 0;
|
|
52370
|
+
if (typeof parent === "string") visit(parent);
|
|
52371
|
+
};
|
|
52372
|
+
for (const root of roots) visit(root);
|
|
52373
|
+
return closure;
|
|
52374
|
+
}
|
|
52375
|
+
/** The `targets` map minus its `companions` entry (`undefined` when not a map). */
|
|
52376
|
+
function withoutCompanionTargets(targets) {
|
|
52377
|
+
if (!isPlainObject$1(targets)) return;
|
|
52378
|
+
const { [TAKT_RUNTIME_COMPANIONS_TARGET_KEY]: _companions, ...remaining } = targets;
|
|
52379
|
+
return remaining;
|
|
52380
|
+
}
|
|
52381
|
+
/**
|
|
52382
|
+
* The named assignment sets with their companion targets removed; a set left
|
|
52383
|
+
* with neither `defaults` nor a non-empty `targets` map is dropped, and
|
|
52384
|
+
* `undefined` means none survived.
|
|
52385
|
+
*/
|
|
52386
|
+
function withoutCompanionAssignments(assignments) {
|
|
52387
|
+
if (!isPlainObject$1(assignments)) return;
|
|
52388
|
+
const remaining = {};
|
|
52389
|
+
for (const [name, set] of Object.entries(assignments)) {
|
|
52390
|
+
if (!isPlainObject$1(set)) continue;
|
|
52391
|
+
const defaults = set[TAKT_RUNTIME_DEFAULTS_KEY];
|
|
52392
|
+
const targets = withoutCompanionTargets(set[TAKT_RUNTIME_TARGETS_KEY]);
|
|
52393
|
+
const keepTargets = hasTargetContent(targets);
|
|
52394
|
+
if (defaults === void 0 && !keepTargets) continue;
|
|
52395
|
+
remaining[name] = {
|
|
52396
|
+
...defaults === void 0 ? {} : { [TAKT_RUNTIME_DEFAULTS_KEY]: defaults },
|
|
52397
|
+
...keepTargets ? { [TAKT_RUNTIME_TARGETS_KEY]: targets } : {}
|
|
52398
|
+
};
|
|
52399
|
+
}
|
|
52400
|
+
return Object.keys(remaining).length > 0 ? remaining : void 0;
|
|
52401
|
+
}
|
|
52402
|
+
/** Whether the section (or any of its named assignment sets) routes companions at all. */
|
|
52403
|
+
function hasCompanionTargets(section) {
|
|
52404
|
+
const routesCompanions = (set) => {
|
|
52405
|
+
const targets = isPlainObject$1(set) ? set[TAKT_RUNTIME_TARGETS_KEY] : void 0;
|
|
52406
|
+
return isPlainObject$1(targets) && targets[TAKT_RUNTIME_COMPANIONS_TARGET_KEY] !== void 0;
|
|
52407
|
+
};
|
|
52408
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52409
|
+
return routesCompanions(section) || isPlainObject$1(assignments) && Object.values(assignments).some(routesCompanions);
|
|
52410
|
+
}
|
|
52411
|
+
/**
|
|
52412
|
+
* The `provider:` section as Takt itself evaluates it, mirroring upstream's
|
|
52413
|
+
* `getEffectiveProviderSection`: unless `companion.enabled` is `true`
|
|
52414
|
+
* (companions are opt-in), the `targets.companions` maps — top-level and inside
|
|
52415
|
+
* every named assignment set — are dropped, along with each profile that only a
|
|
52416
|
+
* companion target reaches through `extends`. A profile an agent assignment
|
|
52417
|
+
* (`defaults`, the other `targets` maps, `auto_routing`) also reaches is kept.
|
|
52418
|
+
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/schema.ts
|
|
52419
|
+
*/
|
|
52420
|
+
function effectiveRuntimeProviderSection(runtime) {
|
|
52421
|
+
const section = runtime[TAKT_RUNTIME_PROVIDER_KEY];
|
|
52422
|
+
if (!isPlainObject$1(section)) return;
|
|
52423
|
+
const companion = runtime[TAKT_RUNTIME_COMPANION_KEY];
|
|
52424
|
+
if (((isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0) ?? TAKT_DEFAULT_COMPANION_ENABLED) === true || !hasCompanionTargets(section)) return section;
|
|
52425
|
+
const profiles = runtimeProfiles(runtime);
|
|
52426
|
+
const companionRoots = /* @__PURE__ */ new Set();
|
|
52427
|
+
const agentRoots = /* @__PURE__ */ new Set();
|
|
52428
|
+
addAssignmentSetRoots({
|
|
52429
|
+
set: section,
|
|
52430
|
+
companionRoots,
|
|
52431
|
+
agentRoots
|
|
52432
|
+
});
|
|
52433
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52434
|
+
for (const set of isPlainObject$1(assignments) ? Object.values(assignments) : []) if (isPlainObject$1(set)) addAssignmentSetRoots({
|
|
52435
|
+
set,
|
|
52436
|
+
companionRoots,
|
|
52437
|
+
agentRoots
|
|
52438
|
+
});
|
|
52439
|
+
addAutoRoutingRoots(section[TAKT_RUNTIME_AUTO_ROUTING_KEY], agentRoots);
|
|
52440
|
+
const companionClosure = profileClosure(profiles, companionRoots);
|
|
52441
|
+
const agentClosure = profileClosure(profiles, agentRoots);
|
|
52442
|
+
const effective = {
|
|
52443
|
+
...section,
|
|
52444
|
+
[TAKT_RUNTIME_PROFILES_KEY]: Object.fromEntries(Object.entries(profiles).filter(([name]) => !companionClosure.has(name) || agentClosure.has(name)))
|
|
52445
|
+
};
|
|
52446
|
+
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];
|
|
52447
|
+
else effective[key] = value;
|
|
52448
|
+
return effective;
|
|
52449
|
+
}
|
|
51631
52450
|
/**
|
|
51632
52451
|
* Whether a parsed `runtime.yaml` puts Takt into runtime mode, mirroring
|
|
51633
|
-
* upstream's mode detection: the `provider:` section
|
|
51634
|
-
*
|
|
51635
|
-
*
|
|
51636
|
-
*
|
|
51637
|
-
*
|
|
52452
|
+
* upstream's mode detection: the effective `provider:` section (see
|
|
52453
|
+
* `effectiveRuntimeProviderSection` — companion-only routing does not count
|
|
52454
|
+
* while companions are disabled) must carry an actual assignment — a non-empty
|
|
52455
|
+
* `defaults`, `profiles` or `auto_routing`, a `targets` map with at least one
|
|
52456
|
+
* non-empty nested map, or a named assignment set with a non-empty `defaults`
|
|
52457
|
+
* or such a `targets` map. The file existing is not enough, and empty nested
|
|
52458
|
+
* maps (`defaults: {}`, `targets: { personas: {} }`) must not flip the mode.
|
|
51638
52459
|
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/mode.ts
|
|
51639
52460
|
*/
|
|
51640
52461
|
function isRuntimeModeActive(runtime) {
|
|
51641
|
-
const
|
|
51642
|
-
if (
|
|
51643
|
-
|
|
51644
|
-
const
|
|
51645
|
-
return
|
|
52462
|
+
const section = effectiveRuntimeProviderSection(runtime);
|
|
52463
|
+
if (section === void 0) return false;
|
|
52464
|
+
const assignments = section[TAKT_RUNTIME_ASSIGNMENTS_KEY];
|
|
52465
|
+
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])));
|
|
52466
|
+
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;
|
|
51646
52467
|
}
|
|
51647
52468
|
/** The provider named by the runtime profile Takt would use by default. */
|
|
51648
52469
|
function resolveRuntimeProvider(runtime) {
|
|
@@ -51665,10 +52486,13 @@ function resolveRuntimeProvider(runtime) {
|
|
|
51665
52486
|
* shape the `takt` override uses. Each profile's options belong to that
|
|
51666
52487
|
* profile's own provider; when several profiles name the same provider they are
|
|
51667
52488
|
* merged in document order, so a later profile wins on a colliding option key.
|
|
52489
|
+
* Profiles Takt itself ignores (companion-only ones while companions are
|
|
52490
|
+
* disabled) are skipped, as they never reach an agent.
|
|
51668
52491
|
*/
|
|
51669
52492
|
function collectRuntimeProviderOptions(runtime) {
|
|
51670
52493
|
const collected = {};
|
|
51671
|
-
|
|
52494
|
+
const profiles = effectiveRuntimeProviderSection(runtime)?.[TAKT_RUNTIME_PROFILES_KEY];
|
|
52495
|
+
for (const profile of Object.values(isPlainObject$1(profiles) ? profiles : {})) {
|
|
51672
52496
|
if (!isPlainObject$1(profile)) continue;
|
|
51673
52497
|
const providerName = profile[TAKT_RUNTIME_PROVIDER_KEY];
|
|
51674
52498
|
const options = profile[TAKT_RUNTIME_OPTIONS_KEY];
|
|
@@ -51707,13 +52531,20 @@ async function readTaktRuntimeFile({ filePath, logger }) {
|
|
|
51707
52531
|
};
|
|
51708
52532
|
}
|
|
51709
52533
|
}
|
|
52534
|
+
/** A map-valued key of a `provider:` section, or `undefined` when it is not a map. */
|
|
52535
|
+
function runtimeSectionMap(section, key) {
|
|
52536
|
+
return isPlainObject$1(section[key]) ? section[key] : void 0;
|
|
52537
|
+
}
|
|
51710
52538
|
/**
|
|
51711
52539
|
* Collapse the project and global `runtime.yaml` into the single document Takt
|
|
51712
|
-
* itself resolves against, matching upstream's loader: `profiles`
|
|
51713
|
-
* with the project definition of a same-named
|
|
51714
|
-
* while `defaults`, `targets` and `auto_routing` are
|
|
51715
|
-
* whole whenever it states them at all (`??`, so a
|
|
51716
|
-
* the global one rather than merging with it).
|
|
52540
|
+
* itself resolves against, matching upstream's loader: `profiles` and
|
|
52541
|
+
* `assignments` are unions with the project definition of a same-named entry
|
|
52542
|
+
* replacing the global one, while `defaults`, `targets` and `auto_routing` are
|
|
52543
|
+
* taken from the project file whole whenever it states them at all (`??`, so a
|
|
52544
|
+
* project `targets: {}` masks the global one rather than merging with it). The
|
|
52545
|
+
* top-level `companion.enabled` — which decides whether companion routing
|
|
52546
|
+
* counts for mode detection — is carried as upstream carries it: unset when
|
|
52547
|
+
* neither file states it, otherwise on only when neither file turns it off.
|
|
51717
52548
|
* https://github.com/nrslib/takt/blob/main/src/infra/config/runtime-provider/loader.ts
|
|
51718
52549
|
*
|
|
51719
52550
|
* Merging before mode detection matters in both directions: a project file
|
|
@@ -51729,14 +52560,15 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
|
|
|
51729
52560
|
const projectSection = sectionOf(project);
|
|
51730
52561
|
const globalSection = sectionOf(globalConfig);
|
|
51731
52562
|
const replaced = (key) => projectSection[key] ?? globalSection[key];
|
|
51732
|
-
const profilesOf = (section) => isPlainObject$1(section[TAKT_RUNTIME_PROFILES_KEY]) ? section[TAKT_RUNTIME_PROFILES_KEY] : void 0;
|
|
51733
|
-
const globalProfiles = profilesOf(globalSection);
|
|
51734
|
-
const projectProfiles = profilesOf(projectSection);
|
|
51735
52563
|
const provider = {};
|
|
51736
|
-
|
|
51737
|
-
|
|
51738
|
-
|
|
51739
|
-
|
|
52564
|
+
for (const key of [TAKT_RUNTIME_PROFILES_KEY, TAKT_RUNTIME_ASSIGNMENTS_KEY]) {
|
|
52565
|
+
const globalMap = runtimeSectionMap(globalSection, key);
|
|
52566
|
+
const projectMap = runtimeSectionMap(projectSection, key);
|
|
52567
|
+
if (globalMap !== void 0 || projectMap !== void 0) provider[key] = {
|
|
52568
|
+
...globalMap,
|
|
52569
|
+
...projectMap
|
|
52570
|
+
};
|
|
52571
|
+
}
|
|
51740
52572
|
for (const key of [
|
|
51741
52573
|
TAKT_RUNTIME_DEFAULTS_KEY,
|
|
51742
52574
|
TAKT_RUNTIME_TARGETS_KEY,
|
|
@@ -51745,7 +52577,17 @@ function mergeTaktRuntimeConfigs({ project, global: globalConfig }) {
|
|
|
51745
52577
|
const value = replaced(key);
|
|
51746
52578
|
if (value !== void 0) provider[key] = value;
|
|
51747
52579
|
}
|
|
51748
|
-
|
|
52580
|
+
const enabledOf = (config) => {
|
|
52581
|
+
const companion = config?.[TAKT_RUNTIME_COMPANION_KEY];
|
|
52582
|
+
return isPlainObject$1(companion) ? companion[TAKT_RUNTIME_ENABLED_KEY] : void 0;
|
|
52583
|
+
};
|
|
52584
|
+
const globalEnabled = enabledOf(globalConfig);
|
|
52585
|
+
const projectEnabled = enabledOf(project);
|
|
52586
|
+
const companion = globalEnabled === void 0 && projectEnabled === void 0 ? void 0 : { [TAKT_RUNTIME_ENABLED_KEY]: globalEnabled !== false && projectEnabled !== false };
|
|
52587
|
+
return {
|
|
52588
|
+
[TAKT_RUNTIME_PROVIDER_KEY]: provider,
|
|
52589
|
+
...companion === void 0 ? {} : { [TAKT_RUNTIME_COMPANION_KEY]: companion }
|
|
52590
|
+
};
|
|
51749
52591
|
}
|
|
51750
52592
|
/**
|
|
51751
52593
|
* The runtime provider configuration in force for a generate run: the project
|
|
@@ -57350,7 +58192,12 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
|
|
|
57350
58192
|
//#region src/features/skills/commandcode-skill.ts
|
|
57351
58193
|
const CommandcodeSkillFrontmatterSchema = z.looseObject({
|
|
57352
58194
|
name: z.string(),
|
|
57353
|
-
description: z.string()
|
|
58195
|
+
description: z.string(),
|
|
58196
|
+
license: z.optional(z.string()),
|
|
58197
|
+
compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
|
|
58198
|
+
metadata: z.optional(z.looseObject({})),
|
|
58199
|
+
"disable-model-invocation": z.optional(z.boolean()),
|
|
58200
|
+
"user-invocable": z.optional(z.boolean())
|
|
57354
58201
|
});
|
|
57355
58202
|
/**
|
|
57356
58203
|
* Represents a Command Code skill directory.
|
|
@@ -57433,10 +58280,35 @@ var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
|
|
|
57433
58280
|
const settablePaths = CommandcodeSkill.getSettablePaths({ global });
|
|
57434
58281
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
57435
58282
|
const { name: _sectionName, description: _sectionDescription, ...commandcodeSection } = rulesyncFrontmatter.commandcode ?? {};
|
|
58283
|
+
const license = resolveLicense({
|
|
58284
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58285
|
+
section: commandcodeSection
|
|
58286
|
+
});
|
|
58287
|
+
const compatibility = resolveCompatibility({
|
|
58288
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58289
|
+
section: commandcodeSection
|
|
58290
|
+
});
|
|
58291
|
+
const metadata = resolveMetadata({
|
|
58292
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58293
|
+
section: commandcodeSection
|
|
58294
|
+
});
|
|
58295
|
+
const disableModelInvocation = resolveDisableModelInvocation({
|
|
58296
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58297
|
+
section: commandcodeSection
|
|
58298
|
+
});
|
|
58299
|
+
const userInvocable = resolveUserInvocable({
|
|
58300
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
58301
|
+
section: commandcodeSection
|
|
58302
|
+
});
|
|
57436
58303
|
const commandcodeFrontmatter = {
|
|
57437
58304
|
...commandcodeSection,
|
|
57438
58305
|
name: rulesyncFrontmatter.name,
|
|
57439
|
-
description: rulesyncFrontmatter.description
|
|
58306
|
+
description: rulesyncFrontmatter.description,
|
|
58307
|
+
...license !== void 0 && { license },
|
|
58308
|
+
...compatibility !== void 0 && { compatibility },
|
|
58309
|
+
...metadata !== void 0 && { metadata },
|
|
58310
|
+
...disableModelInvocation !== void 0 && { "disable-model-invocation": disableModelInvocation },
|
|
58311
|
+
...userInvocable !== void 0 && { "user-invocable": userInvocable }
|
|
57440
58312
|
};
|
|
57441
58313
|
return new CommandcodeSkill({
|
|
57442
58314
|
outputRoot,
|
|
@@ -59384,7 +60256,8 @@ const GrokcliSkillFrontmatterSchema = z.looseObject({
|
|
|
59384
60256
|
name: z.string(),
|
|
59385
60257
|
description: z.string(),
|
|
59386
60258
|
"user-invocable": z.optional(z.boolean()),
|
|
59387
|
-
"disable-model-invocation": z.optional(z.boolean())
|
|
60259
|
+
"disable-model-invocation": z.optional(z.boolean()),
|
|
60260
|
+
paths: z.optional(z.union([z.string(), z.array(z.string())]))
|
|
59388
60261
|
});
|
|
59389
60262
|
/**
|
|
59390
60263
|
* Represents a Grok Build skill directory.
|
|
@@ -59443,7 +60316,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
|
|
|
59443
60316
|
const frontmatter = this.getFrontmatter();
|
|
59444
60317
|
const grokcliSection = {
|
|
59445
60318
|
...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] },
|
|
59446
|
-
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] }
|
|
60319
|
+
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
|
|
60320
|
+
...frontmatter.paths !== void 0 && { paths: frontmatter.paths }
|
|
59447
60321
|
};
|
|
59448
60322
|
const rulesyncFrontmatter = {
|
|
59449
60323
|
name: frontmatter.name,
|
|
@@ -59478,7 +60352,8 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
|
|
|
59478
60352
|
name: rulesyncFrontmatter.name,
|
|
59479
60353
|
description: rulesyncFrontmatter.description,
|
|
59480
60354
|
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
59481
|
-
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
60355
|
+
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation },
|
|
60356
|
+
...grokcliSection?.paths !== void 0 && { paths: grokcliSection.paths }
|
|
59482
60357
|
};
|
|
59483
60358
|
return new GrokcliSkill({
|
|
59484
60359
|
outputRoot,
|
|
@@ -62129,7 +63004,10 @@ var WarpSkill = class WarpSkill extends ToolSkill {
|
|
|
62129
63004
|
}
|
|
62130
63005
|
}
|
|
62131
63006
|
static getSettablePaths({ global: _global = false } = {}) {
|
|
62132
|
-
return {
|
|
63007
|
+
return {
|
|
63008
|
+
relativeDirPath: WARP_SKILLS_DIR_PATH,
|
|
63009
|
+
importOnlySkillRoots: [AGENTSMD_SKILLS_DIR_PATH]
|
|
63010
|
+
};
|
|
62133
63011
|
}
|
|
62134
63012
|
getFrontmatter() {
|
|
62135
63013
|
return WarpSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
@@ -62193,12 +63071,14 @@ var WarpSkill = class WarpSkill extends ToolSkill {
|
|
|
62193
63071
|
return targets.includes("*") || targets.includes("warp");
|
|
62194
63072
|
}
|
|
62195
63073
|
/**
|
|
62196
|
-
* Commands are emitted into
|
|
62197
|
-
* (see `WarpCommand`), so a directory matching a current rulesync
|
|
62198
|
-
* slug is owned by the commands feature: it must not be imported as
|
|
62199
|
-
* skill nor deleted as an orphan skill.
|
|
63074
|
+
* Commands are emitted into the `.warp/skills/` tree as `<slug>/SKILL.md`
|
|
63075
|
+
* (see `WarpCommand`), so a directory there matching a current rulesync
|
|
63076
|
+
* command slug is owned by the commands feature: it must not be imported as
|
|
63077
|
+
* a skill nor deleted as an orphan skill. The import-only `.agents/skills/`
|
|
63078
|
+
* root never receives commands, so a same-named skill in it stays a skill.
|
|
62200
63079
|
*/
|
|
62201
|
-
static async isDirOwned({ dirName, inputRoots }) {
|
|
63080
|
+
static async isDirOwned({ relativeDirPath, dirName, inputRoots }) {
|
|
63081
|
+
if (relativeDirPath !== WARP_SKILLS_DIR_PATH) return true;
|
|
62202
63082
|
return !await rulesyncCommandSlugExists({
|
|
62203
63083
|
inputRoots,
|
|
62204
63084
|
dirName
|
|
@@ -62411,6 +63291,25 @@ const ZedSkillFrontmatterSchema = z.looseObject({
|
|
|
62411
63291
|
description: z.string(),
|
|
62412
63292
|
"disable-model-invocation": z.optional(z.boolean())
|
|
62413
63293
|
});
|
|
63294
|
+
const ZED = "Zed";
|
|
63295
|
+
const ZED_LOAD_FAILURE = "does not load the skill otherwise";
|
|
63296
|
+
/**
|
|
63297
|
+
* Collect the rules Zed enforces on a skill's frontmatter that the loose
|
|
63298
|
+
* schema above does not — `name` rules first, then `description`. Returned as
|
|
63299
|
+
* warnings rather than thrown: the canonical skill is shared with every other
|
|
63300
|
+
* target, so a name Zed rejects must not stop the generate run for the rest.
|
|
63301
|
+
*/
|
|
63302
|
+
function collectZedSkillViolations({ name, description }) {
|
|
63303
|
+
const violations = [];
|
|
63304
|
+
if (name.length === 0) violations.push("`name` must not be empty; Zed does not load a skill without one");
|
|
63305
|
+
else violations.push(...collectSkillNameViolations({
|
|
63306
|
+
name,
|
|
63307
|
+
authority: ZED,
|
|
63308
|
+
consequence: ZED_LOAD_FAILURE
|
|
63309
|
+
}));
|
|
63310
|
+
if (description.length > 1024) violations.push(`\`description\` is ${description.length} characters; ${ZED} loads the skill but warns past ${SKILL_DESCRIPTION_MAX_LENGTH}`);
|
|
63311
|
+
return violations;
|
|
63312
|
+
}
|
|
62414
63313
|
/**
|
|
62415
63314
|
* Represents a Zed agent skill directory.
|
|
62416
63315
|
* Skills are stored under .agents/skills/ (project) or ~/.agents/skills/ (global)
|
|
@@ -62479,9 +63378,10 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62479
63378
|
global: this.global
|
|
62480
63379
|
});
|
|
62481
63380
|
}
|
|
62482
|
-
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
63381
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false, logger }) {
|
|
62483
63382
|
const settablePaths = ZedSkill.getSettablePaths({ global });
|
|
62484
63383
|
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
63384
|
+
const dirName = rulesyncSkill.getDirName();
|
|
62485
63385
|
const zedSection = rulesyncFrontmatter.zed;
|
|
62486
63386
|
const resolvedDisableModelInvocation = resolveDisableModelInvocation({
|
|
62487
63387
|
rootFrontmatter: rulesyncFrontmatter,
|
|
@@ -62493,10 +63393,17 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62493
63393
|
...zedSection,
|
|
62494
63394
|
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation }
|
|
62495
63395
|
};
|
|
63396
|
+
ZedSkill.reportSkillViolations({
|
|
63397
|
+
outputRoot,
|
|
63398
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
63399
|
+
dirName,
|
|
63400
|
+
frontmatter: zedFrontmatter,
|
|
63401
|
+
logger
|
|
63402
|
+
});
|
|
62496
63403
|
return new ZedSkill({
|
|
62497
63404
|
outputRoot,
|
|
62498
63405
|
relativeDirPath: settablePaths.relativeDirPath,
|
|
62499
|
-
dirName
|
|
63406
|
+
dirName,
|
|
62500
63407
|
frontmatter: zedFrontmatter,
|
|
62501
63408
|
body: rulesyncSkill.getBody(),
|
|
62502
63409
|
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
@@ -62504,6 +63411,18 @@ var ZedSkill = class ZedSkill extends ToolSkill {
|
|
|
62504
63411
|
global
|
|
62505
63412
|
});
|
|
62506
63413
|
}
|
|
63414
|
+
/**
|
|
63415
|
+
* Warn about every rule Zed enforces on the skill about to be written. The
|
|
63416
|
+
* reported path includes `outputRoot` so a global-scope skill points at the
|
|
63417
|
+
* file under the home directory rather than a same-named project path.
|
|
63418
|
+
*/
|
|
63419
|
+
static reportSkillViolations({ outputRoot, relativeDirPath, dirName, frontmatter, logger }) {
|
|
63420
|
+
warnSkillViolations({
|
|
63421
|
+
skillPath: join(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME),
|
|
63422
|
+
violations: collectZedSkillViolations(frontmatter),
|
|
63423
|
+
logger
|
|
63424
|
+
});
|
|
63425
|
+
}
|
|
62507
63426
|
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
62508
63427
|
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
62509
63428
|
return targets.includes("*") || targets.includes("zed");
|
|
@@ -64809,6 +65728,35 @@ const CommandcodeSubagentFrontmatterSchema = z.looseObject({
|
|
|
64809
65728
|
showOutput: z.optional(z.boolean())
|
|
64810
65729
|
});
|
|
64811
65730
|
/**
|
|
65731
|
+
* Names Command Code's built-in agents own. The loader runs the frontmatter
|
|
65732
|
+
* `name` (or the file stem when it has none) through `sanitizeAgentName`,
|
|
65733
|
+
* which rewrites every character outside `[a-zA-Z0-9_-]` to `_`, lowercases
|
|
65734
|
+
* it, and skips the file outright when the result is one of these
|
|
65735
|
+
* (`isReservedAgentName`) — with no message. Verified in the `command-code`
|
|
65736
|
+
* 1.54.2 bundle.
|
|
65737
|
+
* @see https://commandcode.ai/docs/agents
|
|
65738
|
+
*/
|
|
65739
|
+
const COMMANDCODE_RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
|
|
65740
|
+
"explore",
|
|
65741
|
+
"plan",
|
|
65742
|
+
"review",
|
|
65743
|
+
"general"
|
|
65744
|
+
]);
|
|
65745
|
+
/** The name Command Code compares against its reserved list. */
|
|
65746
|
+
function toCommandcodeAgentName(name) {
|
|
65747
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
|
|
65748
|
+
}
|
|
65749
|
+
/**
|
|
65750
|
+
* The subagent is still generated under the reserved name — the name is the
|
|
65751
|
+
* user's to change, and a rename here would make it diverge from the other
|
|
65752
|
+
* targets — but Command Code would drop it without a visible word, so say so
|
|
65753
|
+
* now (the same courtesy `VibeSkill` extends to its reserved skill names).
|
|
65754
|
+
*/
|
|
65755
|
+
function warnAboutReservedAgentName({ name, relativeFilePath, logger }) {
|
|
65756
|
+
if (!COMMANDCODE_RESERVED_AGENT_NAMES.has(toCommandcodeAgentName(name))) return;
|
|
65757
|
+
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.`);
|
|
65758
|
+
}
|
|
65759
|
+
/**
|
|
64812
65760
|
* Command Code custom agent: a Markdown file with YAML frontmatter under
|
|
64813
65761
|
* `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
|
|
64814
65762
|
* body is the agent's system prompt.
|
|
@@ -64855,7 +65803,7 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
|
|
|
64855
65803
|
validate: true
|
|
64856
65804
|
});
|
|
64857
65805
|
}
|
|
64858
|
-
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
65806
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false, logger }) {
|
|
64859
65807
|
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
64860
65808
|
const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
|
|
64861
65809
|
const commandcodeSubagentFrontmatter = {
|
|
@@ -64863,6 +65811,11 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
|
|
|
64863
65811
|
description: rulesyncFrontmatter.description,
|
|
64864
65812
|
...commandcodeSection
|
|
64865
65813
|
};
|
|
65814
|
+
warnAboutReservedAgentName({
|
|
65815
|
+
name: commandcodeSubagentFrontmatter.name,
|
|
65816
|
+
relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
|
|
65817
|
+
logger
|
|
65818
|
+
});
|
|
64866
65819
|
const body = rulesyncSubagent.getBody();
|
|
64867
65820
|
const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
|
|
64868
65821
|
const paths = this.getSettablePaths({ global });
|
|
@@ -71235,8 +72188,12 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
71235
72188
|
* | `false` | none | not supported; the rule is dropped |
|
|
71236
72189
|
*
|
|
71237
72190
|
* The default being `true` is the opposite of Cursor, which the adapter was
|
|
71238
|
-
* modeled on
|
|
71239
|
-
*
|
|
72191
|
+
* modeled on. The shipped loader (`@tencent-ai/codebuddy-code` 2.151.0,
|
|
72192
|
+
* `dist/codebuddy.js`) is looser than the table — it resolves
|
|
72193
|
+
* `alwaysApply = fm.alwaysApply === true || (!hasPaths && fm.alwaysApply !== false)`,
|
|
72194
|
+
* so `paths` without `alwaysApply` is already MANUAL — but the explicit
|
|
72195
|
+
* `alwaysApply: false` is written anyway: it is correct under both readings
|
|
72196
|
+
* and the documented one is what a reader of the file goes by.
|
|
71240
72197
|
*
|
|
71241
72198
|
* @see https://www.codebuddy.ai/docs/cli/memory
|
|
71242
72199
|
*/
|
|
@@ -71311,9 +72268,9 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
71311
72268
|
});
|
|
71312
72269
|
}
|
|
71313
72270
|
const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
|
|
71314
|
-
const isAlways = this.frontmatter.alwaysApply !== false;
|
|
72271
|
+
const isAlways = this.frontmatter.alwaysApply === true || sourcePaths.length === 0 && this.frontmatter.alwaysApply !== false;
|
|
71315
72272
|
const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
|
|
71316
|
-
const alwaysApply = this.frontmatter.alwaysApply
|
|
72273
|
+
const alwaysApply = this.frontmatter.alwaysApply;
|
|
71317
72274
|
const rulesyncFrontmatter = {
|
|
71318
72275
|
targets,
|
|
71319
72276
|
root: false,
|
|
@@ -76004,6 +76961,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
76004
76961
|
collisionPolicy: "fold"
|
|
76005
76962
|
}
|
|
76006
76963
|
}],
|
|
76964
|
+
["pool", {
|
|
76965
|
+
class: PoolRule,
|
|
76966
|
+
meta: {
|
|
76967
|
+
extension: "md",
|
|
76968
|
+
supportsGlobal: true,
|
|
76969
|
+
ruleDiscoveryMode: "auto",
|
|
76970
|
+
collisionPolicy: "fold"
|
|
76971
|
+
}
|
|
76972
|
+
}],
|
|
76007
76973
|
["qwencode", {
|
|
76008
76974
|
class: QwencodeRule,
|
|
76009
76975
|
meta: {
|
|
@@ -76127,15 +77093,6 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
76127
77093
|
ruleDiscoveryMode: "auto"
|
|
76128
77094
|
}
|
|
76129
77095
|
}],
|
|
76130
|
-
["pool", {
|
|
76131
|
-
class: PoolRule,
|
|
76132
|
-
meta: {
|
|
76133
|
-
extension: "md",
|
|
76134
|
-
supportsGlobal: true,
|
|
76135
|
-
ruleDiscoveryMode: "auto",
|
|
76136
|
-
collisionPolicy: "fold"
|
|
76137
|
-
}
|
|
76138
|
-
}],
|
|
76139
77096
|
["dsh", {
|
|
76140
77097
|
class: DshRule,
|
|
76141
77098
|
meta: {
|
|
@@ -79321,6 +80278,6 @@ async function importChecksCore(params) {
|
|
|
79321
80278
|
return writtenCount;
|
|
79322
80279
|
}
|
|
79323
80280
|
//#endregion
|
|
79324
|
-
export {
|
|
80281
|
+
export { RulesyncCommand as $, toPosixPath as $t, CLAUDECODE_MEMORIES_DIR_NAME as A, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as An, checkPathTraversal as At, RulesyncSubagent as B, stripControlCharacters as Bn, listDirectoryEntryNames as Bt, COMMANDCODE_DIR as C, RULESYNC_PERMISSIONS_SCHEMA_URL as Cn, withWarnOnceScope as Ct, CODEXCLI_DIR as D, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Dn, assertDirectoryIfExists as Dt, CODEXCLI_BASH_RULES_FILE_NAME as E, RULESYNC_SKILLS_RELATIVE_DIR_PATH as En, applyFileMode as Et, caseFoldIdentity as F, formatError as Fn, getFileSize as Ft, RulesyncRuleFrontmatterSchema as G, readFileContentOrNull as Gt, RulesyncSkill as H, stripHiddenCharacters as Hn, listSubdirectoryNames as Ht, groupSpellingsByCaseFoldedIdentity as I, truncateText as In, getHomeDirectory as It, RulesyncIgnore as J, removeFile as Jt, RulesyncPermissions as K, removeDirectory as Kt, AUGMENTCODE_DIR as L, hasDeceptiveHiddenCharacters as Ln, isFileNotFoundError as Lt, CLAUDECODE_SKILLS_DIR_PATH as M, ALL_FEATURES as Mn, directoryExists as Mt, FACTORYDROID_DIR as N, ALL_FEATURES_WITH_WILDCARD as Nn, ensureDir as Nt, CLAUDECODE_DIR as O, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as On, assertTreeContainsNoSymlinks as Ot, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as P, DEPRECATED_FEATURE_REPLACEMENTS as Pn, fileExists as Pt, parseJsonc as Q, runWithDirectoryRollback as Qt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as R, hasEnclosingMarkOutsideKeycap as Rn, isFileSystemError as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Sn, resetRunWarningState as St, ChecksProcessor as T, RULESYNC_RULES_RELATIVE_DIR_PATH as Tn, ErrorCodes as Tt, RulesyncSkillFrontmatterSchema as U, pathEscapesRoot as Ut, RulesyncSubagentFrontmatterSchema as V, stripControlCharactersKeepingLineFeeds as Vn, listFilePathsRecursively as Vt, RulesyncRule as W, readFileContent as Wt, getRulesyncSourceCandidates as X, removeTempDirectory as Xt, RulesyncHooks as Y, removeFileStrict as Yt, resolveRulesyncSourceWritePath as Z, resolvePath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, JsonLogger as _t, getProcessorRegistryEntry as a, ToolTargetSchema as an, SHARED_USER_MANAGED_CONFIG_PATHS as at, CommandsProcessor as b, RULESYNC_MCP_SCHEMA_URL as bn, warnOnConflictingFlags as bt, RulesProcessor as c, MAX_FILE_SIZE as cn, mergeInputRootConfigs as ct, CODEBUDDY_DIR as d, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as dn, ConfigFileSchema as dt, writeFileBuffer as en, RulesyncCommandFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CONFIG_RELATIVE_FILE_PATH as fn, GITIGNORE_DESTINATION_KEY as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, ConsoleLogger as gt, shortenToWidth as h, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as hn, findControlCharacter as ht, inspectInputRoots as i, PACKAGING_TOOL_TARGETS as in, loadYaml as it, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as j, parseCommaSeparatedList as jn, createTempDirectory as jt, CLAUDECODE_LOCAL_RULE_FILE_NAME as k, RULESYNC_USER_CONFIG_DIR_NAME as kn, assertWritablePathInsideRoot as kt, SubagentsProcessor as l, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as ln, resolveEffectiveInputRoots as lt, displayWidthOf as m, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as mn, assertTargetsFeaturesExclusive as mt, formatSourceLoadFailure as n, ALL_TOOL_TARGETS as nn, RulesyncCheckFrontmatterSchema as nt, convertFromTool as o, CURATED_RULES_FEATURE_SUBDIR as on, SKILL_FILE_NAME as ot, ELLIPSIS_WIDTH as p, RULESYNC_CONFIG_SCHEMA_URL as pn, SourceEntrySchema as pt, RulesyncMcp as q, removeDirectoryStrict as qt, generate as r, ALL_TOOL_TARGETS_WITH_WILDCARD as rn, stringifyFrontmatter as rt, isPackagingToolTarget as s, FEATURE_SOURCE_TREE_ENTRIES as sn, ConfigResolver as st, importFromTool as t, writeFileContent as tn, RulesyncCheck as tt, SkillsProcessor as u, RULESYNC_CHECKS_RELATIVE_DIR_PATH as un, CONFLICTING_TARGET_PAIRS as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, WarningCollectingLogger as vt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as w, RULESYNC_RELATIVE_DIR_PATH as wn, CLIError as wt, QWENCODE_DIR as x, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as xn, withFallbackLoggerTarget as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_RELATIVE_FILE_PATH as yn, fallbackLogger as yt, getLocalSkillDirNames as z, quoteForLog as zn, isSymlink as zt };
|
|
79325
80282
|
|
|
79326
|
-
//# sourceMappingURL=import-
|
|
80283
|
+
//# sourceMappingURL=import-D_iukHSy.js.map
|