rulesync 16.12.0 → 16.14.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 +2 -2
- package/dist/cli/index.cjs +9 -4
- package/dist/cli/index.js +9 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-zGCKgpdt.js → import-Ds0QeVp6.js} +1125 -153
- package/dist/import-Ds0QeVp6.js.map +1 -0
- package/dist/{import-BwdPcfzS.cjs → import-DsiSzsJH.cjs} +1139 -167
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +13 -13
- package/dist/import-zGCKgpdt.js.map +0 -1
|
@@ -412,13 +412,15 @@ const permissionsProcessorToolTargetTuple = [
|
|
|
412
412
|
"kiro-cli",
|
|
413
413
|
"kiro-ide",
|
|
414
414
|
"opencode",
|
|
415
|
+
"pi",
|
|
415
416
|
"qwencode",
|
|
416
417
|
"reasonix",
|
|
417
418
|
"rovodev",
|
|
418
419
|
"takt",
|
|
419
420
|
"vibe",
|
|
420
421
|
"warp",
|
|
421
|
-
"zed"
|
|
422
|
+
"zed",
|
|
423
|
+
"zoocode"
|
|
422
424
|
];
|
|
423
425
|
const checksProcessorToolTargetTuple = [
|
|
424
426
|
"amp",
|
|
@@ -1903,7 +1905,7 @@ function isPlainObject$1(value) {
|
|
|
1903
1905
|
/**
|
|
1904
1906
|
* Type guard to check if a value is an array of strings.
|
|
1905
1907
|
*/
|
|
1906
|
-
function isStringArray$
|
|
1908
|
+
function isStringArray$2(value) {
|
|
1907
1909
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1908
1910
|
}
|
|
1909
1911
|
//#endregion
|
|
@@ -2271,6 +2273,7 @@ const HOOK_EVENTS = [
|
|
|
2271
2273
|
"todoCreated",
|
|
2272
2274
|
"todoCompleted",
|
|
2273
2275
|
"stopFailure",
|
|
2276
|
+
"stopCancelled",
|
|
2274
2277
|
"instructionsLoaded",
|
|
2275
2278
|
"userPromptExpansion",
|
|
2276
2279
|
"postToolBatch",
|
|
@@ -2846,6 +2849,7 @@ const GROKCLI_HOOK_EVENTS = [
|
|
|
2846
2849
|
"permissionDenied",
|
|
2847
2850
|
"stop",
|
|
2848
2851
|
"stopFailure",
|
|
2852
|
+
"stopCancelled",
|
|
2849
2853
|
"notification",
|
|
2850
2854
|
"subagentStart",
|
|
2851
2855
|
"subagentStop",
|
|
@@ -2921,16 +2925,10 @@ const KIMI_CODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CAN
|
|
|
2921
2925
|
/**
|
|
2922
2926
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
2923
2927
|
*
|
|
2924
|
-
* Hermes validates hook events against a fixed `VALID_HOOKS` set
|
|
2925
|
-
*
|
|
2926
|
-
*
|
|
2927
|
-
*
|
|
2928
|
-
* `on_session_reset`, `subagent_start`, `subagent_stop`, `pre_gateway_dispatch`,
|
|
2929
|
-
* `pre_approval_request`, `post_approval_response`, `transform_tool_result`,
|
|
2930
|
-
* `transform_terminal_output`, `transform_llm_output`, and the three
|
|
2931
|
-
* `kanban_task_*` events. Only the events with a clean 1:1 canonical equivalent
|
|
2932
|
-
* are mapped here. All other native events round-trip through
|
|
2933
|
-
* `hermesagent.hooks`.
|
|
2928
|
+
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 37 entries as
|
|
2929
|
+
* of v0.20.2 (`v2026.8.16`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
|
|
2930
|
+
* full list. Only the events with a clean 1:1 canonical equivalent are mapped
|
|
2931
|
+
* here. All other native events round-trip through `hermesagent.hooks`.
|
|
2934
2932
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
2935
2933
|
*/
|
|
2936
2934
|
const HERMESAGENT_HOOK_EVENTS = [
|
|
@@ -2943,6 +2941,21 @@ const HERMESAGENT_HOOK_EVENTS = [
|
|
|
2943
2941
|
"subagentStart",
|
|
2944
2942
|
"subagentStop"
|
|
2945
2943
|
];
|
|
2944
|
+
/**
|
|
2945
|
+
* Hermes Agent's native `VALID_HOOKS` set, in upstream source order.
|
|
2946
|
+
*
|
|
2947
|
+
* The authority is `hermes_cli/plugins.py`'s `VALID_HOOKS`, not the docs: at
|
|
2948
|
+
* v0.20.2 `website/docs/user-guide/features/plugins.md` says "26 lifecycle
|
|
2949
|
+
* events" and then lists 30, omitting the five `on_kanban_*` worker/task/dispatch
|
|
2950
|
+
* observers, `gateway_platform_event` and `transform_api_error_classification`.
|
|
2951
|
+
* Shell hooks are gated on the same set.
|
|
2952
|
+
*
|
|
2953
|
+
* Verified against the tag `v2026.8.16` (v0.20.2): `VALID_HOOKS` holds 37
|
|
2954
|
+
* entries, grown from 23 at `v2026.8.3` (v0.20.0). This list holds 36 of them —
|
|
2955
|
+
* see {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
|
|
2956
|
+
*
|
|
2957
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py
|
|
2958
|
+
*/
|
|
2946
2959
|
const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
2947
2960
|
"pre_tool_call",
|
|
2948
2961
|
"post_tool_call",
|
|
@@ -2951,6 +2964,10 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
2951
2964
|
"transform_llm_output",
|
|
2952
2965
|
"pre_llm_call",
|
|
2953
2966
|
"post_llm_call",
|
|
2967
|
+
"on_stream_start",
|
|
2968
|
+
"on_stream_delta",
|
|
2969
|
+
"on_stream_end",
|
|
2970
|
+
"on_interim_message",
|
|
2954
2971
|
"pre_verify",
|
|
2955
2972
|
"pre_api_request",
|
|
2956
2973
|
"post_api_request",
|
|
@@ -2959,16 +2976,39 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
2959
2976
|
"on_session_end",
|
|
2960
2977
|
"on_session_finalize",
|
|
2961
2978
|
"on_session_reset",
|
|
2979
|
+
"on_skill_lifecycle",
|
|
2962
2980
|
"subagent_start",
|
|
2963
2981
|
"subagent_stop",
|
|
2964
2982
|
"pre_gateway_dispatch",
|
|
2965
2983
|
"pre_approval_request",
|
|
2966
2984
|
"post_approval_response",
|
|
2985
|
+
"pre_transcription",
|
|
2967
2986
|
"kanban_task_claimed",
|
|
2968
2987
|
"kanban_task_completed",
|
|
2969
|
-
"kanban_task_blocked"
|
|
2988
|
+
"kanban_task_blocked",
|
|
2989
|
+
"on_kanban_worker_spawned",
|
|
2990
|
+
"on_kanban_worker_exited",
|
|
2991
|
+
"on_kanban_worker_stale_claim",
|
|
2992
|
+
"on_kanban_task_updated",
|
|
2993
|
+
"on_kanban_dispatch_tick",
|
|
2994
|
+
"gateway_platform_event",
|
|
2995
|
+
"pre_command"
|
|
2970
2996
|
];
|
|
2971
2997
|
/**
|
|
2998
|
+
* `VALID_HOOKS` entries a SHELL hook may not register for. `VALID_HOOKS` doubles
|
|
2999
|
+
* as the shell-hook allow-list, but `SHELL_UNSUPPORTED_HOOKS` is subtracted from
|
|
3000
|
+
* it first: shell hooks cannot return these events' directives, so
|
|
3001
|
+
* `_parse_hooks_block` refuses the registration with a warning rather than
|
|
3002
|
+
* accepting it and dropping the return value.
|
|
3003
|
+
*
|
|
3004
|
+
* rulesync only ever writes shell hooks, so these are excluded from
|
|
3005
|
+
* {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
|
|
3006
|
+
*
|
|
3007
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
|
|
3008
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/agent/shell_hooks.py — `_parse_hooks_block`
|
|
3009
|
+
*/
|
|
3010
|
+
const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
|
|
3011
|
+
/**
|
|
2972
3012
|
* Map canonical camelCase event names to Hermes Agent's native `VALID_HOOKS`
|
|
2973
3013
|
* snake_case keys under the `hooks:` block of `~/.hermes/config.yaml`.
|
|
2974
3014
|
*/
|
|
@@ -3585,6 +3625,7 @@ const CANONICAL_TO_GROKCLI_EVENT_NAMES = {
|
|
|
3585
3625
|
permissionDenied: "PermissionDenied",
|
|
3586
3626
|
stop: "Stop",
|
|
3587
3627
|
stopFailure: "StopFailure",
|
|
3628
|
+
stopCancelled: "StopCancelled",
|
|
3588
3629
|
notification: "Notification",
|
|
3589
3630
|
subagentStart: "SubagentStart",
|
|
3590
3631
|
subagentStop: "SubagentStop",
|
|
@@ -3859,6 +3900,25 @@ const McpServerSchema = z.looseObject({
|
|
|
3859
3900
|
kiroAutoApprove: z.optional(z.array(z.string())),
|
|
3860
3901
|
kiroAutoBlock: z.optional(z.array(z.string())),
|
|
3861
3902
|
headers: z.optional(z.record(z.string(), z.string())),
|
|
3903
|
+
/**
|
|
3904
|
+
* The canonical per-server tool allowlist.
|
|
3905
|
+
*
|
|
3906
|
+
* **Collision rule for adapters.** Several tools spell an allowlist natively
|
|
3907
|
+
* under a name that also exists here, so a single server entry can carry both
|
|
3908
|
+
* a canonical field and its native counterpart. What the *native* key means
|
|
3909
|
+
* decides the resolution — not which one was written first:
|
|
3910
|
+
*
|
|
3911
|
+
* - Native key is a redundant spelling of the same concept, and both lists are
|
|
3912
|
+
* additive → **merge** them, so a config that already spells the field
|
|
3913
|
+
* natively keeps working (`kiroAutoApprove`/`kiroAutoBlock` in
|
|
3914
|
+
* `kiro-mcp.ts`).
|
|
3915
|
+
* - Native key is the same concept but rulesync owns the generated value, so
|
|
3916
|
+
* keeping both would be ambiguous → the **canonical value wins** and the
|
|
3917
|
+
* native one is dropped with a warning (`tools` in `copilotcli-mcp.ts`).
|
|
3918
|
+
* - Native key of the same name means something else entirely → **refuse the
|
|
3919
|
+
* canonical value** rather than write a shape the tool misreads (`tools` in
|
|
3920
|
+
* `codexcli-mcp.ts`, where Codex reads it as a per-tool approval table).
|
|
3921
|
+
*/
|
|
3862
3922
|
enabledTools: z.optional(z.array(z.string())),
|
|
3863
3923
|
disabledTools: z.optional(z.array(z.string()))
|
|
3864
3924
|
});
|
|
@@ -4701,8 +4761,65 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4701
4761
|
dangerouslyAllowAll: z.optional(z.boolean())
|
|
4702
4762
|
});
|
|
4703
4763
|
/**
|
|
4764
|
+
* Tool-scoped override block for Pi Coding Agent. Pi has no allow/ask/deny rule
|
|
4765
|
+
* surface: the only repository-syncable tool gate is `defaultTools`, the list of
|
|
4766
|
+
* built-in tools enabled at startup (added in v0.84.2). It is an enable-list
|
|
4767
|
+
* rather than a canonical allow/deny rule set, so it is authored here rather
|
|
4768
|
+
* than mapped onto `permission`, mirroring `KimiCodePermissionsOverrideSchema`'s
|
|
4769
|
+
* `tools.enabled` and `VibePermissionsOverrideSchema`'s `enabled_tools`.
|
|
4770
|
+
*
|
|
4771
|
+
* An empty array is meaningful upstream — it starts Pi with no built-in tools
|
|
4772
|
+
* while keeping extension and SDK custom tools — so it is emitted as written.
|
|
4773
|
+
* CLI flags (`--tools`, `--no-tools`, `--no-builtin-tools`, `--exclude-tools`)
|
|
4774
|
+
* outrank the setting, and a project array **replaces** the global one rather
|
|
4775
|
+
* than merging with it.
|
|
4776
|
+
*
|
|
4777
|
+
* @example
|
|
4778
|
+
* { "defaultTools": ["bash", "edit", "write"] }
|
|
4779
|
+
* @see https://pi.dev/docs/latest/settings
|
|
4780
|
+
*/
|
|
4781
|
+
const PiPermissionsOverrideSchema = z.looseObject({
|
|
4782
|
+
permission: z.optional(ToolScopedPermissionSchema),
|
|
4783
|
+
defaultTools: z.optional(z.array(z.string()))
|
|
4784
|
+
});
|
|
4785
|
+
/**
|
|
4786
|
+
* The Antigravity CLI's four documented `toolPermission` autonomy presets.
|
|
4787
|
+
* Exported so the import side of `antigravity-cli-permissions.ts` filters
|
|
4788
|
+
* against exactly the values the enum below accepts — an unrecognized value
|
|
4789
|
+
* carried in verbatim would fail validation of the *whole*
|
|
4790
|
+
* `.rulesync/permissions.jsonc`, not just that key.
|
|
4791
|
+
* @see https://antigravity.google/docs/cli/reference
|
|
4792
|
+
*/
|
|
4793
|
+
const ANTIGRAVITY_CLI_TOOL_PERMISSIONS = [
|
|
4794
|
+
"request-review",
|
|
4795
|
+
"proceed-in-sandbox",
|
|
4796
|
+
"always-proceed",
|
|
4797
|
+
"strict"
|
|
4798
|
+
];
|
|
4799
|
+
/**
|
|
4800
|
+
* The Antigravity CLI's three documented `artifactReviewPolicy` values. Shared
|
|
4801
|
+
* with the import filter for the same reason as
|
|
4802
|
+
* {@link ANTIGRAVITY_CLI_TOOL_PERMISSIONS}.
|
|
4803
|
+
* @see https://antigravity.google/docs/cli/settings
|
|
4804
|
+
*/
|
|
4805
|
+
const ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES = [
|
|
4806
|
+
"asks-for-review",
|
|
4807
|
+
"agent-decides",
|
|
4808
|
+
"always-proceed"
|
|
4809
|
+
];
|
|
4810
|
+
/**
|
|
4811
|
+
* The baseline execution modes the CLI persists as `agentMode`. Shared with the
|
|
4812
|
+
* import filter for the same reason as {@link ANTIGRAVITY_CLI_TOOL_PERMISSIONS}.
|
|
4813
|
+
* @see https://antigravity.google/docs/cli/modes
|
|
4814
|
+
*/
|
|
4815
|
+
const ANTIGRAVITY_CLI_AGENT_MODES = [
|
|
4816
|
+
"default",
|
|
4817
|
+
"accept-edits",
|
|
4818
|
+
"plan"
|
|
4819
|
+
];
|
|
4820
|
+
/**
|
|
4704
4821
|
* Tool-scoped override block for the Google Antigravity CLI. Antigravity's CLI
|
|
4705
|
-
* `settings.json` carries
|
|
4822
|
+
* `settings.json` carries five global autonomy/sandbox knobs outside the
|
|
4706
4823
|
* `permissions.allow/ask/deny` arrays rulesync manages: `toolPermission` (the
|
|
4707
4824
|
* global autonomy preset — `request-review` (default) / `proceed-in-sandbox` /
|
|
4708
4825
|
* `always-proceed` / `strict`), `enableTerminalSandbox` (a boolean confining
|
|
@@ -4710,7 +4827,8 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4710
4827
|
* agent's artifact changes are gated on a review prompt — `asks-for-review`
|
|
4711
4828
|
* (default) / `agent-decides` / `always-proceed`) and `allowNonWorkspaceAccess`
|
|
4712
4829
|
* (a boolean, off by default, letting the agent read or write files outside the
|
|
4713
|
-
* active workspace roots)
|
|
4830
|
+
* active workspace roots), and `agentMode` (the baseline execution mode a session
|
|
4831
|
+
* starts in — `default` / `accept-edits` / `plan`). Antigravity applies the allow/deny
|
|
4714
4832
|
* lists as per-rule exceptions to the preset at runtime, so rulesync only
|
|
4715
4833
|
* authors these keys verbatim — no precedence modeling is needed on our side.
|
|
4716
4834
|
* Fields placed here are merged onto the top level of
|
|
@@ -4718,27 +4836,27 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4718
4836
|
* the CLI. The Antigravity IDE exposes the same concepts through a GUI (no
|
|
4719
4837
|
* documented JSON schema), so this override does NOT apply to `antigravity-ide`.
|
|
4720
4838
|
* Verified against https://antigravity.google/docs/cli/reference,
|
|
4721
|
-
* https://antigravity.google/docs/cli/sandbox
|
|
4722
|
-
* https://antigravity.google/docs/cli/settings
|
|
4839
|
+
* https://antigravity.google/docs/cli/sandbox,
|
|
4840
|
+
* https://antigravity.google/docs/cli/settings and
|
|
4841
|
+
* https://antigravity.google/docs/cli/modes.
|
|
4723
4842
|
*
|
|
4724
4843
|
* @example
|
|
4725
4844
|
* { "toolPermission": "strict", "enableTerminalSandbox": true }
|
|
4726
4845
|
*/
|
|
4727
4846
|
const AntigravityCliPermissionsOverrideSchema = z.looseObject({
|
|
4728
4847
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
4729
|
-
toolPermission: z.optional(z.enum(
|
|
4730
|
-
"request-review",
|
|
4731
|
-
"proceed-in-sandbox",
|
|
4732
|
-
"always-proceed",
|
|
4733
|
-
"strict"
|
|
4734
|
-
])),
|
|
4848
|
+
toolPermission: z.optional(z.enum(ANTIGRAVITY_CLI_TOOL_PERMISSIONS)),
|
|
4735
4849
|
enableTerminalSandbox: z.optional(z.boolean()),
|
|
4736
|
-
artifactReviewPolicy: z.optional(z.enum(
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4850
|
+
artifactReviewPolicy: z.optional(z.enum(ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES)),
|
|
4851
|
+
allowNonWorkspaceAccess: z.optional(z.boolean()),
|
|
4852
|
+
/**
|
|
4853
|
+
* The baseline execution mode the CLI starts a session in (Agent Mode), the
|
|
4854
|
+
* persisted form of the `/settings` panel's Agent Mode row. Only
|
|
4855
|
+
* `~/.gemini/antigravity-cli/settings.json` is read for it, which is why this
|
|
4856
|
+
* override stays global-only.
|
|
4857
|
+
* @see https://antigravity.google/docs/cli/modes
|
|
4858
|
+
*/
|
|
4859
|
+
agentMode: z.optional(z.enum(ANTIGRAVITY_CLI_AGENT_MODES))
|
|
4742
4860
|
});
|
|
4743
4861
|
/**
|
|
4744
4862
|
* Tool-scoped override block for AugmentCode. AugmentCode's `toolPermissions[]`
|
|
@@ -5048,6 +5166,7 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
5048
5166
|
takt: z.optional(TaktPermissionsOverrideSchema),
|
|
5049
5167
|
amp: z.optional(AmpPermissionsOverrideSchema),
|
|
5050
5168
|
"antigravity-cli": z.optional(AntigravityCliPermissionsOverrideSchema),
|
|
5169
|
+
pi: z.optional(PiPermissionsOverrideSchema),
|
|
5051
5170
|
augmentcode: z.optional(AugmentcodePermissionsOverrideSchema),
|
|
5052
5171
|
kiro: z.optional(KiroPermissionsOverrideSchema),
|
|
5053
5172
|
codexcli: z.optional(CodexcliPermissionsOverrideSchema),
|
|
@@ -5227,6 +5346,7 @@ const RulesyncRuleFrontmatterSchema = z.object({
|
|
|
5227
5346
|
systemPrompt: z.optional(z.enum(["append"])),
|
|
5228
5347
|
contextFile: z.optional(z.enum(["override"]))
|
|
5229
5348
|
})),
|
|
5349
|
+
roo: z.optional(z.looseObject({ mode: z.optional(z.string()) })),
|
|
5230
5350
|
takt: z.optional(z.looseObject({
|
|
5231
5351
|
name: z.optional(z.string()),
|
|
5232
5352
|
extends: z.optional(z.string()),
|
|
@@ -5529,6 +5649,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
5529
5649
|
cursor: z.optional(z.looseObject({
|
|
5530
5650
|
paths: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
5531
5651
|
"disable-model-invocation": z.optional(z.boolean()),
|
|
5652
|
+
"user-invocable": z.optional(z.boolean()),
|
|
5532
5653
|
metadata: z.optional(z.looseObject({}))
|
|
5533
5654
|
})),
|
|
5534
5655
|
factorydroid: z.optional(z.looseObject({
|
|
@@ -5753,6 +5874,11 @@ async function getLocalSkillDirNames(outputRoot) {
|
|
|
5753
5874
|
* default with a per-target value. A defined section value (including `false`)
|
|
5754
5875
|
* always wins over the root default.
|
|
5755
5876
|
*
|
|
5877
|
+
* `devin` also consumes the root-level value — it maps `true` onto a user-only
|
|
5878
|
+
* `triggers` list (see `devin-skill.ts`) — but has no section key of the same
|
|
5879
|
+
* name, so it does not go through this helper. A `devin.triggers` section value
|
|
5880
|
+
* still overrides it.
|
|
5881
|
+
*
|
|
5756
5882
|
* @returns The resolved boolean, or `undefined` when neither value is set.
|
|
5757
5883
|
*/
|
|
5758
5884
|
function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
@@ -5763,10 +5889,15 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
|
5763
5889
|
*
|
|
5764
5890
|
* The rulesync skill frontmatter exposes a root-level `user-invocable` default
|
|
5765
5891
|
* that applies to every tool supporting the flag (claudecode, copilot,
|
|
5766
|
-
* copilotcli, qwencode, vibe, grokcli, factorydroid). Each tool's own section may override that default with a
|
|
5892
|
+
* copilotcli, cursor, qwencode, vibe, grokcli, factorydroid). Each tool's own section may override that default with a
|
|
5767
5893
|
* per-target value. A defined section value (including `false`) always wins
|
|
5768
5894
|
* over the root default.
|
|
5769
5895
|
*
|
|
5896
|
+
* `devin` also consumes the root-level value — it maps `false` onto a
|
|
5897
|
+
* model-only `triggers` list (see `devin-skill.ts`) — but has no section key of
|
|
5898
|
+
* the same name, so it does not go through this helper. A `devin.triggers`
|
|
5899
|
+
* section value still overrides it.
|
|
5900
|
+
*
|
|
5770
5901
|
* @returns The resolved boolean, or `undefined` when neither value is set.
|
|
5771
5902
|
*/
|
|
5772
5903
|
function resolveUserInvocable({ rootFrontmatter, section }) {
|
|
@@ -7616,6 +7747,22 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
7616
7747
|
}
|
|
7617
7748
|
}
|
|
7618
7749
|
},
|
|
7750
|
+
".pi/settings.json": {
|
|
7751
|
+
format: "json",
|
|
7752
|
+
invalidRootPolicy: "error",
|
|
7753
|
+
features: { permissions: {
|
|
7754
|
+
kind: "replace-owned-keys",
|
|
7755
|
+
ownedKeys: ["defaultTools"]
|
|
7756
|
+
} }
|
|
7757
|
+
},
|
|
7758
|
+
".pi/agent/settings.json": {
|
|
7759
|
+
format: "json",
|
|
7760
|
+
invalidRootPolicy: "error",
|
|
7761
|
+
features: { permissions: {
|
|
7762
|
+
kind: "replace-owned-keys",
|
|
7763
|
+
ownedKeys: ["defaultTools"]
|
|
7764
|
+
} }
|
|
7765
|
+
},
|
|
7619
7766
|
".github/copilot/settings.json": {
|
|
7620
7767
|
format: "json",
|
|
7621
7768
|
invalidRootPolicy: "error",
|
|
@@ -7641,7 +7788,9 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
7641
7788
|
ownedKeys: [
|
|
7642
7789
|
"chat.tools.terminal.autoApprove",
|
|
7643
7790
|
"chat.tools.edits.autoApprove",
|
|
7644
|
-
"chat.tools.urls.autoApprove"
|
|
7791
|
+
"chat.tools.urls.autoApprove",
|
|
7792
|
+
"zoo-code.allowedCommands",
|
|
7793
|
+
"zoo-code.deniedCommands"
|
|
7645
7794
|
]
|
|
7646
7795
|
} }
|
|
7647
7796
|
},
|
|
@@ -12202,6 +12351,8 @@ const PI_RULE_FILE_NAME = "AGENTS.md";
|
|
|
12202
12351
|
const PI_RULE_OVERRIDE_FILE_NAME = "AGENTS.override.md";
|
|
12203
12352
|
const PI_APPEND_SYSTEM_FILE_NAME = "APPEND_SYSTEM.md";
|
|
12204
12353
|
const PI_HOOKS_FILE_NAME = "rulesync-hooks.ts";
|
|
12354
|
+
const PI_SETTINGS_FILE_NAME = "settings.json";
|
|
12355
|
+
const PI_AGENT_DIR_PATH = PI_AGENT_DIR;
|
|
12205
12356
|
//#endregion
|
|
12206
12357
|
//#region src/features/commands/pi-command.ts
|
|
12207
12358
|
/**
|
|
@@ -12603,6 +12754,21 @@ const ROO_DIR = ".roo";
|
|
|
12603
12754
|
const ROO_COMMANDS_DIR_PATH = join(ROO_DIR, "commands");
|
|
12604
12755
|
const ROO_SKILLS_DIR_PATH = join(ROO_DIR, "skills");
|
|
12605
12756
|
const ROO_MCP_FILE_NAME = "mcp.json";
|
|
12757
|
+
/**
|
|
12758
|
+
* Mode slugs Roo/Zoo Code themselves accept for a `rules-{mode}` directory:
|
|
12759
|
+
* the loader builds the directory name by interpolating the active mode slug,
|
|
12760
|
+
* and custom-mode slugs are restricted to this alphabet. Validating against it
|
|
12761
|
+
* also keeps an authored value from escaping `.roo/` through path separators or
|
|
12762
|
+
* `..` segments.
|
|
12763
|
+
*/
|
|
12764
|
+
const ROO_MODE_SLUG_PATTERN = /^[a-zA-Z0-9-]+$/;
|
|
12765
|
+
/**
|
|
12766
|
+
* `.roo/rules-{mode}/` — the mode-specific rule directory Roo/Zoo Code load
|
|
12767
|
+
* INSTEAD of `.roo/rules/` while that mode is active. The relative path is the
|
|
12768
|
+
* same in global scope, where it resolves under `~/.roo/`.
|
|
12769
|
+
* @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/core/prompts/sections/custom-instructions.ts
|
|
12770
|
+
*/
|
|
12771
|
+
const rooModeRulesDirName = (mode) => `rules-${mode}`;
|
|
12606
12772
|
const ROO_IGNORE_FILE_NAME = ".rooignore";
|
|
12607
12773
|
/**
|
|
12608
12774
|
* Roo Code reads project-level custom modes from a single aggregated
|
|
@@ -14126,7 +14292,7 @@ const isBooleanValue = ({ value }) => typeof value === "boolean";
|
|
|
14126
14292
|
const isFiniteNumber = ({ value }) => Number.isFinite(value);
|
|
14127
14293
|
const isNonEmptyString = (value) => typeof value === "string" && value !== "";
|
|
14128
14294
|
const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
14129
|
-
const isEmittableArray = ({ value }) => isStringArray(value);
|
|
14295
|
+
const isEmittableArray = ({ value }) => isStringArray$1(value);
|
|
14130
14296
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
14131
14297
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
14132
14298
|
/**
|
|
@@ -14435,7 +14601,7 @@ function isStringRecord(value) {
|
|
|
14435
14601
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
14436
14602
|
return Object.values(value).every((v) => typeof v === "string");
|
|
14437
14603
|
}
|
|
14438
|
-
function isStringArray(value) {
|
|
14604
|
+
function isStringArray$1(value) {
|
|
14439
14605
|
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
14440
14606
|
}
|
|
14441
14607
|
/** Compare object values without letting key order decide the answer. */
|
|
@@ -14467,7 +14633,7 @@ function isSafeStringRecord(value) {
|
|
|
14467
14633
|
* skipped wholesale when that read fails.
|
|
14468
14634
|
*/
|
|
14469
14635
|
function isSafeStringArray(value) {
|
|
14470
|
-
return isStringArray(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
14636
|
+
return isStringArray$1(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
14471
14637
|
}
|
|
14472
14638
|
/**
|
|
14473
14639
|
* A raw string kept only if the canonical field it would land in accepts it,
|
|
@@ -14509,7 +14675,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
14509
14675
|
return {
|
|
14510
14676
|
...url !== void 0 && { url },
|
|
14511
14677
|
...isStringRecord(headers) && { headers },
|
|
14512
|
-
...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
14678
|
+
...isStringArray$1(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
14513
14679
|
};
|
|
14514
14680
|
}
|
|
14515
14681
|
if (hookType === "mcp_tool") {
|
|
@@ -15492,7 +15658,7 @@ const MANAGED_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_CLINE_EVENT_NAMES
|
|
|
15492
15658
|
function parseManifest(fileContent) {
|
|
15493
15659
|
try {
|
|
15494
15660
|
const parsed = JSON.parse(fileContent);
|
|
15495
|
-
if (!isRecord$1(parsed) || !isStringArray$
|
|
15661
|
+
if (!isRecord$1(parsed) || !isStringArray$2(parsed.events)) return null;
|
|
15496
15662
|
return {
|
|
15497
15663
|
generatedBy: "rulesync",
|
|
15498
15664
|
events: parsed.events.filter((event) => MANAGED_EVENT_NAMES.has(event))
|
|
@@ -17127,6 +17293,7 @@ const HERMESAGENT_MATCHER_EVENTS = /* @__PURE__ */ new Set(["pre_tool_call", "po
|
|
|
17127
17293
|
const HERMESAGENT_FAIL_CLOSED_EVENT = "pre_tool_call";
|
|
17128
17294
|
const HERMESAGENT_CANONICAL_EVENTS = new Set(HERMESAGENT_HOOK_EVENTS);
|
|
17129
17295
|
const HERMESAGENT_NATIVE_EVENTS = new Set(HERMESAGENT_NATIVE_HOOK_EVENTS);
|
|
17296
|
+
const HERMESAGENT_SHELL_UNSUPPORTED_EVENTS = new Set(HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS);
|
|
17130
17297
|
/**
|
|
17131
17298
|
* Whether an entry of the `hooks:` mapping is a hook-event list rather than one
|
|
17132
17299
|
* of its non-event siblings.
|
|
@@ -17215,7 +17382,8 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
17215
17382
|
}
|
|
17216
17383
|
for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
|
|
17217
17384
|
if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
|
|
17218
|
-
if (
|
|
17385
|
+
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.`);
|
|
17386
|
+
else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.20.2; preserving it for forward compatibility.`);
|
|
17219
17387
|
setHermesHookEntries({
|
|
17220
17388
|
result,
|
|
17221
17389
|
event: nativeEvent,
|
|
@@ -22339,7 +22507,7 @@ function isCodexStdioServer(config) {
|
|
|
22339
22507
|
* @see https://learn.chatgpt.com/docs/extend/mcp
|
|
22340
22508
|
*/
|
|
22341
22509
|
function isValidRenamedArray(key, value) {
|
|
22342
|
-
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$
|
|
22510
|
+
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$2(value);
|
|
22343
22511
|
}
|
|
22344
22512
|
/**
|
|
22345
22513
|
* Translate a server's `oauth` table from the canonical rulesync shape (Claude
|
|
@@ -22859,6 +23027,49 @@ const resolveCopilotcliServerType = (server) => {
|
|
|
22859
23027
|
return isRemoteMcpServer(server) ? "http" : "stdio";
|
|
22860
23028
|
};
|
|
22861
23029
|
/**
|
|
23030
|
+
* Copilot CLI's per-server tool allowlist. `tools` selects which of the
|
|
23031
|
+
* server's tools are exposed — `["*"]` (the default) for all, or a list of
|
|
23032
|
+
* names — so the canonical `enabledTools` is written under that name rather
|
|
23033
|
+
* than passed through as a key the CLI ignores.
|
|
23034
|
+
*
|
|
23035
|
+
* When a server carries both, the canonical `enabledTools` wins and the native
|
|
23036
|
+
* `tools` is dropped with a warning. rulesync owns the generated allowlist, and
|
|
23037
|
+
* the `tools` value most likely present is the upstream default `["*"]` — left
|
|
23038
|
+
* to win, it would silently discard a real allowlist in favour of "expose every
|
|
23039
|
+
* tool", which is the failure this mapping exists to prevent. That is the
|
|
23040
|
+
* "canonical wins" branch of the collision rule documented on `enabledTools` in
|
|
23041
|
+
* `src/types/mcp.ts`; `kiro-mcp.ts` merges instead (its native names are
|
|
23042
|
+
* redundant spellings of additive lists) and `codexcli-mcp.ts` refuses instead
|
|
23043
|
+
* (Codex's same-named key means a different thing).
|
|
23044
|
+
*
|
|
23045
|
+
* `disabledTools` has no counterpart upstream (expressing it would need the
|
|
23046
|
+
* server's full tool list), so the target keeps `supportsDisabledTools: false`
|
|
23047
|
+
* and the processor strips it before it reaches here.
|
|
23048
|
+
*
|
|
23049
|
+
* @see https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers
|
|
23050
|
+
*/
|
|
23051
|
+
function renameEnabledToolsToTools({ server, serverName, logger }) {
|
|
23052
|
+
const { enabledTools, ...rest } = server;
|
|
23053
|
+
if (enabledTools === void 0) return rest;
|
|
23054
|
+
if (rest.tools !== void 0) logger?.warn(`[CopilotcliMcp] MCP server "${serverName}" declares both 'tools' and 'enabledTools'; keeping 'enabledTools' and dropping 'tools'.`);
|
|
23055
|
+
return {
|
|
23056
|
+
...rest,
|
|
23057
|
+
tools: enabledTools
|
|
23058
|
+
};
|
|
23059
|
+
}
|
|
23060
|
+
/**
|
|
23061
|
+
* Copilot CLI's `tools` accepts more than the canonical `enabledTools` shape:
|
|
23062
|
+
* `--tools` documents `*`, a comma-separated list, or `""`, so a hand-written or
|
|
23063
|
+
* CLI-written entry can be a bare string. Canonical `enabledTools` is
|
|
23064
|
+
* `string[]`, and `RulesyncMcp` throws on a schema failure — carrying a string
|
|
23065
|
+
* through would reject the WHOLE imported MCP file rather than one key. Anything
|
|
23066
|
+
* that is not a string array is therefore left under `tools` untouched, matching
|
|
23067
|
+
* `codexcli-mcp.ts`'s `isValidRenamedArray` guard on the same class of rename.
|
|
23068
|
+
*/
|
|
23069
|
+
function isStringArray(value) {
|
|
23070
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
23071
|
+
}
|
|
23072
|
+
/**
|
|
22862
23073
|
* Resolves and sets the transport type for each MCP server config.
|
|
22863
23074
|
* GitHub Copilot CLI requires the "type" field for each server. A server it
|
|
22864
23075
|
* cannot express — one that names no transport, or names one it carries no way
|
|
@@ -22902,7 +23113,11 @@ function addTypeField(mcpServers, logger) {
|
|
|
22902
23113
|
}
|
|
22903
23114
|
const { httpUrl: _httpUrl, ...rest } = parsed;
|
|
22904
23115
|
result[name] = {
|
|
22905
|
-
...
|
|
23116
|
+
...renameEnabledToolsToTools({
|
|
23117
|
+
server: rest,
|
|
23118
|
+
serverName: name,
|
|
23119
|
+
logger
|
|
23120
|
+
}),
|
|
22906
23121
|
type,
|
|
22907
23122
|
url
|
|
22908
23123
|
};
|
|
@@ -22920,7 +23135,11 @@ function addTypeField(mcpServers, logger) {
|
|
|
22920
23135
|
continue;
|
|
22921
23136
|
}
|
|
22922
23137
|
result[name] = {
|
|
22923
|
-
...
|
|
23138
|
+
...renameEnabledToolsToTools({
|
|
23139
|
+
server: local,
|
|
23140
|
+
serverName: name,
|
|
23141
|
+
logger
|
|
23142
|
+
}),
|
|
22924
23143
|
type,
|
|
22925
23144
|
command: head,
|
|
22926
23145
|
...tail.length > 0 && { args: tail }
|
|
@@ -22934,11 +23153,19 @@ function addTypeField(mcpServers, logger) {
|
|
|
22934
23153
|
function removeTypeField(config) {
|
|
22935
23154
|
const result = {};
|
|
22936
23155
|
for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
|
|
22937
|
-
|
|
22938
|
-
|
|
23156
|
+
const { tools, ...withoutTools } = server;
|
|
23157
|
+
const restored = isStringArray(tools) ? {
|
|
23158
|
+
...withoutTools,
|
|
23159
|
+
enabledTools: tools
|
|
23160
|
+
} : {
|
|
23161
|
+
...withoutTools,
|
|
23162
|
+
...tools !== void 0 && { tools }
|
|
23163
|
+
};
|
|
23164
|
+
if (restored.type !== "stdio") {
|
|
23165
|
+
result[name] = restored;
|
|
22939
23166
|
continue;
|
|
22940
23167
|
}
|
|
22941
|
-
const { type: _, ...rest } =
|
|
23168
|
+
const { type: _, ...rest } = restored;
|
|
22942
23169
|
result[name] = rest;
|
|
22943
23170
|
}
|
|
22944
23171
|
return result;
|
|
@@ -23590,11 +23817,11 @@ function applyGooseStdioFields(ext, config) {
|
|
|
23590
23817
|
if (Array.isArray(command)) {
|
|
23591
23818
|
if (typeof command[0] === "string") ext.cmd = command[0];
|
|
23592
23819
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
23593
|
-
const args = isStringArray$
|
|
23820
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
23594
23821
|
if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
|
|
23595
23822
|
} else if (typeof command === "string") {
|
|
23596
23823
|
ext.cmd = command;
|
|
23597
|
-
if (isStringArray$
|
|
23824
|
+
if (isStringArray$2(config.args)) ext.args = config.args;
|
|
23598
23825
|
}
|
|
23599
23826
|
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
23600
23827
|
}
|
|
@@ -23696,7 +23923,7 @@ function convertFromGooseFormat(extensions) {
|
|
|
23696
23923
|
else if (type === "streamable_http") server.type = "http";
|
|
23697
23924
|
else server.type = "stdio";
|
|
23698
23925
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
23699
|
-
if (isStringArray$
|
|
23926
|
+
if (isStringArray$2(ext.args)) server.args = ext.args;
|
|
23700
23927
|
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
23701
23928
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
23702
23929
|
if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
@@ -23717,11 +23944,11 @@ function buildGoosePluginStdioServer(config) {
|
|
|
23717
23944
|
if (Array.isArray(command)) {
|
|
23718
23945
|
if (typeof command[0] === "string") server.command = command[0];
|
|
23719
23946
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
23720
|
-
const args = isStringArray$
|
|
23947
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
23721
23948
|
if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
|
|
23722
23949
|
} else if (typeof command === "string") {
|
|
23723
23950
|
server.command = command;
|
|
23724
|
-
if (isStringArray$
|
|
23951
|
+
if (isStringArray$2(config.args)) server.args = config.args;
|
|
23725
23952
|
}
|
|
23726
23953
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
23727
23954
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -24053,7 +24280,7 @@ function copyHermesOauth(source) {
|
|
|
24053
24280
|
"client_secret"
|
|
24054
24281
|
]) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
24055
24282
|
if (typeof source.redirect_port === "number") oauth.redirect_port = source.redirect_port;
|
|
24056
|
-
if (isStringArray$
|
|
24283
|
+
if (isStringArray$2(source.scopes)) oauth.scopes = source.scopes;
|
|
24057
24284
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
24058
24285
|
}
|
|
24059
24286
|
function copyHermesAdvancedFields(source, target) {
|
|
@@ -24062,7 +24289,7 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
24062
24289
|
target.auth = source.auth;
|
|
24063
24290
|
copied = true;
|
|
24064
24291
|
}
|
|
24065
|
-
if (typeof source.client_cert === "string" || isStringArray$
|
|
24292
|
+
if (typeof source.client_cert === "string" || isStringArray$2(source.client_cert)) {
|
|
24066
24293
|
target.client_cert = source.client_cert;
|
|
24067
24294
|
copied = true;
|
|
24068
24295
|
}
|
|
@@ -24131,8 +24358,8 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
24131
24358
|
*/
|
|
24132
24359
|
function buildHermesToolsBlock(config) {
|
|
24133
24360
|
const tools = {};
|
|
24134
|
-
if (isStringArray$
|
|
24135
|
-
if (isStringArray$
|
|
24361
|
+
if (isStringArray$2(config.enabledTools)) tools.include = config.enabledTools;
|
|
24362
|
+
if (isStringArray$2(config.disabledTools)) tools.exclude = config.disabledTools;
|
|
24136
24363
|
if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
|
|
24137
24364
|
if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
|
|
24138
24365
|
return tools;
|
|
@@ -24144,8 +24371,8 @@ function buildHermesToolsBlock(config) {
|
|
|
24144
24371
|
* `promptsEnabled`/`resourcesEnabled` top-level toggles.
|
|
24145
24372
|
*/
|
|
24146
24373
|
function applyHermesToolsBlock(hermesTools, server) {
|
|
24147
|
-
if (isStringArray$
|
|
24148
|
-
if (isStringArray$
|
|
24374
|
+
if (isStringArray$2(hermesTools.include)) server.enabledTools = hermesTools.include;
|
|
24375
|
+
if (isStringArray$2(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
|
|
24149
24376
|
if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
|
|
24150
24377
|
if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
|
|
24151
24378
|
}
|
|
@@ -24171,11 +24398,11 @@ function convertServerToHermes(config) {
|
|
|
24171
24398
|
if (Array.isArray(command)) {
|
|
24172
24399
|
if (typeof command[0] === "string") out.command = command[0];
|
|
24173
24400
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
24174
|
-
const args = isStringArray$
|
|
24401
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
24175
24402
|
if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
|
|
24176
24403
|
} else if (typeof command === "string") {
|
|
24177
24404
|
out.command = command;
|
|
24178
|
-
if (isStringArray$
|
|
24405
|
+
if (isStringArray$2(config.args)) out.args = config.args;
|
|
24179
24406
|
}
|
|
24180
24407
|
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
24181
24408
|
} else if (url !== void 0) {
|
|
@@ -24225,7 +24452,7 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
24225
24452
|
if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord$1(config)) continue;
|
|
24226
24453
|
const server = {};
|
|
24227
24454
|
if (typeof config.command === "string") server.command = config.command;
|
|
24228
|
-
if (isStringArray$
|
|
24455
|
+
if (isStringArray$2(config.args)) server.args = config.args;
|
|
24229
24456
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
24230
24457
|
if (typeof config.url === "string") server.url = config.url;
|
|
24231
24458
|
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
@@ -25183,13 +25410,17 @@ function mergeToolLists(...lists) {
|
|
|
25183
25410
|
*
|
|
25184
25411
|
* Both native names are documented per-server fields, so a config that already
|
|
25185
25412
|
* spells them natively keeps working: the two lists are merged rather than
|
|
25186
|
-
* one overwriting the other.
|
|
25413
|
+
* one overwriting the other. That is the "merge" branch of the collision rule
|
|
25414
|
+
* documented on `enabledTools` in `src/types/mcp.ts` — it applies here because
|
|
25415
|
+
* the native names are redundant spellings of the same additive lists;
|
|
25416
|
+
* `copilotcli-mcp.ts` lets the canonical value win instead, and
|
|
25417
|
+
* `codexcli-mcp.ts` refuses the canonical value instead.
|
|
25187
25418
|
* @see https://kiro.dev/docs/mcp/configuration/
|
|
25188
25419
|
*/
|
|
25189
25420
|
function toKiroMcpServers(servers) {
|
|
25190
25421
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
25191
25422
|
const { kiroAutoApprove, kiroAutoBlock, disabledTools, ...rest } = server;
|
|
25192
|
-
const autoApprove = mergeToolLists(isStringArray$
|
|
25423
|
+
const autoApprove = mergeToolLists(isStringArray$2(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
|
|
25193
25424
|
const disabled = mergeToolLists(disabledTools, kiroAutoBlock);
|
|
25194
25425
|
return [name, {
|
|
25195
25426
|
...rest,
|
|
@@ -25213,7 +25444,7 @@ function fromKiroMcpServers(servers) {
|
|
|
25213
25444
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
25214
25445
|
if (server === null || typeof server !== "object" || Array.isArray(server)) return [name, server];
|
|
25215
25446
|
const { autoApprove, ...rest } = server;
|
|
25216
|
-
if (!isStringArray$
|
|
25447
|
+
if (!isStringArray$2(autoApprove)) return [name, server];
|
|
25217
25448
|
return [name, {
|
|
25218
25449
|
...rest,
|
|
25219
25450
|
kiroAutoApprove: autoApprove
|
|
@@ -26355,7 +26586,7 @@ async function readRovodevConfigYaml({ outputRoot }) {
|
|
|
26355
26586
|
}
|
|
26356
26587
|
function disabledNamesOf(config) {
|
|
26357
26588
|
const mcpBlock = config && isRecord$1(config.mcp) ? config.mcp : {};
|
|
26358
|
-
return isStringArray$
|
|
26589
|
+
return isStringArray$2(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
|
|
26359
26590
|
}
|
|
26360
26591
|
/**
|
|
26361
26592
|
* Auxiliary writer for the `mcp:` block of `.rovodev/config.yml` (project) /
|
|
@@ -26488,7 +26719,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
26488
26719
|
return [];
|
|
26489
26720
|
}
|
|
26490
26721
|
const existingMcp = isRecord$1(existingParsed.mcp) ? { ...existingParsed.mcp } : {};
|
|
26491
|
-
const existingDisabled = isStringArray$
|
|
26722
|
+
const existingDisabled = isStringArray$2(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
|
|
26492
26723
|
const managedNameSet = new Set(managedNames);
|
|
26493
26724
|
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
26494
26725
|
if (reEnabled.length > 0) logger?.warn(`Rovo Dev MCP: re-enabling ${reEnabled.join(", ")} — the canonical config does not mark ${reEnabled.length === 1 ? "it" : "them"} disabled. Set "disabled": true in .rulesync/mcp.jsonc to keep a managed server off.`);
|
|
@@ -27330,7 +27561,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27330
27561
|
meta: {
|
|
27331
27562
|
supportsProject: true,
|
|
27332
27563
|
supportsGlobal: true,
|
|
27333
|
-
supportsEnabledTools:
|
|
27564
|
+
supportsEnabledTools: true,
|
|
27334
27565
|
supportsDisabledTools: false
|
|
27335
27566
|
}
|
|
27336
27567
|
}],
|
|
@@ -27563,6 +27794,27 @@ const defaultGetFactory$3 = (target) => {
|
|
|
27563
27794
|
if (!factory) throw new Error(`Unsupported tool target: ${target}`);
|
|
27564
27795
|
return factory;
|
|
27565
27796
|
};
|
|
27797
|
+
/**
|
|
27798
|
+
* Warn about per-server tool filters the target tool cannot express.
|
|
27799
|
+
*
|
|
27800
|
+
* `enabledTools`/`disabledTools` are canonical fields, so a single
|
|
27801
|
+
* `.rulesync/.mcp.json` can carry a filter that only some targets read. The
|
|
27802
|
+
* unsupported ones are stripped before generation (writing them would produce a
|
|
27803
|
+
* key the tool discards on load), which used to happen silently — the filter
|
|
27804
|
+
* simply did not apply and nothing said so. Warning names the servers whose
|
|
27805
|
+
* filter is being dropped for this target so the gap is visible at generate
|
|
27806
|
+
* time rather than in the tool's behavior.
|
|
27807
|
+
*
|
|
27808
|
+
* Only fields actually present are reported, so a config that never authored
|
|
27809
|
+
* the filter stays quiet.
|
|
27810
|
+
*/
|
|
27811
|
+
function warnStrippedMcpServerFields({ mcpServers, fields, toolTarget, logger }) {
|
|
27812
|
+
for (const field of fields) {
|
|
27813
|
+
const serverNames = Object.entries(mcpServers ?? {}).filter(([, serverConfig]) => serverConfig[field] !== void 0).map(([serverName]) => serverName);
|
|
27814
|
+
if (serverNames.length === 0) continue;
|
|
27815
|
+
logger.warn(`${toolTarget} does not read the per-server \`${field}\` MCP tool filter; dropping it from ${serverNames.join(", ")}.`);
|
|
27816
|
+
}
|
|
27817
|
+
}
|
|
27566
27818
|
var McpProcessor = class extends FeatureProcessor {
|
|
27567
27819
|
toolTarget;
|
|
27568
27820
|
global;
|
|
@@ -27642,6 +27894,12 @@ var McpProcessor = class extends FeatureProcessor {
|
|
|
27642
27894
|
const fieldsToStrip = [];
|
|
27643
27895
|
if (!factory.meta.supportsEnabledTools) fieldsToStrip.push("enabledTools");
|
|
27644
27896
|
if (!factory.meta.supportsDisabledTools) fieldsToStrip.push("disabledTools");
|
|
27897
|
+
warnStrippedMcpServerFields({
|
|
27898
|
+
mcpServers: targetedRulesyncMcp.getJson().mcpServers,
|
|
27899
|
+
fields: fieldsToStrip,
|
|
27900
|
+
toolTarget: this.toolTarget,
|
|
27901
|
+
logger: this.logger
|
|
27902
|
+
});
|
|
27645
27903
|
const filteredRulesyncMcp = targetedRulesyncMcp.stripMcpServerFields(fieldsToStrip);
|
|
27646
27904
|
return await factory.class.fromRulesyncMcp({
|
|
27647
27905
|
outputRoot: this.outputRoot,
|
|
@@ -28154,6 +28412,34 @@ function convertAmpToRulesync({ disable, permissions }) {
|
|
|
28154
28412
|
//#endregion
|
|
28155
28413
|
//#region src/features/permissions/antigravity-cli-permissions.ts
|
|
28156
28414
|
/**
|
|
28415
|
+
* Top-level `~/.gemini/antigravity-cli/settings.json` keys the `antigravity-cli`
|
|
28416
|
+
* permissions override may author. They are siblings of `permissions`, passed
|
|
28417
|
+
* through verbatim in both directions.
|
|
28418
|
+
*/
|
|
28419
|
+
const ANTIGRAVITY_CLI_OVERRIDE_KEYS = [
|
|
28420
|
+
"toolPermission",
|
|
28421
|
+
"enableTerminalSandbox",
|
|
28422
|
+
"artifactReviewPolicy",
|
|
28423
|
+
"allowNonWorkspaceAccess",
|
|
28424
|
+
"agentMode"
|
|
28425
|
+
];
|
|
28426
|
+
const moduleLogger$2 = fallbackLogger;
|
|
28427
|
+
/**
|
|
28428
|
+
* Narrow an imported `settings.json` value to one of the documented values of
|
|
28429
|
+
* an enum-typed override key. The override schema declares `toolPermission`,
|
|
28430
|
+
* `artifactReviewPolicy` and `agentMode` as `z.enum`, so carrying an
|
|
28431
|
+
* unrecognized value through — a hand-written typo, or a preset a newer
|
|
28432
|
+
* Antigravity release added — would fail validation of the whole imported
|
|
28433
|
+
* `.rulesync/permissions.jsonc` instead of dropping just that key. A dropped
|
|
28434
|
+
* value is warned about rather than discarded silently, so a preset rulesync
|
|
28435
|
+
* does not know about yet is visible instead of quietly missing.
|
|
28436
|
+
*/
|
|
28437
|
+
function pickDocumentedValue({ key, value, documented }) {
|
|
28438
|
+
if (typeof value !== "string") return void 0;
|
|
28439
|
+
if (documented.includes(value)) return value;
|
|
28440
|
+
moduleLogger$2.warn(`Antigravity CLI permissions: dropping '${key}: ${value}' on import — it is not one of the documented values (${documented.join(", ")}), and carrying it through would fail validation of the whole permissions file. Author it under the 'antigravity-cli' override by hand once rulesync supports it.`);
|
|
28441
|
+
}
|
|
28442
|
+
/**
|
|
28157
28443
|
* Deliberate mapping from rulesync canonical permission categories to the
|
|
28158
28444
|
* Antigravity CLI Fine-Grained Permissions Engine action vocabulary
|
|
28159
28445
|
* (`read_file`, `write_file`, `read_url`, `command`, `mcp`, plus `execute_url` /
|
|
@@ -28228,9 +28514,10 @@ function buildPermissionEntry$1(toolName, pattern) {
|
|
|
28228
28514
|
* file (global scope only). The file holds other CLI settings besides
|
|
28229
28515
|
* permissions, so it is never deleted.
|
|
28230
28516
|
*
|
|
28231
|
-
*
|
|
28517
|
+
* Five CLI-only autonomy/sandbox knobs outside the allow/ask/deny arrays —
|
|
28232
28518
|
* `toolPermission` (the global autonomy preset), `enableTerminalSandbox`,
|
|
28233
|
-
* `artifactReviewPolicy` and `
|
|
28519
|
+
* `artifactReviewPolicy`, `allowNonWorkspaceAccess` and `agentMode` (the
|
|
28520
|
+
* baseline execution mode) — are authored and
|
|
28234
28521
|
* round-tripped through the `antigravity-cli` permissions override (see
|
|
28235
28522
|
* `AntigravityCliPermissionsOverrideSchema`).
|
|
28236
28523
|
*/
|
|
@@ -28294,10 +28581,10 @@ var AntigravityCliPermissions = class AntigravityCliPermissions extends ToolPerm
|
|
|
28294
28581
|
permissions: mergedPermissions
|
|
28295
28582
|
};
|
|
28296
28583
|
const override = config["antigravity-cli"];
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28584
|
+
for (const key of ANTIGRAVITY_CLI_OVERRIDE_KEYS) {
|
|
28585
|
+
const value = override?.[key];
|
|
28586
|
+
if (value !== void 0) merged[key] = value;
|
|
28587
|
+
}
|
|
28301
28588
|
const fileContent = JSON.stringify(merged, null, 2);
|
|
28302
28589
|
return new AntigravityCliPermissions({
|
|
28303
28590
|
outputRoot,
|
|
@@ -28322,10 +28609,26 @@ var AntigravityCliPermissions = class AntigravityCliPermissions extends ToolPerm
|
|
|
28322
28609
|
deny: permissions.deny ?? []
|
|
28323
28610
|
});
|
|
28324
28611
|
const override = {};
|
|
28325
|
-
|
|
28612
|
+
const toolPermission = pickDocumentedValue({
|
|
28613
|
+
key: "toolPermission",
|
|
28614
|
+
value: settings.toolPermission,
|
|
28615
|
+
documented: ANTIGRAVITY_CLI_TOOL_PERMISSIONS
|
|
28616
|
+
});
|
|
28617
|
+
if (toolPermission !== void 0) override.toolPermission = toolPermission;
|
|
28326
28618
|
if (typeof settings.enableTerminalSandbox === "boolean") override.enableTerminalSandbox = settings.enableTerminalSandbox;
|
|
28327
|
-
|
|
28619
|
+
const artifactReviewPolicy = pickDocumentedValue({
|
|
28620
|
+
key: "artifactReviewPolicy",
|
|
28621
|
+
value: settings.artifactReviewPolicy,
|
|
28622
|
+
documented: ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES
|
|
28623
|
+
});
|
|
28624
|
+
if (artifactReviewPolicy !== void 0) override.artifactReviewPolicy = artifactReviewPolicy;
|
|
28328
28625
|
if (typeof settings.allowNonWorkspaceAccess === "boolean") override.allowNonWorkspaceAccess = settings.allowNonWorkspaceAccess;
|
|
28626
|
+
const agentMode = pickDocumentedValue({
|
|
28627
|
+
key: "agentMode",
|
|
28628
|
+
value: settings.agentMode,
|
|
28629
|
+
documented: ANTIGRAVITY_CLI_AGENT_MODES
|
|
28630
|
+
});
|
|
28631
|
+
if (agentMode !== void 0) override.agentMode = agentMode;
|
|
28329
28632
|
const result = { ...config };
|
|
28330
28633
|
if (Object.keys(override).length > 0) result["antigravity-cli"] = override;
|
|
28331
28634
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
@@ -29151,6 +29454,119 @@ function deepMergeRecords(base, patch) {
|
|
|
29151
29454
|
}
|
|
29152
29455
|
return merged;
|
|
29153
29456
|
}
|
|
29457
|
+
/**
|
|
29458
|
+
* `sandbox.*` paths Claude Code honors only from user settings, managed settings
|
|
29459
|
+
* and the `--settings` CLI flag. Written into a project `.claude/settings.json`
|
|
29460
|
+
* they are silently ignored, so a committed file would read as though it
|
|
29461
|
+
* enforced a sandbox policy while doing nothing — security-relevant for
|
|
29462
|
+
* `network.strictAllowlist` and the credential-masking keys in particular.
|
|
29463
|
+
* Generation therefore drops them at project scope with a per-key warning and
|
|
29464
|
+
* emits them only under `--global`.
|
|
29465
|
+
*
|
|
29466
|
+
* Deliberately NOT listed:
|
|
29467
|
+
* - `bwrapPath` / `socatPath`: v2.1.232 added them to the managed-settings
|
|
29468
|
+
* approval dialog, which is a consent prompt, not a project-scope rejection.
|
|
29469
|
+
* - `credentials.envVars` / `credentials.files`: the ignored-at-project-scope
|
|
29470
|
+
* unit is the individual entry's mode, not the settings key, and the same
|
|
29471
|
+
* lists carry `deny` entries that project settings *do* honor — dropping a
|
|
29472
|
+
* whole list would remove real restrictions. `stripProjectIgnoredMaskEntries`
|
|
29473
|
+
* filters those lists per entry instead.
|
|
29474
|
+
*
|
|
29475
|
+
* @see https://code.claude.com/docs/en/sandboxing
|
|
29476
|
+
* @see https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md — v2.1.232 scoped `sandbox.ripgrep`
|
|
29477
|
+
*/
|
|
29478
|
+
const CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS = [
|
|
29479
|
+
["filesystem", "disabled"],
|
|
29480
|
+
["network", "strictAllowlist"],
|
|
29481
|
+
["network", "tlsTerminate"],
|
|
29482
|
+
["credentials", "allowPlaintextInject"],
|
|
29483
|
+
["credentials", "awsPairs"],
|
|
29484
|
+
["credentials", "sigv4"],
|
|
29485
|
+
["allowAppleEvents"],
|
|
29486
|
+
["ripgrep"]
|
|
29487
|
+
];
|
|
29488
|
+
/**
|
|
29489
|
+
* Copy of the authored `sandbox` override with the user/managed-only paths
|
|
29490
|
+
* removed, warning once per dropped path. Only the override copy is filtered —
|
|
29491
|
+
* a value already hand-written in the target file is left untouched, matching
|
|
29492
|
+
* the `qwencode` `security.allowPrivateNetworkHooks` precedent.
|
|
29493
|
+
*/
|
|
29494
|
+
function stripGlobalOnlySandboxPaths({ sandbox, relativeFilePath, logger }) {
|
|
29495
|
+
const filtered = structuredClone(sandbox);
|
|
29496
|
+
for (const path of CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS) {
|
|
29497
|
+
const leaf = path.at(-1);
|
|
29498
|
+
if (leaf === void 0) continue;
|
|
29499
|
+
const parentPath = path.slice(0, -1);
|
|
29500
|
+
let parent = filtered;
|
|
29501
|
+
for (const segment of parentPath) {
|
|
29502
|
+
const next = parent[segment];
|
|
29503
|
+
if (!isPlainRecord(next)) {
|
|
29504
|
+
parent = {};
|
|
29505
|
+
break;
|
|
29506
|
+
}
|
|
29507
|
+
parent = next;
|
|
29508
|
+
}
|
|
29509
|
+
if (parent[leaf] === void 0) continue;
|
|
29510
|
+
delete parent[leaf];
|
|
29511
|
+
const [container] = parentPath;
|
|
29512
|
+
if (container !== void 0 && isPlainRecord(filtered[container])) {
|
|
29513
|
+
if (Object.keys(filtered[container]).length === 0) delete filtered[container];
|
|
29514
|
+
}
|
|
29515
|
+
logger?.warn(`Claude Code permissions: 'sandbox.${path.join(".")}' is only honored in user/managed/--settings settings, so it is not written to the project-scoped ${relativeFilePath}. Author it in the global scope instead, and check that file for a stale value an earlier generate may have left there.`);
|
|
29516
|
+
}
|
|
29517
|
+
return filtered;
|
|
29518
|
+
}
|
|
29519
|
+
/** The `sandbox.credentials` lists whose entries accept `"mode": "mask"`. */
|
|
29520
|
+
const CLAUDECODE_MASKABLE_CREDENTIAL_LISTS = ["envVars", "files"];
|
|
29521
|
+
/**
|
|
29522
|
+
* Copy of the authored `sandbox` override with the `credentials.envVars` /
|
|
29523
|
+
* `credentials.files` entries whose `mode` is `"mask"` removed, warning once per
|
|
29524
|
+
* list.
|
|
29525
|
+
*
|
|
29526
|
+
* Masking authorizes the sandbox proxy to send the real credential to the listed
|
|
29527
|
+
* hosts, so Claude Code honors it only from user settings, managed settings and
|
|
29528
|
+
* the `--settings` CLI flag; a `mask` entry in a repository's
|
|
29529
|
+
* `.claude/settings.json` is ignored outright. Keeping it would read as though
|
|
29530
|
+
* the credential were masked while nothing protects it — the one project-scope
|
|
29531
|
+
* gap whose consequence is a live credential leaving the sandbox unmasked.
|
|
29532
|
+
*
|
|
29533
|
+
* Filtered per entry rather than per key, because the same lists carry `deny`
|
|
29534
|
+
* entries that project settings *do* honor. `mode` is matched exactly: an entry
|
|
29535
|
+
* Claude Code cannot read as `mask` is not treated as one either (it degrades
|
|
29536
|
+
* such entries to `deny`), so the "reads as masked but isn't" state this guards
|
|
29537
|
+
* against cannot slip through a differently-spelled value.
|
|
29538
|
+
*
|
|
29539
|
+
* Like `stripGlobalOnlySandboxPaths`, only the override copy is filtered — a
|
|
29540
|
+
* value already in the target file is left untouched, which is why the warning
|
|
29541
|
+
* points at it.
|
|
29542
|
+
*
|
|
29543
|
+
* @see https://code.claude.com/docs/en/sandboxing — "`mask` entries … are all
|
|
29544
|
+
* ignored in a repository's `.claude/settings.json` or
|
|
29545
|
+
* `.claude/settings.local.json`"; the file-entry section states the same
|
|
29546
|
+
* settings-source restriction applies there.
|
|
29547
|
+
*/
|
|
29548
|
+
function stripProjectIgnoredMaskEntries({ sandbox, relativeFilePath, logger }) {
|
|
29549
|
+
const credentials = sandbox.credentials;
|
|
29550
|
+
if (!isPlainRecord(credentials)) return sandbox;
|
|
29551
|
+
const filteredCredentials = { ...credentials };
|
|
29552
|
+
let changed = false;
|
|
29553
|
+
for (const listKey of CLAUDECODE_MASKABLE_CREDENTIAL_LISTS) {
|
|
29554
|
+
const list = filteredCredentials[listKey];
|
|
29555
|
+
if (!Array.isArray(list)) continue;
|
|
29556
|
+
const kept = list.filter((entry) => !(isPlainRecord(entry) && entry.mode === "mask"));
|
|
29557
|
+
if (kept.length === list.length) continue;
|
|
29558
|
+
changed = true;
|
|
29559
|
+
const dropped = list.length - kept.length;
|
|
29560
|
+
logger?.warn(`Claude Code permissions: 'sandbox.credentials.${listKey}' entries with 'mode: "mask"' are only honored in user/managed/--settings settings, so ${dropped} of them ${dropped === 1 ? "was" : "were"} not written to the project-scoped ${relativeFilePath}. Author them in the global scope instead, and check that file for a stale value an earlier generate may have left there.`);
|
|
29561
|
+
if (kept.length === 0) delete filteredCredentials[listKey];
|
|
29562
|
+
else filteredCredentials[listKey] = kept;
|
|
29563
|
+
}
|
|
29564
|
+
if (!changed) return sandbox;
|
|
29565
|
+
const filtered = { ...sandbox };
|
|
29566
|
+
if (Object.keys(filteredCredentials).length === 0) delete filtered.credentials;
|
|
29567
|
+
else filtered.credentials = filteredCredentials;
|
|
29568
|
+
return filtered;
|
|
29569
|
+
}
|
|
29154
29570
|
const CLAUDE_PATH_RULE_ALIASES = {
|
|
29155
29571
|
Write: "Edit",
|
|
29156
29572
|
NotebookEdit: "Edit",
|
|
@@ -29204,7 +29620,7 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29204
29620
|
validate
|
|
29205
29621
|
});
|
|
29206
29622
|
}
|
|
29207
|
-
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, logger }) {
|
|
29623
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
29208
29624
|
const paths = ClaudecodePermissions.getSettablePaths();
|
|
29209
29625
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
29210
29626
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
@@ -29228,7 +29644,18 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29228
29644
|
};
|
|
29229
29645
|
}
|
|
29230
29646
|
const overrideSandbox = config.claudecode?.sandbox;
|
|
29231
|
-
if (isPlainRecord(overrideSandbox))
|
|
29647
|
+
if (isPlainRecord(overrideSandbox)) {
|
|
29648
|
+
const scopedSandbox = global ? overrideSandbox : stripProjectIgnoredMaskEntries({
|
|
29649
|
+
sandbox: stripGlobalOnlySandboxPaths({
|
|
29650
|
+
sandbox: overrideSandbox,
|
|
29651
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29652
|
+
logger
|
|
29653
|
+
}),
|
|
29654
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29655
|
+
logger
|
|
29656
|
+
});
|
|
29657
|
+
if (Object.keys(scopedSandbox).length > 0) settings.sandbox = deepMergeRecords(isPlainRecord(settings.sandbox) ? settings.sandbox : {}, scopedSandbox);
|
|
29658
|
+
}
|
|
29232
29659
|
const managedToolNames = managedClaudeToolNames(config);
|
|
29233
29660
|
const merged = applyPermissions({
|
|
29234
29661
|
settings,
|
|
@@ -31405,7 +31832,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
31405
31832
|
function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
31406
31833
|
const permission = {};
|
|
31407
31834
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
31408
|
-
const toolNames = isStringArray$
|
|
31835
|
+
const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
|
|
31409
31836
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
31410
31837
|
for (const toolName of toolNames) {
|
|
31411
31838
|
const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
|
|
@@ -31698,7 +32125,7 @@ const ACTION_RANK = {
|
|
|
31698
32125
|
* (mirrors the Cursor adapter's preservation of unmanaged types).
|
|
31699
32126
|
*/
|
|
31700
32127
|
function unmanagedEntries(existingPermission, key) {
|
|
31701
|
-
return (isStringArray$
|
|
32128
|
+
return (isStringArray$2(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
|
|
31702
32129
|
}
|
|
31703
32130
|
/**
|
|
31704
32131
|
* Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
|
|
@@ -31741,9 +32168,9 @@ function buildGrokPermissionArrays(config, existingPermission, logger) {
|
|
|
31741
32168
|
* described more than once resolves to the strictest action.
|
|
31742
32169
|
*/
|
|
31743
32170
|
function parseGrokPermissionArrays(permission) {
|
|
31744
|
-
const allow = isStringArray$
|
|
31745
|
-
const deny = isStringArray$
|
|
31746
|
-
const ask = isStringArray$
|
|
32171
|
+
const allow = isStringArray$2(permission.allow) ? permission.allow : void 0;
|
|
32172
|
+
const deny = isStringArray$2(permission.deny) ? permission.deny : void 0;
|
|
32173
|
+
const ask = isStringArray$2(permission.ask) ? permission.ask : void 0;
|
|
31747
32174
|
const rules = Array.isArray(permission[GROKCLI_PERMISSION_RULES_KEY]) ? permission[GROKCLI_PERMISSION_RULES_KEY] : void 0;
|
|
31748
32175
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
31749
32176
|
const result = {};
|
|
@@ -31925,20 +32352,20 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
31925
32352
|
const permission = clonePermissionBlock(provenance.permission);
|
|
31926
32353
|
reconcileCommandAllowlist({
|
|
31927
32354
|
permission,
|
|
31928
|
-
commandAllowlist: isStringArray$
|
|
32355
|
+
commandAllowlist: isStringArray$2(config.command_allowlist) ? config.command_allowlist : []
|
|
31929
32356
|
});
|
|
31930
32357
|
const approvals = isRecord$1(config.approvals) ? config.approvals : {};
|
|
31931
32358
|
reconcileNativeDenies({
|
|
31932
32359
|
permission,
|
|
31933
32360
|
category: "bash",
|
|
31934
|
-
patterns: isStringArray$
|
|
32361
|
+
patterns: isStringArray$2(approvals.deny) ? approvals.deny : []
|
|
31935
32362
|
});
|
|
31936
32363
|
const security = isRecord$1(config.security) ? config.security : {};
|
|
31937
32364
|
const websiteBlocklist = isRecord$1(security.website_blocklist) ? security.website_blocklist : {};
|
|
31938
32365
|
reconcileNativeDenies({
|
|
31939
32366
|
permission,
|
|
31940
32367
|
category: "webfetch",
|
|
31941
|
-
patterns: websiteBlocklist.enabled === true && isStringArray$
|
|
32368
|
+
patterns: websiteBlocklist.enabled === true && isStringArray$2(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
31942
32369
|
});
|
|
31943
32370
|
removeEmptyCategories(permission);
|
|
31944
32371
|
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
@@ -33126,7 +33553,7 @@ function extractKiroOverride(toolsSettings) {
|
|
|
33126
33553
|
if (Object.keys(overrideToolsSettings).length === 0) return void 0;
|
|
33127
33554
|
return { toolsSettings: overrideToolsSettings };
|
|
33128
33555
|
}
|
|
33129
|
-
function asStringArray(value) {
|
|
33556
|
+
function asStringArray$1(value) {
|
|
33130
33557
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
33131
33558
|
}
|
|
33132
33559
|
/**
|
|
@@ -33136,8 +33563,8 @@ function asStringArray(value) {
|
|
|
33136
33563
|
*/
|
|
33137
33564
|
function rulesFromArrays(settings, allowKey, denyKey) {
|
|
33138
33565
|
const rules = {};
|
|
33139
|
-
for (const pattern of asStringArray(settings[allowKey])) rules[pattern] = "allow";
|
|
33140
|
-
for (const pattern of asStringArray(settings[denyKey])) rules[pattern] = "deny";
|
|
33566
|
+
for (const pattern of asStringArray$1(settings[allowKey])) rules[pattern] = "allow";
|
|
33567
|
+
for (const pattern of asStringArray$1(settings[denyKey])) rules[pattern] = "deny";
|
|
33141
33568
|
return rules;
|
|
33142
33569
|
}
|
|
33143
33570
|
//#endregion
|
|
@@ -33366,6 +33793,115 @@ var OpencodePermissions = class OpencodePermissions extends ToolPermissions {
|
|
|
33366
33793
|
}
|
|
33367
33794
|
};
|
|
33368
33795
|
//#endregion
|
|
33796
|
+
//#region src/features/permissions/pi-permissions.ts
|
|
33797
|
+
/** The one `settings.json` key this feature owns. */
|
|
33798
|
+
const DEFAULT_TOOLS_KEY = "defaultTools";
|
|
33799
|
+
/**
|
|
33800
|
+
* Permissions generator for Pi Coding Agent.
|
|
33801
|
+
*
|
|
33802
|
+
* Pi exposes no allow/ask/deny rule surface, so no canonical permission
|
|
33803
|
+
* category maps onto it. Its one repository-syncable tool gate is `defaultTools` —
|
|
33804
|
+
* the built-in tools enabled at startup (v0.84.2) — authored through the `pi`
|
|
33805
|
+
* permissions override.
|
|
33806
|
+
*
|
|
33807
|
+
* Scope semantics are the **opposite** of the union-merge most targets use:
|
|
33808
|
+
* a project `defaultTools` array *replaces* the global array rather than adding
|
|
33809
|
+
* to it, so the two scopes are written independently and never combined.
|
|
33810
|
+
* Project scope writes `.pi/settings.json`; global scope writes
|
|
33811
|
+
* `~/.pi/agent/settings.json`.
|
|
33812
|
+
*
|
|
33813
|
+
* The file is hand-edited and holds many unrelated keys (`theme`,
|
|
33814
|
+
* `defaultModel`, `packages`, `sessionDir`, ...), so writes go through the
|
|
33815
|
+
* shared-config gateway with `defaultTools` as the only owned key, and the file
|
|
33816
|
+
* is never deleted.
|
|
33817
|
+
*
|
|
33818
|
+
* @see https://pi.dev/docs/latest/settings
|
|
33819
|
+
*/
|
|
33820
|
+
var PiPermissions = class PiPermissions extends ToolPermissions {
|
|
33821
|
+
constructor(params) {
|
|
33822
|
+
super({
|
|
33823
|
+
...params,
|
|
33824
|
+
fileContent: params.fileContent ?? "{}"
|
|
33825
|
+
});
|
|
33826
|
+
}
|
|
33827
|
+
/** `settings.json` holds unrelated user settings, so it must not be deleted. */
|
|
33828
|
+
isDeletable() {
|
|
33829
|
+
return false;
|
|
33830
|
+
}
|
|
33831
|
+
static getSettablePaths({ global = false } = {}) {
|
|
33832
|
+
return {
|
|
33833
|
+
relativeDirPath: global ? PI_AGENT_DIR_PATH : ".pi",
|
|
33834
|
+
relativeFilePath: PI_SETTINGS_FILE_NAME
|
|
33835
|
+
};
|
|
33836
|
+
}
|
|
33837
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
33838
|
+
const paths = PiPermissions.getSettablePaths({ global });
|
|
33839
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
|
|
33840
|
+
return new PiPermissions({
|
|
33841
|
+
outputRoot,
|
|
33842
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33843
|
+
relativeFilePath: paths.relativeFilePath,
|
|
33844
|
+
fileContent,
|
|
33845
|
+
validate,
|
|
33846
|
+
global
|
|
33847
|
+
});
|
|
33848
|
+
}
|
|
33849
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false }) {
|
|
33850
|
+
const paths = PiPermissions.getSettablePaths({ global });
|
|
33851
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
33852
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
33853
|
+
const defaultTools = rulesyncPermissions.getJson().pi?.defaultTools;
|
|
33854
|
+
const patch = {};
|
|
33855
|
+
if (defaultTools !== void 0) patch[DEFAULT_TOOLS_KEY] = defaultTools;
|
|
33856
|
+
return new PiPermissions({
|
|
33857
|
+
outputRoot,
|
|
33858
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33859
|
+
relativeFilePath: paths.relativeFilePath,
|
|
33860
|
+
fileContent: applySharedConfigPatch({
|
|
33861
|
+
fileKey: sharedConfigFileKey(paths),
|
|
33862
|
+
feature: "permissions",
|
|
33863
|
+
existingContent,
|
|
33864
|
+
patch,
|
|
33865
|
+
filePath
|
|
33866
|
+
}),
|
|
33867
|
+
validate: true,
|
|
33868
|
+
global
|
|
33869
|
+
});
|
|
33870
|
+
}
|
|
33871
|
+
toRulesyncPermissions() {
|
|
33872
|
+
const defaultTools = this.parseSettings()[DEFAULT_TOOLS_KEY];
|
|
33873
|
+
const config = { permission: {} };
|
|
33874
|
+
if (Array.isArray(defaultTools) && defaultTools.every((tool) => typeof tool === "string")) config.pi = { [DEFAULT_TOOLS_KEY]: defaultTools };
|
|
33875
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(config, null, 2) });
|
|
33876
|
+
}
|
|
33877
|
+
parseSettings() {
|
|
33878
|
+
try {
|
|
33879
|
+
return parseSharedConfig({
|
|
33880
|
+
format: "json",
|
|
33881
|
+
fileContent: this.getFileContent()
|
|
33882
|
+
});
|
|
33883
|
+
} catch (error) {
|
|
33884
|
+
throw new Error(`Failed to parse Pi settings in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
33885
|
+
}
|
|
33886
|
+
}
|
|
33887
|
+
validate() {
|
|
33888
|
+
return {
|
|
33889
|
+
success: true,
|
|
33890
|
+
error: null
|
|
33891
|
+
};
|
|
33892
|
+
}
|
|
33893
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
33894
|
+
return new PiPermissions({
|
|
33895
|
+
outputRoot,
|
|
33896
|
+
relativeDirPath,
|
|
33897
|
+
relativeFilePath,
|
|
33898
|
+
fileContent: "{}",
|
|
33899
|
+
validate: false,
|
|
33900
|
+
global
|
|
33901
|
+
});
|
|
33902
|
+
}
|
|
33903
|
+
};
|
|
33904
|
+
//#endregion
|
|
33369
33905
|
//#region src/features/permissions/qwencode-permissions.ts
|
|
33370
33906
|
/**
|
|
33371
33907
|
* Qwen Code uses a settings.json file in `.qwen/` (project) or `~/.qwen/` (global).
|
|
@@ -34429,7 +34965,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
34429
34965
|
permission[category] ??= {};
|
|
34430
34966
|
permission[category][CATCH_ALL_PATTERN$1] = levels.reduce((strictest, level) => strictestAction(strictest, level ?? implicitLevel), permission[category][CATCH_ALL_PATTERN$1]);
|
|
34431
34967
|
}
|
|
34432
|
-
if (isStringArray$
|
|
34968
|
+
if (isStringArray$2(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
34433
34969
|
permission.read ??= {};
|
|
34434
34970
|
permission.read[path] ??= "allow";
|
|
34435
34971
|
}
|
|
@@ -35406,8 +35942,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
|
|
|
35406
35942
|
const executionProfiles = isRecord$1(agents[EXECUTION_PROFILES_KEY]) ? agents[EXECUTION_PROFILES_KEY] : void 0;
|
|
35407
35943
|
const defaultProfile = executionProfiles && isRecord$1(executionProfiles[DEFAULT_PROFILE_KEY]) ? executionProfiles[DEFAULT_PROFILE_KEY] : void 0;
|
|
35408
35944
|
const config = convertWarpToRulesyncPermissions({
|
|
35409
|
-
allow: defaultProfile ? isStringArray$
|
|
35410
|
-
deny: defaultProfile ? isStringArray$
|
|
35945
|
+
allow: defaultProfile ? isStringArray$2(defaultProfile[PROFILE_ALLOWLIST_KEY]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$2(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
|
|
35946
|
+
deny: defaultProfile ? isStringArray$2(defaultProfile[PROFILE_DENYLIST_KEY]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$2(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
|
|
35411
35947
|
});
|
|
35412
35948
|
const warpOverride = {};
|
|
35413
35949
|
for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
|
|
@@ -35940,6 +36476,181 @@ var ZedPermissions = class ZedPermissions extends ToolPermissions {
|
|
|
35940
36476
|
}
|
|
35941
36477
|
};
|
|
35942
36478
|
//#endregion
|
|
36479
|
+
//#region src/constants/zoocode-paths.ts
|
|
36480
|
+
/**
|
|
36481
|
+
* Zoo Code is a VS Code extension, so its committable command allow/deny lists
|
|
36482
|
+
* are workspace settings rather than files in the `.roo/` agent-asset tree that
|
|
36483
|
+
* the other Zoo Code features write.
|
|
36484
|
+
*
|
|
36485
|
+
* `zoo-code.allowedCommands` / `zoo-code.deniedCommands` are contributed with no
|
|
36486
|
+
* `scope`, which in VS Code means `window` scope — settable in a workspace's
|
|
36487
|
+
* `.vscode/settings.json` — and `ClineProvider.mergeCommandLists()` unions the
|
|
36488
|
+
* workspace values into the effective auto-approval lists.
|
|
36489
|
+
*
|
|
36490
|
+
* The `zoo-code.*` namespace is Zoo-era (the v3.74.0 rebrand); the archived Roo
|
|
36491
|
+
* Code lineage spelled the same settings `roo-cline.*`, so this surface is
|
|
36492
|
+
* deliberately not shared with the `roo` target.
|
|
36493
|
+
*
|
|
36494
|
+
* @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/package.json
|
|
36495
|
+
* @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/core/webview/ClineProvider.ts
|
|
36496
|
+
*/
|
|
36497
|
+
const ZOOCODE_VSCODE_SETTINGS_DIR = ".vscode";
|
|
36498
|
+
const ZOOCODE_VSCODE_SETTINGS_FILE_NAME = "settings.json";
|
|
36499
|
+
const ZOOCODE_ALLOWED_COMMANDS_KEY = "zoo-code.allowedCommands";
|
|
36500
|
+
const ZOOCODE_DENIED_COMMANDS_KEY = "zoo-code.deniedCommands";
|
|
36501
|
+
//#endregion
|
|
36502
|
+
//#region src/features/permissions/zoocode-permissions.ts
|
|
36503
|
+
/**
|
|
36504
|
+
* The canonical category Zoo Code's command lists correspond to. Zoo Code gates
|
|
36505
|
+
* terminal command execution and nothing else through these settings, so only
|
|
36506
|
+
* `bash` maps; `read`/`write`/`edit`/`webfetch` have no workspace-settable
|
|
36507
|
+
* counterpart in the extension's contributions.
|
|
36508
|
+
*/
|
|
36509
|
+
const COMMAND_CATEGORY = "bash";
|
|
36510
|
+
function asStringArray(value) {
|
|
36511
|
+
if (!Array.isArray(value)) return [];
|
|
36512
|
+
return value.filter((entry) => typeof entry === "string");
|
|
36513
|
+
}
|
|
36514
|
+
/**
|
|
36515
|
+
* Split one canonical category's rules into Zoo Code's two command lists.
|
|
36516
|
+
*
|
|
36517
|
+
* Zoo Code matches these entries as command **prefixes**: a command runs
|
|
36518
|
+
* without a confirmation prompt when it starts with an `allowedCommands` entry,
|
|
36519
|
+
* and is refused outright when it starts with a `deniedCommands` entry (deny
|
|
36520
|
+
* wins). `ask` is represented by listing the pattern in neither list, which
|
|
36521
|
+
* leaves Zoo Code's default approval prompt in charge.
|
|
36522
|
+
*
|
|
36523
|
+
* Each list is `undefined` when it would be empty, so the key is retracted from
|
|
36524
|
+
* the settings file rather than written as an empty array — an empty
|
|
36525
|
+
* `allowedCommands` and an absent one mean the same thing to Zoo Code, and the
|
|
36526
|
+
* absent form leaves no rulesync residue behind.
|
|
36527
|
+
*/
|
|
36528
|
+
function buildCommandLists(rules) {
|
|
36529
|
+
const allowed = [];
|
|
36530
|
+
const denied = [];
|
|
36531
|
+
for (const [pattern, action] of Object.entries(rules)) if (action === "allow") allowed.push(pattern);
|
|
36532
|
+
else if (action === "deny") denied.push(pattern);
|
|
36533
|
+
return {
|
|
36534
|
+
allowed: allowed.length > 0 ? allowed : void 0,
|
|
36535
|
+
denied: denied.length > 0 ? denied : void 0
|
|
36536
|
+
};
|
|
36537
|
+
}
|
|
36538
|
+
/**
|
|
36539
|
+
* Permissions generator for Zoo Code.
|
|
36540
|
+
*
|
|
36541
|
+
* Zoo Code has no policy file in its `.roo/` tree: the committable command
|
|
36542
|
+
* allow/deny lists are VS Code workspace settings
|
|
36543
|
+
* (`zoo-code.allowedCommands` / `zoo-code.deniedCommands` in
|
|
36544
|
+
* `.vscode/settings.json`), which `ClineProvider.mergeCommandLists()` unions
|
|
36545
|
+
* into the lists the auto-approval decision reads. That file is a
|
|
36546
|
+
* general-purpose workspace settings file with many unrelated keys, so reads
|
|
36547
|
+
* and writes merge into the existing JSONC (touching only the two managed keys)
|
|
36548
|
+
* and the file is never deleted.
|
|
36549
|
+
*
|
|
36550
|
+
* Only project scope is modeled: VS Code's user-scope `settings.json` lives at
|
|
36551
|
+
* a platform-dependent path outside rulesync's home-relative global model.
|
|
36552
|
+
*
|
|
36553
|
+
* The `roo` target deliberately does not get this adapter. The settings
|
|
36554
|
+
* namespace is Zoo-era (`roo-cline.*` before the v3.74.0 rebrand), and Roo Code
|
|
36555
|
+
* is EOL with its repository archived, so emitting `zoo-code.*` keys for a
|
|
36556
|
+
* `--targets roo` generate would write settings that Roo itself never reads.
|
|
36557
|
+
*
|
|
36558
|
+
* @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/package.json
|
|
36559
|
+
* @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/core/auto-approval/commands.ts
|
|
36560
|
+
*/
|
|
36561
|
+
var ZoocodePermissions = class ZoocodePermissions extends ToolPermissions {
|
|
36562
|
+
constructor(params) {
|
|
36563
|
+
super({
|
|
36564
|
+
...params,
|
|
36565
|
+
fileContent: params.fileContent ?? "{}"
|
|
36566
|
+
});
|
|
36567
|
+
}
|
|
36568
|
+
/**
|
|
36569
|
+
* `.vscode/settings.json` is a user-managed workspace file with unrelated
|
|
36570
|
+
* settings, so it must not be deleted.
|
|
36571
|
+
*/
|
|
36572
|
+
isDeletable() {
|
|
36573
|
+
return false;
|
|
36574
|
+
}
|
|
36575
|
+
static getSettablePaths(_options = {}) {
|
|
36576
|
+
return {
|
|
36577
|
+
relativeDirPath: ZOOCODE_VSCODE_SETTINGS_DIR,
|
|
36578
|
+
relativeFilePath: ZOOCODE_VSCODE_SETTINGS_FILE_NAME
|
|
36579
|
+
};
|
|
36580
|
+
}
|
|
36581
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true }) {
|
|
36582
|
+
const paths = ZoocodePermissions.getSettablePaths();
|
|
36583
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
|
|
36584
|
+
return new ZoocodePermissions({
|
|
36585
|
+
outputRoot,
|
|
36586
|
+
relativeDirPath: paths.relativeDirPath,
|
|
36587
|
+
relativeFilePath: paths.relativeFilePath,
|
|
36588
|
+
fileContent,
|
|
36589
|
+
validate
|
|
36590
|
+
});
|
|
36591
|
+
}
|
|
36592
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions }) {
|
|
36593
|
+
const paths = ZoocodePermissions.getSettablePaths();
|
|
36594
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
36595
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
36596
|
+
const rules = rulesyncPermissions.getJson().permission[COMMAND_CATEGORY];
|
|
36597
|
+
const patch = {};
|
|
36598
|
+
if (rules !== void 0) {
|
|
36599
|
+
const { allowed, denied } = buildCommandLists(rules);
|
|
36600
|
+
patch[ZOOCODE_ALLOWED_COMMANDS_KEY] = allowed;
|
|
36601
|
+
patch[ZOOCODE_DENIED_COMMANDS_KEY] = denied;
|
|
36602
|
+
}
|
|
36603
|
+
return new ZoocodePermissions({
|
|
36604
|
+
outputRoot,
|
|
36605
|
+
relativeDirPath: paths.relativeDirPath,
|
|
36606
|
+
relativeFilePath: paths.relativeFilePath,
|
|
36607
|
+
fileContent: applySharedConfigPatch({
|
|
36608
|
+
fileKey: sharedConfigFileKey(paths),
|
|
36609
|
+
feature: "permissions",
|
|
36610
|
+
existingContent,
|
|
36611
|
+
patch,
|
|
36612
|
+
filePath
|
|
36613
|
+
}),
|
|
36614
|
+
validate: true
|
|
36615
|
+
});
|
|
36616
|
+
}
|
|
36617
|
+
toRulesyncPermissions() {
|
|
36618
|
+
let settings;
|
|
36619
|
+
try {
|
|
36620
|
+
settings = parseSharedConfig({
|
|
36621
|
+
format: "jsonc",
|
|
36622
|
+
fileContent: this.getFileContent() || "{}",
|
|
36623
|
+
filePath: join(this.getRelativeDirPath(), this.getRelativeFilePath()),
|
|
36624
|
+
invalidRootPolicy: "error",
|
|
36625
|
+
jsoncParseErrors: "error"
|
|
36626
|
+
});
|
|
36627
|
+
} catch (error) {
|
|
36628
|
+
throw new Error(`Failed to parse Zoo Code VS Code settings in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
36629
|
+
}
|
|
36630
|
+
const rules = {};
|
|
36631
|
+
for (const pattern of asStringArray(settings[ZOOCODE_ALLOWED_COMMANDS_KEY])) rules[pattern] = "allow";
|
|
36632
|
+
for (const pattern of asStringArray(settings[ZOOCODE_DENIED_COMMANDS_KEY])) rules[pattern] = "deny";
|
|
36633
|
+
const permission = {};
|
|
36634
|
+
if (Object.keys(rules).length > 0) permission[COMMAND_CATEGORY] = rules;
|
|
36635
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission }, null, 2) });
|
|
36636
|
+
}
|
|
36637
|
+
validate() {
|
|
36638
|
+
return {
|
|
36639
|
+
success: true,
|
|
36640
|
+
error: null
|
|
36641
|
+
};
|
|
36642
|
+
}
|
|
36643
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
36644
|
+
return new ZoocodePermissions({
|
|
36645
|
+
outputRoot,
|
|
36646
|
+
relativeDirPath,
|
|
36647
|
+
relativeFilePath,
|
|
36648
|
+
fileContent: "{}",
|
|
36649
|
+
validate: false
|
|
36650
|
+
});
|
|
36651
|
+
}
|
|
36652
|
+
};
|
|
36653
|
+
//#endregion
|
|
35943
36654
|
//#region src/features/permissions/permissions-processor.ts
|
|
35944
36655
|
const PermissionsProcessorToolTargetSchema = z.enum(permissionsProcessorToolTargetTuple);
|
|
35945
36656
|
const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
@@ -36119,6 +36830,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
36119
36830
|
supportsImport: true
|
|
36120
36831
|
}
|
|
36121
36832
|
}],
|
|
36833
|
+
["pi", {
|
|
36834
|
+
class: PiPermissions,
|
|
36835
|
+
meta: {
|
|
36836
|
+
supportsProject: true,
|
|
36837
|
+
supportsGlobal: true,
|
|
36838
|
+
supportsImport: true
|
|
36839
|
+
}
|
|
36840
|
+
}],
|
|
36122
36841
|
["qwencode", {
|
|
36123
36842
|
class: QwencodePermissions,
|
|
36124
36843
|
meta: {
|
|
@@ -36174,6 +36893,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
36174
36893
|
supportsGlobal: true,
|
|
36175
36894
|
supportsImport: true
|
|
36176
36895
|
}
|
|
36896
|
+
}],
|
|
36897
|
+
["zoocode", {
|
|
36898
|
+
class: ZoocodePermissions,
|
|
36899
|
+
meta: {
|
|
36900
|
+
supportsProject: true,
|
|
36901
|
+
supportsGlobal: false,
|
|
36902
|
+
supportsImport: true
|
|
36903
|
+
}
|
|
36177
36904
|
}]
|
|
36178
36905
|
]);
|
|
36179
36906
|
var PermissionsProcessor = class extends FeatureProcessor {
|
|
@@ -38430,6 +39157,7 @@ const CursorSkillFrontmatterSchema = z.looseObject({
|
|
|
38430
39157
|
description: z.string(),
|
|
38431
39158
|
paths: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
38432
39159
|
"disable-model-invocation": z.optional(z.boolean()),
|
|
39160
|
+
"user-invocable": z.optional(z.boolean()),
|
|
38433
39161
|
metadata: z.optional(z.looseObject({}))
|
|
38434
39162
|
});
|
|
38435
39163
|
/**
|
|
@@ -38484,6 +39212,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
|
|
|
38484
39212
|
const cursorSection = {
|
|
38485
39213
|
...frontmatter.paths !== void 0 && { paths: frontmatter.paths },
|
|
38486
39214
|
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
|
|
39215
|
+
...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] },
|
|
38487
39216
|
...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata }
|
|
38488
39217
|
};
|
|
38489
39218
|
const rulesyncFrontmatter = {
|
|
@@ -38511,11 +39240,16 @@ var CursorSkill = class CursorSkill extends ToolSkill {
|
|
|
38511
39240
|
rootFrontmatter: rulesyncFrontmatter,
|
|
38512
39241
|
section: cursorSection
|
|
38513
39242
|
});
|
|
39243
|
+
const resolvedUserInvocable = resolveUserInvocable({
|
|
39244
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
39245
|
+
section: cursorSection
|
|
39246
|
+
});
|
|
38514
39247
|
const cursorFrontmatter = {
|
|
38515
39248
|
name: rulesyncFrontmatter.name,
|
|
38516
39249
|
description: rulesyncFrontmatter.description,
|
|
38517
39250
|
...cursorSection?.paths !== void 0 && { paths: cursorSection.paths },
|
|
38518
39251
|
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation },
|
|
39252
|
+
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
38519
39253
|
...cursorSection?.metadata !== void 0 && { metadata: cursorSection.metadata }
|
|
38520
39254
|
};
|
|
38521
39255
|
return new CursorSkill({
|
|
@@ -46518,6 +47252,23 @@ var TaktSubagent = class TaktSubagent extends ToolSubagent {
|
|
|
46518
47252
|
};
|
|
46519
47253
|
//#endregion
|
|
46520
47254
|
//#region src/features/subagents/vibe-subagent.ts
|
|
47255
|
+
/**
|
|
47256
|
+
* Where Vibe reads a custom system prompt from: `.vibe/prompts/<id>.md`
|
|
47257
|
+
* (project) and `~/.vibe/prompts/<id>.md` (user), with the custom directories
|
|
47258
|
+
* winning over the builtins. The id must be a bare file name with no path
|
|
47259
|
+
* separators.
|
|
47260
|
+
* @see vibe/core/prompts/__init__.py
|
|
47261
|
+
*/
|
|
47262
|
+
const VIBE_PROMPTS_DIR_PATH = join(".vibe", "prompts");
|
|
47263
|
+
/**
|
|
47264
|
+
* Vibe resolves a `system_prompt_id` by file name, and
|
|
47265
|
+
* `VibeConfigSchema._check_system_prompt` evaluates it during validation — an
|
|
47266
|
+
* unresolvable id raises and the agent is dropped at discovery. The id must
|
|
47267
|
+
* therefore stay a bare slug with no path separators.
|
|
47268
|
+
*/
|
|
47269
|
+
function promptSlug(relativeFilePath) {
|
|
47270
|
+
return basename(relativeFilePath).replace(/\.(md|toml)$/, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
47271
|
+
}
|
|
46521
47272
|
const VibeSubagentTomlSchema = z.looseObject({
|
|
46522
47273
|
agent_type: z.enum(["agent", "subagent"]),
|
|
46523
47274
|
display_name: z.optional(z.string()),
|
|
@@ -46534,14 +47285,22 @@ const VibeSubagentTomlSchema = z.looseObject({
|
|
|
46534
47285
|
});
|
|
46535
47286
|
var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
46536
47287
|
body;
|
|
46537
|
-
|
|
46538
|
-
|
|
47288
|
+
promptFile;
|
|
47289
|
+
resolvedSystemPrompt;
|
|
47290
|
+
constructor({ body, promptFile = false, resolvedSystemPrompt, ...rest }) {
|
|
47291
|
+
if (rest.validate !== false && !promptFile) try {
|
|
46539
47292
|
VibeSubagentTomlSchema.parse(smolToml.parse(body));
|
|
46540
47293
|
} catch (error) {
|
|
46541
47294
|
throw new Error(`Invalid TOML in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(error)}`, { cause: error });
|
|
46542
47295
|
}
|
|
46543
47296
|
super({ ...rest });
|
|
46544
47297
|
this.body = body;
|
|
47298
|
+
this.promptFile = promptFile;
|
|
47299
|
+
this.resolvedSystemPrompt = resolvedSystemPrompt;
|
|
47300
|
+
}
|
|
47301
|
+
/** True for the companion `.vibe/prompts/<id>.md` file. */
|
|
47302
|
+
isPromptFile() {
|
|
47303
|
+
return this.promptFile;
|
|
46545
47304
|
}
|
|
46546
47305
|
static getSettablePaths(_options = {}) {
|
|
46547
47306
|
return { relativeDirPath: join(".vibe", "agents") };
|
|
@@ -46556,7 +47315,9 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46556
47315
|
} catch (error) {
|
|
46557
47316
|
throw new Error(`Failed to parse TOML in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
46558
47317
|
}
|
|
46559
|
-
const { system_prompt, description, display_name, ...vibeSection } = parsed;
|
|
47318
|
+
const { system_prompt, system_prompt_id, description, display_name, ...vibeSection } = parsed;
|
|
47319
|
+
const resolvedBody = this.resolvedSystemPrompt ?? system_prompt;
|
|
47320
|
+
const unresolvedPromptId = this.resolvedSystemPrompt === void 0 ? system_prompt_id : void 0;
|
|
46560
47321
|
const fileStem = basename(this.getRelativeFilePath(), ".toml");
|
|
46561
47322
|
const rulesyncFrontmatter = {
|
|
46562
47323
|
targets: ["vibe"],
|
|
@@ -46565,13 +47326,14 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46565
47326
|
vibe: {
|
|
46566
47327
|
...display_name !== void 0 && { display_name },
|
|
46567
47328
|
...description !== void 0 && { description },
|
|
47329
|
+
...unresolvedPromptId !== void 0 && { system_prompt_id: unresolvedPromptId },
|
|
46568
47330
|
...vibeSection
|
|
46569
47331
|
}
|
|
46570
47332
|
};
|
|
46571
47333
|
return new RulesyncSubagent({
|
|
46572
47334
|
outputRoot: this.outputRoot,
|
|
46573
47335
|
frontmatter: rulesyncFrontmatter,
|
|
46574
|
-
body:
|
|
47336
|
+
body: resolvedBody ?? "",
|
|
46575
47337
|
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
46576
47338
|
relativeFilePath: this.getRelativeFilePath().replace(/\.toml$/, ".md"),
|
|
46577
47339
|
validate: true
|
|
@@ -46586,12 +47348,14 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46586
47348
|
"description",
|
|
46587
47349
|
"system_prompt"
|
|
46588
47350
|
]);
|
|
47351
|
+
const promptBody = rulesyncSubagent.getBody();
|
|
47352
|
+
const slug = promptSlug(rulesyncSubagent.getRelativeFilePath());
|
|
46589
47353
|
const tomlObj = {
|
|
46590
47354
|
agent_type: rawSection.agent_type === "agent" ? "agent" : "subagent",
|
|
46591
47355
|
display_name: typeof rawSection.display_name === "string" ? rawSection.display_name : frontmatter.name,
|
|
46592
47356
|
...frontmatter.description ? { description: frontmatter.description } : {},
|
|
46593
|
-
...
|
|
46594
|
-
...
|
|
47357
|
+
...vibeSection,
|
|
47358
|
+
...promptBody ? { system_prompt_id: slug } : {}
|
|
46595
47359
|
};
|
|
46596
47360
|
const body = smolToml.stringify(tomlObj);
|
|
46597
47361
|
const paths = this.getSettablePaths({ global });
|
|
@@ -46606,6 +47370,36 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46606
47370
|
global
|
|
46607
47371
|
});
|
|
46608
47372
|
}
|
|
47373
|
+
/**
|
|
47374
|
+
* Emits the agent TOML plus its companion prompt file. The prompt file MUST
|
|
47375
|
+
* be written together with the id: `VibeConfigSchema._check_system_prompt`
|
|
47376
|
+
* evaluates the property during validation, so an unresolvable
|
|
47377
|
+
* `system_prompt_id` raises and `AgentRegistry._try_load` drops the agent
|
|
47378
|
+
* with a warning.
|
|
47379
|
+
*/
|
|
47380
|
+
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false }) {
|
|
47381
|
+
return rulesyncSubagents.flatMap((rulesyncSubagent) => {
|
|
47382
|
+
const agentFile = VibeSubagent.fromRulesyncSubagent({
|
|
47383
|
+
outputRoot,
|
|
47384
|
+
relativeDirPath: VibeSubagent.getSettablePaths({ global }).relativeDirPath,
|
|
47385
|
+
rulesyncSubagent,
|
|
47386
|
+
global
|
|
47387
|
+
});
|
|
47388
|
+
const promptBody = rulesyncSubagent.getBody();
|
|
47389
|
+
if (!promptBody) return [agentFile];
|
|
47390
|
+
const slug = promptSlug(rulesyncSubagent.getRelativeFilePath());
|
|
47391
|
+
return [agentFile, new VibeSubagent({
|
|
47392
|
+
outputRoot,
|
|
47393
|
+
body: promptBody,
|
|
47394
|
+
relativeDirPath: VIBE_PROMPTS_DIR_PATH,
|
|
47395
|
+
relativeFilePath: `${slug}.md`,
|
|
47396
|
+
fileContent: promptBody,
|
|
47397
|
+
promptFile: true,
|
|
47398
|
+
validate: false,
|
|
47399
|
+
global
|
|
47400
|
+
})];
|
|
47401
|
+
});
|
|
47402
|
+
}
|
|
46609
47403
|
validate() {
|
|
46610
47404
|
try {
|
|
46611
47405
|
VibeSubagentTomlSchema.parse(smolToml.parse(this.body));
|
|
@@ -46630,6 +47424,10 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46630
47424
|
const paths = this.getSettablePaths({ global });
|
|
46631
47425
|
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
46632
47426
|
const fileContent = await readFileContent(filePath);
|
|
47427
|
+
const resolvedSystemPrompt = await VibeSubagent.readSystemPrompt({
|
|
47428
|
+
outputRoot,
|
|
47429
|
+
fileContent
|
|
47430
|
+
});
|
|
46633
47431
|
const subagent = new VibeSubagent({
|
|
46634
47432
|
outputRoot,
|
|
46635
47433
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -46637,7 +47435,8 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46637
47435
|
body: fileContent.trim(),
|
|
46638
47436
|
fileContent,
|
|
46639
47437
|
validate,
|
|
46640
|
-
global
|
|
47438
|
+
global,
|
|
47439
|
+
...resolvedSystemPrompt !== void 0 && { resolvedSystemPrompt }
|
|
46641
47440
|
});
|
|
46642
47441
|
if (validate) {
|
|
46643
47442
|
const result = subagent.validate();
|
|
@@ -46645,6 +47444,21 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46645
47444
|
}
|
|
46646
47445
|
return subagent;
|
|
46647
47446
|
}
|
|
47447
|
+
/**
|
|
47448
|
+
* Read `.vibe/prompts/<id>.md` for an agent that names a `system_prompt_id`.
|
|
47449
|
+
* Returns `undefined` when there is no id or the file is missing, in which
|
|
47450
|
+
* case import falls back to the legacy `system_prompt` key.
|
|
47451
|
+
*/
|
|
47452
|
+
static async readSystemPrompt({ outputRoot, fileContent }) {
|
|
47453
|
+
let promptId;
|
|
47454
|
+
try {
|
|
47455
|
+
promptId = VibeSubagentTomlSchema.parse(smolToml.parse(fileContent)).system_prompt_id;
|
|
47456
|
+
} catch {
|
|
47457
|
+
return;
|
|
47458
|
+
}
|
|
47459
|
+
if (typeof promptId !== "string" || promptId === "" || /[\\/]/.test(promptId)) return;
|
|
47460
|
+
return await readFileContentOrNull(join(outputRoot, VIBE_PROMPTS_DIR_PATH, `${promptId}.md`)) ?? void 0;
|
|
47461
|
+
}
|
|
46648
47462
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
46649
47463
|
return new VibeSubagent({
|
|
46650
47464
|
outputRoot,
|
|
@@ -46687,7 +47501,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
46687
47501
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
46688
47502
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
46689
47503
|
const zoocodeSection = isRecord$1(frontmatter.zoocode) ? frontmatter.zoocode : {};
|
|
46690
|
-
if (isStringArray$
|
|
47504
|
+
if (isStringArray$2(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
|
|
46691
47505
|
return mode;
|
|
46692
47506
|
}
|
|
46693
47507
|
toRulesyncSubagents() {
|
|
@@ -46699,7 +47513,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
46699
47513
|
targets: ["zoocode"],
|
|
46700
47514
|
roo: restRooSection
|
|
46701
47515
|
};
|
|
46702
|
-
if (isStringArray$
|
|
47516
|
+
if (isStringArray$2(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
|
|
46703
47517
|
return new RulesyncSubagent({
|
|
46704
47518
|
outputRoot: ".",
|
|
46705
47519
|
frontmatter: rebuilt,
|
|
@@ -49602,8 +50416,13 @@ const STRATEGIES = [
|
|
|
49602
50416
|
* file per rule under `.devin/rules/*.md` (Devin Desktop Cascade) with YAML
|
|
49603
50417
|
* frontmatter carrying a `trigger` (always_on | glob | manual | model_decision)
|
|
49604
50418
|
* plus companion `globs`/`description` fields.
|
|
49605
|
-
* - Global scope:
|
|
49606
|
-
*
|
|
50419
|
+
* - Global scope: mirrors the project layout. The root rule is a single
|
|
50420
|
+
* plain-markdown, always-on file (no frontmatter) at
|
|
50421
|
+
* `~/.config/devin/AGENTS.md`; non-root rules are one file per rule under
|
|
50422
|
+
* `~/.devin/rules/*.md` with the same `trigger`/`globs` frontmatter as project
|
|
50423
|
+
* scope. Note the directory split — the per-rule global directory is the home
|
|
50424
|
+
* `~/.devin/`, not the `~/.config/devin/` tree every other Devin global
|
|
50425
|
+
* surface uses. That is what the rules page documents.
|
|
49607
50426
|
*
|
|
49608
50427
|
* Trigger inference (when no explicit devin trigger is persisted):
|
|
49609
50428
|
* - Specific globs (non wildcard) → glob
|
|
@@ -49634,19 +50453,32 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49634
50453
|
relativeFilePath: DEVIN_GLOBAL_AGENTS_FILE_NAME
|
|
49635
50454
|
};
|
|
49636
50455
|
}
|
|
50456
|
+
/**
|
|
50457
|
+
* The always-on root rule file for a scope: the project-root `AGENTS.md`
|
|
50458
|
+
* Devin CLI / Devin Local reads, or `~/.config/devin/AGENTS.md` in global
|
|
50459
|
+
* scope. Single-sourced so `getSettablePaths`, `fromFile` and
|
|
50460
|
+
* `fromRulesyncRule` cannot disagree about it.
|
|
50461
|
+
*/
|
|
50462
|
+
static getRootPath(global, excludeToolDir) {
|
|
50463
|
+
return global ? DevinRule.getGlobalRootPath(excludeToolDir) : {
|
|
50464
|
+
relativeDirPath: ".",
|
|
50465
|
+
relativeFilePath: "AGENTS.md"
|
|
50466
|
+
};
|
|
50467
|
+
}
|
|
49637
50468
|
static getSettablePaths({ global = false, excludeToolDir } = {}) {
|
|
49638
|
-
if (global) return {
|
|
50469
|
+
if (global) return {
|
|
50470
|
+
root: DevinRule.getRootPath(true, excludeToolDir),
|
|
50471
|
+
nonRoot: { relativeDirPath: buildToolPath(DEVIN_DIR, "rules", excludeToolDir) }
|
|
50472
|
+
};
|
|
49639
50473
|
return {
|
|
49640
|
-
root:
|
|
49641
|
-
relativeDirPath: ".",
|
|
49642
|
-
relativeFilePath: "AGENTS.md"
|
|
49643
|
-
},
|
|
50474
|
+
root: DevinRule.getRootPath(false, excludeToolDir),
|
|
49644
50475
|
nonRoot: { relativeDirPath: buildToolPath(DEVIN_DIR, "rules", excludeToolDir) }
|
|
49645
50476
|
};
|
|
49646
50477
|
}
|
|
49647
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
49648
|
-
|
|
49649
|
-
|
|
50478
|
+
static async fromFile({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, validate = true, global = false }) {
|
|
50479
|
+
const nonRootDirPath = buildToolPath(DEVIN_DIR, "rules");
|
|
50480
|
+
if (relativeDirPath !== void 0 ? relativeDirPath !== nonRootDirPath : relativeFilePath === DevinRule.getRootPath(global).relativeFilePath) {
|
|
50481
|
+
const rootPath = DevinRule.getRootPath(global);
|
|
49650
50482
|
const fileContent = await readFileContent(join(outputRoot, rootPath.relativeDirPath, rootPath.relativeFilePath));
|
|
49651
50483
|
return new DevinRule({
|
|
49652
50484
|
outputRoot,
|
|
@@ -49658,19 +50490,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49658
50490
|
root: true
|
|
49659
50491
|
});
|
|
49660
50492
|
}
|
|
49661
|
-
if (relativeFilePath === "AGENTS.md") {
|
|
49662
|
-
const fileContent = await readFileContent(join(outputRoot, relativeFilePath));
|
|
49663
|
-
return new DevinRule({
|
|
49664
|
-
outputRoot,
|
|
49665
|
-
relativeDirPath: ".",
|
|
49666
|
-
relativeFilePath: "AGENTS.md",
|
|
49667
|
-
frontmatter: {},
|
|
49668
|
-
body: fileContent,
|
|
49669
|
-
validate,
|
|
49670
|
-
root: true
|
|
49671
|
-
});
|
|
49672
|
-
}
|
|
49673
|
-
const nonRootDirPath = buildToolPath(DEVIN_DIR, "rules");
|
|
49674
50493
|
const filePath = join(outputRoot, nonRootDirPath, relativeFilePath);
|
|
49675
50494
|
const { frontmatter, body } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
49676
50495
|
let parsedFrontmatter;
|
|
@@ -49690,8 +50509,9 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49690
50509
|
});
|
|
49691
50510
|
}
|
|
49692
50511
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
49693
|
-
|
|
49694
|
-
|
|
50512
|
+
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
50513
|
+
if (rulesyncFrontmatter.root) {
|
|
50514
|
+
const rootPath = DevinRule.getRootPath(global);
|
|
49695
50515
|
return new DevinRule({
|
|
49696
50516
|
outputRoot,
|
|
49697
50517
|
relativeDirPath: rootPath.relativeDirPath,
|
|
@@ -49702,16 +50522,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49702
50522
|
root: true
|
|
49703
50523
|
});
|
|
49704
50524
|
}
|
|
49705
|
-
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
49706
|
-
if (rulesyncFrontmatter.root) return new DevinRule({
|
|
49707
|
-
outputRoot,
|
|
49708
|
-
relativeDirPath: ".",
|
|
49709
|
-
relativeFilePath: "AGENTS.md",
|
|
49710
|
-
frontmatter: {},
|
|
49711
|
-
body: rulesyncRule.getBody(),
|
|
49712
|
-
validate,
|
|
49713
|
-
root: true
|
|
49714
|
-
});
|
|
49715
50525
|
const storedDevin = rulesyncFrontmatter.devin;
|
|
49716
50526
|
const normalized = normalizeStoredDevin(storedDevin);
|
|
49717
50527
|
const storedTrigger = storedDevin?.trigger;
|
|
@@ -49771,7 +50581,7 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49771
50581
|
error: null
|
|
49772
50582
|
};
|
|
49773
50583
|
}
|
|
49774
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath
|
|
50584
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
49775
50585
|
return new DevinRule({
|
|
49776
50586
|
outputRoot,
|
|
49777
50587
|
relativeDirPath,
|
|
@@ -49779,7 +50589,7 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49779
50589
|
frontmatter: {},
|
|
49780
50590
|
body: "",
|
|
49781
50591
|
validate: false,
|
|
49782
|
-
root:
|
|
50592
|
+
root: relativeDirPath !== buildToolPath(DEVIN_DIR, "rules")
|
|
49783
50593
|
});
|
|
49784
50594
|
}
|
|
49785
50595
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
@@ -50412,6 +51222,11 @@ var KimiCodeRule = class KimiCodeRule extends ToolRule {
|
|
|
50412
51222
|
};
|
|
50413
51223
|
//#endregion
|
|
50414
51224
|
//#region src/features/rules/kiro-rule.ts
|
|
51225
|
+
/**
|
|
51226
|
+
* Kiro loads `AGENTS.md` as steering context from any directory in the
|
|
51227
|
+
* workspace tree (CLI 2.18.0 / IDE 1.0.309), not just the workspace root.
|
|
51228
|
+
*/
|
|
51229
|
+
const KIRO_NESTED_STEERING_FILE_NAME = "AGENTS.md";
|
|
50415
51230
|
const WILDCARD_GLOBS = /* @__PURE__ */ new Set([
|
|
50416
51231
|
"**/*",
|
|
50417
51232
|
"**",
|
|
@@ -50485,8 +51300,17 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50485
51300
|
};
|
|
50486
51301
|
return { nonRoot: { relativeDirPath: steeringDirPath } };
|
|
50487
51302
|
}
|
|
50488
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
51303
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
50489
51304
|
const paths = this.getSettablePaths({ global });
|
|
51305
|
+
const steeringDirPath = paths.nonRoot?.relativeDirPath ?? buildToolPath(".kiro", "steering");
|
|
51306
|
+
if (overrideDirPath !== void 0 && overrideDirPath !== steeringDirPath && overrideDirPath !== "." && relativeFilePath === KIRO_NESTED_STEERING_FILE_NAME) return new KiroRule({
|
|
51307
|
+
outputRoot,
|
|
51308
|
+
relativeDirPath: overrideDirPath,
|
|
51309
|
+
relativeFilePath,
|
|
51310
|
+
fileContent: await readFileContent(join(outputRoot, overrideDirPath, relativeFilePath)),
|
|
51311
|
+
validate,
|
|
51312
|
+
root: false
|
|
51313
|
+
});
|
|
50490
51314
|
const isRoot = "root" in paths && relativeFilePath === paths.root.relativeFilePath;
|
|
50491
51315
|
const relativeDirPath = paths.nonRoot?.relativeDirPath ?? buildToolPath(".kiro", "steering");
|
|
50492
51316
|
const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
|
|
@@ -50510,6 +51334,12 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50510
51334
|
});
|
|
50511
51335
|
if (params.root) return new KiroRule(params);
|
|
50512
51336
|
const frontmatter = rulesyncRule.getFrontmatter();
|
|
51337
|
+
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
51338
|
+
if (!global && subprojectPath) return new KiroRule({
|
|
51339
|
+
...params,
|
|
51340
|
+
relativeDirPath: join(subprojectPath),
|
|
51341
|
+
relativeFilePath: KIRO_NESTED_STEERING_FILE_NAME
|
|
51342
|
+
});
|
|
50513
51343
|
const inclusion = deriveKiroInclusion({
|
|
50514
51344
|
kiro: frontmatter.kiro,
|
|
50515
51345
|
globs: frontmatter.globs
|
|
@@ -50520,7 +51350,34 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50520
51350
|
fileContent: stringifyFrontmatter(rulesyncRule.getBody(), inclusion)
|
|
50521
51351
|
});
|
|
50522
51352
|
}
|
|
51353
|
+
/**
|
|
51354
|
+
* The subproject directory a nested `AGENTS.md` scopes, or `undefined` for the
|
|
51355
|
+
* root rule and for the `.kiro/steering/` files.
|
|
51356
|
+
*/
|
|
51357
|
+
getSubprojectPath() {
|
|
51358
|
+
if (this.isRoot() || this.getRelativeFilePath() !== KIRO_NESTED_STEERING_FILE_NAME) return;
|
|
51359
|
+
const relativeDirPath = toPosixPath(this.getRelativeDirPath());
|
|
51360
|
+
if (relativeDirPath === "." || relativeDirPath === "" || relativeDirPath.startsWith(".")) return;
|
|
51361
|
+
return relativeDirPath;
|
|
51362
|
+
}
|
|
50523
51363
|
toRulesyncRule() {
|
|
51364
|
+
const subprojectPath = this.getSubprojectPath();
|
|
51365
|
+
if (subprojectPath !== void 0) {
|
|
51366
|
+
const slug = subprojectPath.replaceAll("/", "-");
|
|
51367
|
+
return new RulesyncRule({
|
|
51368
|
+
outputRoot: this.getOutputRoot(),
|
|
51369
|
+
relativeDirPath: RulesyncRule.getSettablePaths().recommended.relativeDirPath,
|
|
51370
|
+
relativeFilePath: `${slug}-kiro.md`,
|
|
51371
|
+
frontmatter: {
|
|
51372
|
+
targets: ["kiro"],
|
|
51373
|
+
root: false,
|
|
51374
|
+
globs: [`${subprojectPath}/**/*`],
|
|
51375
|
+
agentsmd: { subprojectPath }
|
|
51376
|
+
},
|
|
51377
|
+
body: this.getFileContent(),
|
|
51378
|
+
validate: true
|
|
51379
|
+
});
|
|
51380
|
+
}
|
|
50524
51381
|
const { frontmatter, body } = parseFrontmatter(this.getFileContent(), this.getRelativeFilePath());
|
|
50525
51382
|
const inclusion = typeof frontmatter.inclusion === "string" ? frontmatter.inclusion : void 0;
|
|
50526
51383
|
if (!inclusion) return this.toRulesyncRuleDefault();
|
|
@@ -50553,6 +51410,26 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50553
51410
|
error: null
|
|
50554
51411
|
};
|
|
50555
51412
|
}
|
|
51413
|
+
/**
|
|
51414
|
+
* Nested `AGENTS.md` files anywhere in the workspace tree, which Kiro loads as
|
|
51415
|
+
* steering context alongside `.kiro/steering/`. Import-only, like every other
|
|
51416
|
+
* nested scan: these are hand-authored files outside a rulesync-owned
|
|
51417
|
+
* directory, so enumerating them for `--delete` would sweep away work rulesync
|
|
51418
|
+
* never wrote.
|
|
51419
|
+
* @see https://kiro.dev/docs/steering/
|
|
51420
|
+
*/
|
|
51421
|
+
static getNestedFilePatterns({ outputRoot }) {
|
|
51422
|
+
const root = toPosixPath(outputRoot);
|
|
51423
|
+
return {
|
|
51424
|
+
include: [`${root}/**/${KIRO_NESTED_STEERING_FILE_NAME}`],
|
|
51425
|
+
ignore: [
|
|
51426
|
+
`${root}/${KIRO_NESTED_STEERING_FILE_NAME}`,
|
|
51427
|
+
`${root}/**/.*/**`,
|
|
51428
|
+
...NESTED_SCAN_EXCLUDED_DIRS_ANY_DEPTH.map((dir) => `${root}/**/${dir}/**`),
|
|
51429
|
+
...NESTED_SCAN_EXCLUDED_ROOT_DIRS.map((dir) => `${root}/${dir}/**`)
|
|
51430
|
+
]
|
|
51431
|
+
};
|
|
51432
|
+
}
|
|
50556
51433
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
50557
51434
|
return new KiroRule({
|
|
50558
51435
|
outputRoot,
|
|
@@ -51403,11 +52280,12 @@ var RooRule = class RooRule extends ToolRule {
|
|
|
51403
52280
|
static getSettablePaths(_options = {}) {
|
|
51404
52281
|
return { nonRoot: { relativeDirPath: buildToolPath(ROO_DIR, "rules", _options.excludeToolDir) } };
|
|
51405
52282
|
}
|
|
51406
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true }) {
|
|
51407
|
-
const
|
|
52283
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true }) {
|
|
52284
|
+
const relativeDirPath = overrideDirPath !== void 0 && RooRule.extractModeFromDirPath(overrideDirPath) !== void 0 ? overrideDirPath : this.getSettablePaths().nonRoot.relativeDirPath;
|
|
52285
|
+
const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
|
|
51408
52286
|
return new RooRule({
|
|
51409
52287
|
outputRoot,
|
|
51410
|
-
relativeDirPath
|
|
52288
|
+
relativeDirPath,
|
|
51411
52289
|
relativeFilePath,
|
|
51412
52290
|
fileContent,
|
|
51413
52291
|
validate,
|
|
@@ -51415,12 +52293,16 @@ var RooRule = class RooRule extends ToolRule {
|
|
|
51415
52293
|
});
|
|
51416
52294
|
}
|
|
51417
52295
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true }) {
|
|
51418
|
-
|
|
52296
|
+
const params = this.buildToolRuleParamsDefault({
|
|
51419
52297
|
outputRoot,
|
|
51420
52298
|
rulesyncRule,
|
|
51421
52299
|
validate,
|
|
51422
52300
|
nonRootPath: this.getSettablePaths().nonRoot
|
|
51423
|
-
})
|
|
52301
|
+
});
|
|
52302
|
+
const mode = rulesyncRule.getFrontmatter().roo?.mode;
|
|
52303
|
+
if (!params.root && mode !== void 0 && mode !== "") if (!ROO_MODE_SLUG_PATTERN.test(mode)) warnWithFallback(void 0, `Ignoring roo.mode "${mode}" on ${rulesyncRule.getRelativeFilePath()}: a mode slug may contain only letters, digits and hyphens. Writing the rule to ${params.relativeDirPath} instead.`);
|
|
52304
|
+
else params.relativeDirPath = join(dirname(params.relativeDirPath), rooModeRulesDirName(mode));
|
|
52305
|
+
return new RooRule(params);
|
|
51424
52306
|
}
|
|
51425
52307
|
/**
|
|
51426
52308
|
* Extract mode slug from file path for mode-specific rules
|
|
@@ -51432,8 +52314,51 @@ var RooRule = class RooRule extends ToolRule {
|
|
|
51432
52314
|
const singleFileMatch = filePath.match(/\.(roo|cline)rules-([a-zA-Z0-9-]+)$/);
|
|
51433
52315
|
if (singleFileMatch) return singleFileMatch[2];
|
|
51434
52316
|
}
|
|
52317
|
+
/**
|
|
52318
|
+
* The mode slug of a `.roo/rules-{mode}/` directory, or `undefined` for the
|
|
52319
|
+
* generic `.roo/rules/` directory and anything else. Path-shaped input is
|
|
52320
|
+
* rejected by the slug pattern, so a crafted directory name cannot be lifted
|
|
52321
|
+
* back into frontmatter.
|
|
52322
|
+
*/
|
|
52323
|
+
static extractModeFromDirPath(relativeDirPath) {
|
|
52324
|
+
for (const segment of toPosixPath(relativeDirPath).split("/")) {
|
|
52325
|
+
if (!segment.startsWith("rules-")) continue;
|
|
52326
|
+
const mode = segment.slice(6);
|
|
52327
|
+
if (ROO_MODE_SLUG_PATTERN.test(mode)) return mode;
|
|
52328
|
+
}
|
|
52329
|
+
}
|
|
51435
52330
|
toRulesyncRule() {
|
|
51436
|
-
|
|
52331
|
+
const mode = RooRule.extractModeFromDirPath(this.getRelativeDirPath());
|
|
52332
|
+
if (mode === void 0) return this.toRulesyncRuleDefault();
|
|
52333
|
+
const baseName = this.getRelativeFilePath().replace(/\.md$/, "");
|
|
52334
|
+
const suffix = `-${mode}`;
|
|
52335
|
+
const importedName = baseName.endsWith(suffix) ? baseName : `${baseName}${suffix}`;
|
|
52336
|
+
return new RulesyncRule({
|
|
52337
|
+
outputRoot: this.getOutputRoot(),
|
|
52338
|
+
relativeDirPath: RulesyncRule.getSettablePaths().recommended.relativeDirPath,
|
|
52339
|
+
relativeFilePath: `${importedName}.md`,
|
|
52340
|
+
frontmatter: {
|
|
52341
|
+
root: false,
|
|
52342
|
+
targets: [this.constructor.getToolTargetName()],
|
|
52343
|
+
description: this.description,
|
|
52344
|
+
globs: this.globs ?? [],
|
|
52345
|
+
roo: { mode }
|
|
52346
|
+
},
|
|
52347
|
+
body: this.getFileContent(),
|
|
52348
|
+
validate: true
|
|
52349
|
+
});
|
|
52350
|
+
}
|
|
52351
|
+
/**
|
|
52352
|
+
* Mode-specific rule directories (`.roo/rules-{mode}/`), which Roo/Zoo Code
|
|
52353
|
+
* load instead of `.roo/rules/` while that mode is active. Import-only: the
|
|
52354
|
+
* generic directory is the only one the deletion sweep enumerates, because a
|
|
52355
|
+
* `rules-*` glob would also match mode rules a user wrote by hand.
|
|
52356
|
+
*/
|
|
52357
|
+
static getNestedFilePatterns({ outputRoot }) {
|
|
52358
|
+
return {
|
|
52359
|
+
include: [`${toPosixPath(outputRoot)}/${toPosixPath(ROO_DIR)}/rules-*/**/*.md`],
|
|
52360
|
+
ignore: []
|
|
52361
|
+
};
|
|
51437
52362
|
}
|
|
51438
52363
|
validate() {
|
|
51439
52364
|
return {
|
|
@@ -51454,10 +52379,18 @@ var RooRule = class RooRule extends ToolRule {
|
|
|
51454
52379
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
51455
52380
|
return this.isTargetedByRulesyncRuleDefault({
|
|
51456
52381
|
rulesyncRule,
|
|
51457
|
-
toolTarget:
|
|
52382
|
+
toolTarget: this.getToolTargetName()
|
|
51458
52383
|
});
|
|
51459
52384
|
}
|
|
51460
52385
|
/**
|
|
52386
|
+
* The tool target this class imports as. `ZoocodeRule` narrows the same
|
|
52387
|
+
* `.roo/` adapters to the `zoocode` target, so the name has to come from the
|
|
52388
|
+
* class rather than a literal.
|
|
52389
|
+
*/
|
|
52390
|
+
static getToolTargetName() {
|
|
52391
|
+
return "roo";
|
|
52392
|
+
}
|
|
52393
|
+
/**
|
|
51461
52394
|
* Glob for the `separate-local-file` deletion; Roo reads `AGENTS.local.md`
|
|
51462
52395
|
* at the project root, not under `.roo/` (mirrors rovodev).
|
|
51463
52396
|
*/
|
|
@@ -51990,11 +52923,8 @@ var ZedRule = class ZedRule extends ToolRule {
|
|
|
51990
52923
|
* @see https://docs.zoocode.dev
|
|
51991
52924
|
*/
|
|
51992
52925
|
var ZoocodeRule = class extends RooRule {
|
|
51993
|
-
static
|
|
51994
|
-
return
|
|
51995
|
-
rulesyncRule,
|
|
51996
|
-
toolTarget: "zoocode"
|
|
51997
|
-
});
|
|
52926
|
+
static getToolTargetName() {
|
|
52927
|
+
return "zoocode";
|
|
51998
52928
|
}
|
|
51999
52929
|
};
|
|
52000
52930
|
//#endregion
|
|
@@ -52021,6 +52951,18 @@ const resolveIncludeLocalRoot = (options) => {
|
|
|
52021
52951
|
return parsed.data.includeLocalRoot ?? true;
|
|
52022
52952
|
};
|
|
52023
52953
|
/**
|
|
52954
|
+
* Whether `candidate` is `base` itself or a class that extends it.
|
|
52955
|
+
*
|
|
52956
|
+
* Class-identity dispatch (`factory.class === Base`) is subclass-blind: a target
|
|
52957
|
+
* whose adapter merely narrows another one — `ZoocodeRule extends RooRule` — fails
|
|
52958
|
+
* a strict `===` and silently falls through. That is how `zoocode` ended up
|
|
52959
|
+
* declaring `localRootMode: "separate-local-file"` while never emitting
|
|
52960
|
+
* `AGENTS.local.md`, in both directions and without a warning.
|
|
52961
|
+
*/
|
|
52962
|
+
function isClassOrSubclassOf({ candidate, base }) {
|
|
52963
|
+
return candidate === base || Object.prototype.isPrototypeOf.call(base, candidate);
|
|
52964
|
+
}
|
|
52965
|
+
/**
|
|
52024
52966
|
* Factory Map mapping tool targets to their rule factories.
|
|
52025
52967
|
* Using Map to preserve insertion order for consistent iteration.
|
|
52026
52968
|
*/
|
|
@@ -52374,7 +53316,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
52374
53316
|
meta: {
|
|
52375
53317
|
extension: "md",
|
|
52376
53318
|
supportsGlobal: true,
|
|
52377
|
-
ruleDiscoveryMode: "auto"
|
|
53319
|
+
ruleDiscoveryMode: "auto",
|
|
53320
|
+
localRootMode: "separate-local-file",
|
|
53321
|
+
localRootFileName: "AGENTS.local.md"
|
|
52378
53322
|
}
|
|
52379
53323
|
}],
|
|
52380
53324
|
["zed", {
|
|
@@ -52661,7 +53605,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52661
53605
|
if (rootRule) rootRule.setFileContent(rootRule.getFileContent() + "\n\n" + localRootBody);
|
|
52662
53606
|
}
|
|
52663
53607
|
buildLocalRootFile({ factory, fileName, body, relativeDirPath, localRoot = false }) {
|
|
52664
|
-
if (
|
|
53608
|
+
if (isClassOrSubclassOf({
|
|
53609
|
+
candidate: factory.class,
|
|
53610
|
+
base: ClaudecodeRule
|
|
53611
|
+
})) {
|
|
52665
53612
|
const paths = ClaudecodeRule.getSettablePaths({ global: this.global });
|
|
52666
53613
|
return new ClaudecodeRule({
|
|
52667
53614
|
outputRoot: this.outputRoot,
|
|
@@ -52674,7 +53621,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52674
53621
|
localRoot
|
|
52675
53622
|
});
|
|
52676
53623
|
}
|
|
52677
|
-
if (
|
|
53624
|
+
if (isClassOrSubclassOf({
|
|
53625
|
+
candidate: factory.class,
|
|
53626
|
+
base: ClaudecodeLegacyRule
|
|
53627
|
+
})) {
|
|
52678
53628
|
const paths = ClaudecodeLegacyRule.getSettablePaths({ global: this.global });
|
|
52679
53629
|
return new ClaudecodeLegacyRule({
|
|
52680
53630
|
outputRoot: this.outputRoot,
|
|
@@ -52686,7 +53636,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52686
53636
|
localRoot
|
|
52687
53637
|
});
|
|
52688
53638
|
}
|
|
52689
|
-
if (
|
|
53639
|
+
if (isClassOrSubclassOf({
|
|
53640
|
+
candidate: factory.class,
|
|
53641
|
+
base: RovodevRule
|
|
53642
|
+
})) return new RovodevRule({
|
|
52690
53643
|
outputRoot: this.outputRoot,
|
|
52691
53644
|
relativeDirPath: relativeDirPath ?? ".",
|
|
52692
53645
|
relativeFilePath: fileName,
|
|
@@ -52695,7 +53648,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52695
53648
|
root: true,
|
|
52696
53649
|
localRoot
|
|
52697
53650
|
});
|
|
52698
|
-
if (
|
|
53651
|
+
if (isClassOrSubclassOf({
|
|
53652
|
+
candidate: factory.class,
|
|
53653
|
+
base: RooRule
|
|
53654
|
+
})) return new RooRule({
|
|
52699
53655
|
outputRoot: this.outputRoot,
|
|
52700
53656
|
relativeDirPath: relativeDirPath ?? ".",
|
|
52701
53657
|
relativeFilePath: fileName,
|
|
@@ -52704,7 +53660,23 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52704
53660
|
root: true,
|
|
52705
53661
|
localRoot
|
|
52706
53662
|
});
|
|
52707
|
-
if (
|
|
53663
|
+
if (isClassOrSubclassOf({
|
|
53664
|
+
candidate: factory.class,
|
|
53665
|
+
base: DevinRule
|
|
53666
|
+
})) return new DevinRule({
|
|
53667
|
+
outputRoot: this.outputRoot,
|
|
53668
|
+
relativeDirPath: relativeDirPath ?? ".",
|
|
53669
|
+
relativeFilePath: fileName,
|
|
53670
|
+
frontmatter: {},
|
|
53671
|
+
body,
|
|
53672
|
+
validate: true,
|
|
53673
|
+
root: true,
|
|
53674
|
+
localRoot
|
|
53675
|
+
});
|
|
53676
|
+
if (isClassOrSubclassOf({
|
|
53677
|
+
candidate: factory.class,
|
|
53678
|
+
base: QwencodeRule
|
|
53679
|
+
})) return new QwencodeRule({
|
|
52708
53680
|
outputRoot: this.outputRoot,
|
|
52709
53681
|
relativeDirPath: relativeDirPath ?? ".qwen",
|
|
52710
53682
|
relativeFilePath: fileName,
|
|
@@ -54921,4 +55893,4 @@ async function importChecksCore(params) {
|
|
|
54921
55893
|
//#endregion
|
|
54922
55894
|
export { JsonLogger as $, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as $t, RulesyncRuleFrontmatterSchema as A, ALL_TOOL_TARGETS_WITH_WILDCARD as At, RulesyncCheck as B, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Bt, CODEXCLI_DIR as C, removeTempDirectory as Ct, RulesyncSkill as D, writeFileBuffer as Dt, RulesyncSubagentFrontmatterSchema as E, toPosixPath as Et, getRulesyncSourceCandidates as F, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as Ft, SKILL_FILE_NAME as G, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Gt, stringifyFrontmatter as H, RULESYNC_HOOKS_FILE_NAME as Ht, resolveRulesyncSourceWritePath as I, RULESYNC_CHECKS_RELATIVE_DIR_PATH as It, ConfigFileSchema as J, RULESYNC_MCP_LEGACY_FILE_NAME as Jt, ConfigResolver as K, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Kt, parseJsonc as L, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Lt, RulesyncMcp as M, ToolTargetSchema as Mt, RulesyncIgnore as N, MAX_FILE_SIZE as Nt, RulesyncSkillFrontmatterSchema as O, writeFileContent as Ot, RulesyncHooks as P, RULESYNC_AIIGNORE_FILE_NAME as Pt, ConsoleLogger as Q, RULESYNC_PERMISSIONS_FILE_NAME as Qt, RulesyncCommand as R, RULESYNC_CONFIG_RELATIVE_FILE_PATH as Rt, CODEXCLI_BASH_RULES_FILE_NAME as S, removeFileStrict as St, RulesyncSubagent as T, runWithDirectoryRollback as Tt, loadYaml as U, RULESYNC_HOOKS_LEGACY_FILE_NAME as Ut, RulesyncCheckFrontmatterSchema as V, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Vt, SHARED_USER_MANAGED_CONFIG_PATHS as W, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Wt, SourceEntrySchema as X, RULESYNC_MCP_SCHEMA_URL as Xt, GITIGNORE_DESTINATION_KEY as Y, RULESYNC_MCP_RELATIVE_FILE_PATH as Yt, findControlCharacter as Z, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Zt, CLAUDECODE_LOCAL_RULE_FILE_NAME as _, readFileContent as _t, convertFromTool as a, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as an, assertTreeContainsNoSymlinks as at, CLAUDECODE_SKILLS_DIR_PATH as b, removeDirectoryStrict as bt, SubagentsProcessor as c, ALL_FEATURES_WITH_WILDCARD as cn, createTempDirectory as ct, IgnoreProcessor as d, fileExists as dt, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as en, fallbackLogger as et, HooksProcessor as f, findFilesByGlobs as ft, CLAUDECODE_DIR as g, listDirectoryFiles as gt, QWENCODE_LOCAL_RULE_FILE_NAME as h, isSymlink as ht, getProcessorRegistryEntry as i, RULESYNC_SKILLS_RELATIVE_DIR_PATH as in, assertDirectoryIfExists as it, RulesyncPermissions as j, PACKAGING_TOOL_TARGETS as jt, RulesyncRule as k, ALL_TOOL_TARGETS as kt, SkillsProcessor as l, DEPRECATED_FEATURE_REPLACEMENTS as ln, directoryExists as lt, QWENCODE_DIR as m, getHomeDirectory as mt, checkRulesyncDirExists as n, RULESYNC_RELATIVE_DIR_PATH as nn, CLIError as nt, isPackagingToolTarget as o, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as on, assertWritablePathInsideRoot as ot, CommandsProcessor as p, getFileSize as pt, CONFLICTING_TARGET_PAIRS as q, RULESYNC_MCP_FILE_NAME as qt, generate as r, RULESYNC_RULES_RELATIVE_DIR_PATH as rn, ErrorCodes as rt, RulesProcessor as s, ALL_FEATURES as sn, checkPathTraversal as st, importFromTool as t, RULESYNC_PERMISSIONS_SCHEMA_URL as tn, warnOnConflictingFlags as tt, McpProcessor as u, formatError as un, ensureDir as ut, CLAUDECODE_MEMORIES_DIR_NAME as v, readFileContentOrNull as vt, getLocalSkillDirNames as w, resolvePath as wt, ChecksProcessor as x, removeFile as xt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as y, removeDirectory as yt, RulesyncCommandFrontmatterSchema as z, RULESYNC_CONFIG_SCHEMA_URL as zt };
|
|
54923
55895
|
|
|
54924
|
-
//# sourceMappingURL=import-
|
|
55896
|
+
//# sourceMappingURL=import-Ds0QeVp6.js.map
|