rulesync 16.12.0 → 16.13.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 +1 -1
- 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-BwdPcfzS.cjs → import-ChnrtxiJ.cjs} +840 -150
- package/dist/{import-zGCKgpdt.js → import-Dswkc7Ub.js} +826 -136
- package/dist/import-Dswkc7Ub.js.map +1 -0
- 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,6 +412,7 @@ const permissionsProcessorToolTargetTuple = [
|
|
|
412
412
|
"kiro-cli",
|
|
413
413
|
"kiro-ide",
|
|
414
414
|
"opencode",
|
|
415
|
+
"pi",
|
|
415
416
|
"qwencode",
|
|
416
417
|
"reasonix",
|
|
417
418
|
"rovodev",
|
|
@@ -1903,7 +1904,7 @@ function isPlainObject$1(value) {
|
|
|
1903
1904
|
/**
|
|
1904
1905
|
* Type guard to check if a value is an array of strings.
|
|
1905
1906
|
*/
|
|
1906
|
-
function isStringArray$
|
|
1907
|
+
function isStringArray$2(value) {
|
|
1907
1908
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1908
1909
|
}
|
|
1909
1910
|
//#endregion
|
|
@@ -2271,6 +2272,7 @@ const HOOK_EVENTS = [
|
|
|
2271
2272
|
"todoCreated",
|
|
2272
2273
|
"todoCompleted",
|
|
2273
2274
|
"stopFailure",
|
|
2275
|
+
"stopCancelled",
|
|
2274
2276
|
"instructionsLoaded",
|
|
2275
2277
|
"userPromptExpansion",
|
|
2276
2278
|
"postToolBatch",
|
|
@@ -2846,6 +2848,7 @@ const GROKCLI_HOOK_EVENTS = [
|
|
|
2846
2848
|
"permissionDenied",
|
|
2847
2849
|
"stop",
|
|
2848
2850
|
"stopFailure",
|
|
2851
|
+
"stopCancelled",
|
|
2849
2852
|
"notification",
|
|
2850
2853
|
"subagentStart",
|
|
2851
2854
|
"subagentStop",
|
|
@@ -2921,16 +2924,10 @@ const KIMI_CODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CAN
|
|
|
2921
2924
|
/**
|
|
2922
2925
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
2923
2926
|
*
|
|
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`.
|
|
2927
|
+
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 37 entries as
|
|
2928
|
+
* of v0.20.2 (`v2026.8.16`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
|
|
2929
|
+
* full list. Only the events with a clean 1:1 canonical equivalent are mapped
|
|
2930
|
+
* here. All other native events round-trip through `hermesagent.hooks`.
|
|
2934
2931
|
* @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
|
|
2935
2932
|
*/
|
|
2936
2933
|
const HERMESAGENT_HOOK_EVENTS = [
|
|
@@ -2943,6 +2940,21 @@ const HERMESAGENT_HOOK_EVENTS = [
|
|
|
2943
2940
|
"subagentStart",
|
|
2944
2941
|
"subagentStop"
|
|
2945
2942
|
];
|
|
2943
|
+
/**
|
|
2944
|
+
* Hermes Agent's native `VALID_HOOKS` set, in upstream source order.
|
|
2945
|
+
*
|
|
2946
|
+
* The authority is `hermes_cli/plugins.py`'s `VALID_HOOKS`, not the docs: at
|
|
2947
|
+
* v0.20.2 `website/docs/user-guide/features/plugins.md` says "26 lifecycle
|
|
2948
|
+
* events" and then lists 30, omitting the five `on_kanban_*` worker/task/dispatch
|
|
2949
|
+
* observers, `gateway_platform_event` and `transform_api_error_classification`.
|
|
2950
|
+
* Shell hooks are gated on the same set.
|
|
2951
|
+
*
|
|
2952
|
+
* Verified against the tag `v2026.8.16` (v0.20.2): `VALID_HOOKS` holds 37
|
|
2953
|
+
* entries, grown from 23 at `v2026.8.3` (v0.20.0). This list holds 36 of them —
|
|
2954
|
+
* see {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
|
|
2955
|
+
*
|
|
2956
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py
|
|
2957
|
+
*/
|
|
2946
2958
|
const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
2947
2959
|
"pre_tool_call",
|
|
2948
2960
|
"post_tool_call",
|
|
@@ -2951,6 +2963,10 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
2951
2963
|
"transform_llm_output",
|
|
2952
2964
|
"pre_llm_call",
|
|
2953
2965
|
"post_llm_call",
|
|
2966
|
+
"on_stream_start",
|
|
2967
|
+
"on_stream_delta",
|
|
2968
|
+
"on_stream_end",
|
|
2969
|
+
"on_interim_message",
|
|
2954
2970
|
"pre_verify",
|
|
2955
2971
|
"pre_api_request",
|
|
2956
2972
|
"post_api_request",
|
|
@@ -2959,16 +2975,39 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
|
|
|
2959
2975
|
"on_session_end",
|
|
2960
2976
|
"on_session_finalize",
|
|
2961
2977
|
"on_session_reset",
|
|
2978
|
+
"on_skill_lifecycle",
|
|
2962
2979
|
"subagent_start",
|
|
2963
2980
|
"subagent_stop",
|
|
2964
2981
|
"pre_gateway_dispatch",
|
|
2965
2982
|
"pre_approval_request",
|
|
2966
2983
|
"post_approval_response",
|
|
2984
|
+
"pre_transcription",
|
|
2967
2985
|
"kanban_task_claimed",
|
|
2968
2986
|
"kanban_task_completed",
|
|
2969
|
-
"kanban_task_blocked"
|
|
2987
|
+
"kanban_task_blocked",
|
|
2988
|
+
"on_kanban_worker_spawned",
|
|
2989
|
+
"on_kanban_worker_exited",
|
|
2990
|
+
"on_kanban_worker_stale_claim",
|
|
2991
|
+
"on_kanban_task_updated",
|
|
2992
|
+
"on_kanban_dispatch_tick",
|
|
2993
|
+
"gateway_platform_event",
|
|
2994
|
+
"pre_command"
|
|
2970
2995
|
];
|
|
2971
2996
|
/**
|
|
2997
|
+
* `VALID_HOOKS` entries a SHELL hook may not register for. `VALID_HOOKS` doubles
|
|
2998
|
+
* as the shell-hook allow-list, but `SHELL_UNSUPPORTED_HOOKS` is subtracted from
|
|
2999
|
+
* it first: shell hooks cannot return these events' directives, so
|
|
3000
|
+
* `_parse_hooks_block` refuses the registration with a warning rather than
|
|
3001
|
+
* accepting it and dropping the return value.
|
|
3002
|
+
*
|
|
3003
|
+
* rulesync only ever writes shell hooks, so these are excluded from
|
|
3004
|
+
* {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
|
|
3005
|
+
*
|
|
3006
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
|
|
3007
|
+
* @see https://github.com/NousResearch/hermes-agent/blob/v2026.8.16/agent/shell_hooks.py — `_parse_hooks_block`
|
|
3008
|
+
*/
|
|
3009
|
+
const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
|
|
3010
|
+
/**
|
|
2972
3011
|
* Map canonical camelCase event names to Hermes Agent's native `VALID_HOOKS`
|
|
2973
3012
|
* snake_case keys under the `hooks:` block of `~/.hermes/config.yaml`.
|
|
2974
3013
|
*/
|
|
@@ -3585,6 +3624,7 @@ const CANONICAL_TO_GROKCLI_EVENT_NAMES = {
|
|
|
3585
3624
|
permissionDenied: "PermissionDenied",
|
|
3586
3625
|
stop: "Stop",
|
|
3587
3626
|
stopFailure: "StopFailure",
|
|
3627
|
+
stopCancelled: "StopCancelled",
|
|
3588
3628
|
notification: "Notification",
|
|
3589
3629
|
subagentStart: "SubagentStart",
|
|
3590
3630
|
subagentStop: "SubagentStop",
|
|
@@ -3859,6 +3899,25 @@ const McpServerSchema = z.looseObject({
|
|
|
3859
3899
|
kiroAutoApprove: z.optional(z.array(z.string())),
|
|
3860
3900
|
kiroAutoBlock: z.optional(z.array(z.string())),
|
|
3861
3901
|
headers: z.optional(z.record(z.string(), z.string())),
|
|
3902
|
+
/**
|
|
3903
|
+
* The canonical per-server tool allowlist.
|
|
3904
|
+
*
|
|
3905
|
+
* **Collision rule for adapters.** Several tools spell an allowlist natively
|
|
3906
|
+
* under a name that also exists here, so a single server entry can carry both
|
|
3907
|
+
* a canonical field and its native counterpart. What the *native* key means
|
|
3908
|
+
* decides the resolution — not which one was written first:
|
|
3909
|
+
*
|
|
3910
|
+
* - Native key is a redundant spelling of the same concept, and both lists are
|
|
3911
|
+
* additive → **merge** them, so a config that already spells the field
|
|
3912
|
+
* natively keeps working (`kiroAutoApprove`/`kiroAutoBlock` in
|
|
3913
|
+
* `kiro-mcp.ts`).
|
|
3914
|
+
* - Native key is the same concept but rulesync owns the generated value, so
|
|
3915
|
+
* keeping both would be ambiguous → the **canonical value wins** and the
|
|
3916
|
+
* native one is dropped with a warning (`tools` in `copilotcli-mcp.ts`).
|
|
3917
|
+
* - Native key of the same name means something else entirely → **refuse the
|
|
3918
|
+
* canonical value** rather than write a shape the tool misreads (`tools` in
|
|
3919
|
+
* `codexcli-mcp.ts`, where Codex reads it as a per-tool approval table).
|
|
3920
|
+
*/
|
|
3862
3921
|
enabledTools: z.optional(z.array(z.string())),
|
|
3863
3922
|
disabledTools: z.optional(z.array(z.string()))
|
|
3864
3923
|
});
|
|
@@ -4701,8 +4760,65 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4701
4760
|
dangerouslyAllowAll: z.optional(z.boolean())
|
|
4702
4761
|
});
|
|
4703
4762
|
/**
|
|
4763
|
+
* Tool-scoped override block for Pi Coding Agent. Pi has no allow/ask/deny rule
|
|
4764
|
+
* surface: the only repository-syncable tool gate is `defaultTools`, the list of
|
|
4765
|
+
* built-in tools enabled at startup (added in v0.84.2). It is an enable-list
|
|
4766
|
+
* rather than a canonical allow/deny rule set, so it is authored here rather
|
|
4767
|
+
* than mapped onto `permission`, mirroring `KimiCodePermissionsOverrideSchema`'s
|
|
4768
|
+
* `tools.enabled` and `VibePermissionsOverrideSchema`'s `enabled_tools`.
|
|
4769
|
+
*
|
|
4770
|
+
* An empty array is meaningful upstream — it starts Pi with no built-in tools
|
|
4771
|
+
* while keeping extension and SDK custom tools — so it is emitted as written.
|
|
4772
|
+
* CLI flags (`--tools`, `--no-tools`, `--no-builtin-tools`, `--exclude-tools`)
|
|
4773
|
+
* outrank the setting, and a project array **replaces** the global one rather
|
|
4774
|
+
* than merging with it.
|
|
4775
|
+
*
|
|
4776
|
+
* @example
|
|
4777
|
+
* { "defaultTools": ["bash", "edit", "write"] }
|
|
4778
|
+
* @see https://pi.dev/docs/latest/settings
|
|
4779
|
+
*/
|
|
4780
|
+
const PiPermissionsOverrideSchema = z.looseObject({
|
|
4781
|
+
permission: z.optional(ToolScopedPermissionSchema),
|
|
4782
|
+
defaultTools: z.optional(z.array(z.string()))
|
|
4783
|
+
});
|
|
4784
|
+
/**
|
|
4785
|
+
* The Antigravity CLI's four documented `toolPermission` autonomy presets.
|
|
4786
|
+
* Exported so the import side of `antigravity-cli-permissions.ts` filters
|
|
4787
|
+
* against exactly the values the enum below accepts — an unrecognized value
|
|
4788
|
+
* carried in verbatim would fail validation of the *whole*
|
|
4789
|
+
* `.rulesync/permissions.jsonc`, not just that key.
|
|
4790
|
+
* @see https://antigravity.google/docs/cli/reference
|
|
4791
|
+
*/
|
|
4792
|
+
const ANTIGRAVITY_CLI_TOOL_PERMISSIONS = [
|
|
4793
|
+
"request-review",
|
|
4794
|
+
"proceed-in-sandbox",
|
|
4795
|
+
"always-proceed",
|
|
4796
|
+
"strict"
|
|
4797
|
+
];
|
|
4798
|
+
/**
|
|
4799
|
+
* The Antigravity CLI's three documented `artifactReviewPolicy` values. Shared
|
|
4800
|
+
* with the import filter for the same reason as
|
|
4801
|
+
* {@link ANTIGRAVITY_CLI_TOOL_PERMISSIONS}.
|
|
4802
|
+
* @see https://antigravity.google/docs/cli/settings
|
|
4803
|
+
*/
|
|
4804
|
+
const ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES = [
|
|
4805
|
+
"asks-for-review",
|
|
4806
|
+
"agent-decides",
|
|
4807
|
+
"always-proceed"
|
|
4808
|
+
];
|
|
4809
|
+
/**
|
|
4810
|
+
* The baseline execution modes the CLI persists as `agentMode`. Shared with the
|
|
4811
|
+
* import filter for the same reason as {@link ANTIGRAVITY_CLI_TOOL_PERMISSIONS}.
|
|
4812
|
+
* @see https://antigravity.google/docs/cli/modes
|
|
4813
|
+
*/
|
|
4814
|
+
const ANTIGRAVITY_CLI_AGENT_MODES = [
|
|
4815
|
+
"default",
|
|
4816
|
+
"accept-edits",
|
|
4817
|
+
"plan"
|
|
4818
|
+
];
|
|
4819
|
+
/**
|
|
4704
4820
|
* Tool-scoped override block for the Google Antigravity CLI. Antigravity's CLI
|
|
4705
|
-
* `settings.json` carries
|
|
4821
|
+
* `settings.json` carries five global autonomy/sandbox knobs outside the
|
|
4706
4822
|
* `permissions.allow/ask/deny` arrays rulesync manages: `toolPermission` (the
|
|
4707
4823
|
* global autonomy preset — `request-review` (default) / `proceed-in-sandbox` /
|
|
4708
4824
|
* `always-proceed` / `strict`), `enableTerminalSandbox` (a boolean confining
|
|
@@ -4710,7 +4826,8 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4710
4826
|
* agent's artifact changes are gated on a review prompt — `asks-for-review`
|
|
4711
4827
|
* (default) / `agent-decides` / `always-proceed`) and `allowNonWorkspaceAccess`
|
|
4712
4828
|
* (a boolean, off by default, letting the agent read or write files outside the
|
|
4713
|
-
* active workspace roots)
|
|
4829
|
+
* active workspace roots), and `agentMode` (the baseline execution mode a session
|
|
4830
|
+
* starts in — `default` / `accept-edits` / `plan`). Antigravity applies the allow/deny
|
|
4714
4831
|
* lists as per-rule exceptions to the preset at runtime, so rulesync only
|
|
4715
4832
|
* authors these keys verbatim — no precedence modeling is needed on our side.
|
|
4716
4833
|
* Fields placed here are merged onto the top level of
|
|
@@ -4718,27 +4835,27 @@ const AmpPermissionsOverrideSchema = z.looseObject({
|
|
|
4718
4835
|
* the CLI. The Antigravity IDE exposes the same concepts through a GUI (no
|
|
4719
4836
|
* documented JSON schema), so this override does NOT apply to `antigravity-ide`.
|
|
4720
4837
|
* Verified against https://antigravity.google/docs/cli/reference,
|
|
4721
|
-
* https://antigravity.google/docs/cli/sandbox
|
|
4722
|
-
* https://antigravity.google/docs/cli/settings
|
|
4838
|
+
* https://antigravity.google/docs/cli/sandbox,
|
|
4839
|
+
* https://antigravity.google/docs/cli/settings and
|
|
4840
|
+
* https://antigravity.google/docs/cli/modes.
|
|
4723
4841
|
*
|
|
4724
4842
|
* @example
|
|
4725
4843
|
* { "toolPermission": "strict", "enableTerminalSandbox": true }
|
|
4726
4844
|
*/
|
|
4727
4845
|
const AntigravityCliPermissionsOverrideSchema = z.looseObject({
|
|
4728
4846
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
4729
|
-
toolPermission: z.optional(z.enum(
|
|
4730
|
-
"request-review",
|
|
4731
|
-
"proceed-in-sandbox",
|
|
4732
|
-
"always-proceed",
|
|
4733
|
-
"strict"
|
|
4734
|
-
])),
|
|
4847
|
+
toolPermission: z.optional(z.enum(ANTIGRAVITY_CLI_TOOL_PERMISSIONS)),
|
|
4735
4848
|
enableTerminalSandbox: z.optional(z.boolean()),
|
|
4736
|
-
artifactReviewPolicy: z.optional(z.enum(
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4849
|
+
artifactReviewPolicy: z.optional(z.enum(ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES)),
|
|
4850
|
+
allowNonWorkspaceAccess: z.optional(z.boolean()),
|
|
4851
|
+
/**
|
|
4852
|
+
* The baseline execution mode the CLI starts a session in (Agent Mode), the
|
|
4853
|
+
* persisted form of the `/settings` panel's Agent Mode row. Only
|
|
4854
|
+
* `~/.gemini/antigravity-cli/settings.json` is read for it, which is why this
|
|
4855
|
+
* override stays global-only.
|
|
4856
|
+
* @see https://antigravity.google/docs/cli/modes
|
|
4857
|
+
*/
|
|
4858
|
+
agentMode: z.optional(z.enum(ANTIGRAVITY_CLI_AGENT_MODES))
|
|
4742
4859
|
});
|
|
4743
4860
|
/**
|
|
4744
4861
|
* Tool-scoped override block for AugmentCode. AugmentCode's `toolPermissions[]`
|
|
@@ -5048,6 +5165,7 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
5048
5165
|
takt: z.optional(TaktPermissionsOverrideSchema),
|
|
5049
5166
|
amp: z.optional(AmpPermissionsOverrideSchema),
|
|
5050
5167
|
"antigravity-cli": z.optional(AntigravityCliPermissionsOverrideSchema),
|
|
5168
|
+
pi: z.optional(PiPermissionsOverrideSchema),
|
|
5051
5169
|
augmentcode: z.optional(AugmentcodePermissionsOverrideSchema),
|
|
5052
5170
|
kiro: z.optional(KiroPermissionsOverrideSchema),
|
|
5053
5171
|
codexcli: z.optional(CodexcliPermissionsOverrideSchema),
|
|
@@ -5529,6 +5647,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
5529
5647
|
cursor: z.optional(z.looseObject({
|
|
5530
5648
|
paths: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
5531
5649
|
"disable-model-invocation": z.optional(z.boolean()),
|
|
5650
|
+
"user-invocable": z.optional(z.boolean()),
|
|
5532
5651
|
metadata: z.optional(z.looseObject({}))
|
|
5533
5652
|
})),
|
|
5534
5653
|
factorydroid: z.optional(z.looseObject({
|
|
@@ -5753,6 +5872,11 @@ async function getLocalSkillDirNames(outputRoot) {
|
|
|
5753
5872
|
* default with a per-target value. A defined section value (including `false`)
|
|
5754
5873
|
* always wins over the root default.
|
|
5755
5874
|
*
|
|
5875
|
+
* `devin` also consumes the root-level value — it maps `true` onto a user-only
|
|
5876
|
+
* `triggers` list (see `devin-skill.ts`) — but has no section key of the same
|
|
5877
|
+
* name, so it does not go through this helper. A `devin.triggers` section value
|
|
5878
|
+
* still overrides it.
|
|
5879
|
+
*
|
|
5756
5880
|
* @returns The resolved boolean, or `undefined` when neither value is set.
|
|
5757
5881
|
*/
|
|
5758
5882
|
function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
@@ -5763,10 +5887,15 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
|
|
|
5763
5887
|
*
|
|
5764
5888
|
* The rulesync skill frontmatter exposes a root-level `user-invocable` default
|
|
5765
5889
|
* 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
|
|
5890
|
+
* copilotcli, cursor, qwencode, vibe, grokcli, factorydroid). Each tool's own section may override that default with a
|
|
5767
5891
|
* per-target value. A defined section value (including `false`) always wins
|
|
5768
5892
|
* over the root default.
|
|
5769
5893
|
*
|
|
5894
|
+
* `devin` also consumes the root-level value — it maps `false` onto a
|
|
5895
|
+
* model-only `triggers` list (see `devin-skill.ts`) — but has no section key of
|
|
5896
|
+
* the same name, so it does not go through this helper. A `devin.triggers`
|
|
5897
|
+
* section value still overrides it.
|
|
5898
|
+
*
|
|
5770
5899
|
* @returns The resolved boolean, or `undefined` when neither value is set.
|
|
5771
5900
|
*/
|
|
5772
5901
|
function resolveUserInvocable({ rootFrontmatter, section }) {
|
|
@@ -7616,6 +7745,22 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
7616
7745
|
}
|
|
7617
7746
|
}
|
|
7618
7747
|
},
|
|
7748
|
+
".pi/settings.json": {
|
|
7749
|
+
format: "json",
|
|
7750
|
+
invalidRootPolicy: "error",
|
|
7751
|
+
features: { permissions: {
|
|
7752
|
+
kind: "replace-owned-keys",
|
|
7753
|
+
ownedKeys: ["defaultTools"]
|
|
7754
|
+
} }
|
|
7755
|
+
},
|
|
7756
|
+
".pi/agent/settings.json": {
|
|
7757
|
+
format: "json",
|
|
7758
|
+
invalidRootPolicy: "error",
|
|
7759
|
+
features: { permissions: {
|
|
7760
|
+
kind: "replace-owned-keys",
|
|
7761
|
+
ownedKeys: ["defaultTools"]
|
|
7762
|
+
} }
|
|
7763
|
+
},
|
|
7619
7764
|
".github/copilot/settings.json": {
|
|
7620
7765
|
format: "json",
|
|
7621
7766
|
invalidRootPolicy: "error",
|
|
@@ -12202,6 +12347,8 @@ const PI_RULE_FILE_NAME = "AGENTS.md";
|
|
|
12202
12347
|
const PI_RULE_OVERRIDE_FILE_NAME = "AGENTS.override.md";
|
|
12203
12348
|
const PI_APPEND_SYSTEM_FILE_NAME = "APPEND_SYSTEM.md";
|
|
12204
12349
|
const PI_HOOKS_FILE_NAME = "rulesync-hooks.ts";
|
|
12350
|
+
const PI_SETTINGS_FILE_NAME = "settings.json";
|
|
12351
|
+
const PI_AGENT_DIR_PATH = PI_AGENT_DIR;
|
|
12205
12352
|
//#endregion
|
|
12206
12353
|
//#region src/features/commands/pi-command.ts
|
|
12207
12354
|
/**
|
|
@@ -14126,7 +14273,7 @@ const isBooleanValue = ({ value }) => typeof value === "boolean";
|
|
|
14126
14273
|
const isFiniteNumber = ({ value }) => Number.isFinite(value);
|
|
14127
14274
|
const isNonEmptyString = (value) => typeof value === "string" && value !== "";
|
|
14128
14275
|
const isEmittableString = ({ value }) => isNonEmptyString(value);
|
|
14129
|
-
const isEmittableArray = ({ value }) => isStringArray(value);
|
|
14276
|
+
const isEmittableArray = ({ value }) => isStringArray$1(value);
|
|
14130
14277
|
const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
|
|
14131
14278
|
const isImportableArray = ({ value }) => isSafeStringArray(value);
|
|
14132
14279
|
/**
|
|
@@ -14435,7 +14582,7 @@ function isStringRecord(value) {
|
|
|
14435
14582
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
14436
14583
|
return Object.values(value).every((v) => typeof v === "string");
|
|
14437
14584
|
}
|
|
14438
|
-
function isStringArray(value) {
|
|
14585
|
+
function isStringArray$1(value) {
|
|
14439
14586
|
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
14440
14587
|
}
|
|
14441
14588
|
/** Compare object values without letting key order decide the answer. */
|
|
@@ -14467,7 +14614,7 @@ function isSafeStringRecord(value) {
|
|
|
14467
14614
|
* skipped wholesale when that read fails.
|
|
14468
14615
|
*/
|
|
14469
14616
|
function isSafeStringArray(value) {
|
|
14470
|
-
return isStringArray(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
14617
|
+
return isStringArray$1(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
|
|
14471
14618
|
}
|
|
14472
14619
|
/**
|
|
14473
14620
|
* A raw string kept only if the canonical field it would land in accepts it,
|
|
@@ -14509,7 +14656,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
|
|
|
14509
14656
|
return {
|
|
14510
14657
|
...url !== void 0 && { url },
|
|
14511
14658
|
...isStringRecord(headers) && { headers },
|
|
14512
|
-
...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
14659
|
+
...isStringArray$1(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
|
|
14513
14660
|
};
|
|
14514
14661
|
}
|
|
14515
14662
|
if (hookType === "mcp_tool") {
|
|
@@ -15492,7 +15639,7 @@ const MANAGED_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_CLINE_EVENT_NAMES
|
|
|
15492
15639
|
function parseManifest(fileContent) {
|
|
15493
15640
|
try {
|
|
15494
15641
|
const parsed = JSON.parse(fileContent);
|
|
15495
|
-
if (!isRecord$1(parsed) || !isStringArray$
|
|
15642
|
+
if (!isRecord$1(parsed) || !isStringArray$2(parsed.events)) return null;
|
|
15496
15643
|
return {
|
|
15497
15644
|
generatedBy: "rulesync",
|
|
15498
15645
|
events: parsed.events.filter((event) => MANAGED_EVENT_NAMES.has(event))
|
|
@@ -17127,6 +17274,7 @@ const HERMESAGENT_MATCHER_EVENTS = /* @__PURE__ */ new Set(["pre_tool_call", "po
|
|
|
17127
17274
|
const HERMESAGENT_FAIL_CLOSED_EVENT = "pre_tool_call";
|
|
17128
17275
|
const HERMESAGENT_CANONICAL_EVENTS = new Set(HERMESAGENT_HOOK_EVENTS);
|
|
17129
17276
|
const HERMESAGENT_NATIVE_EVENTS = new Set(HERMESAGENT_NATIVE_HOOK_EVENTS);
|
|
17277
|
+
const HERMESAGENT_SHELL_UNSUPPORTED_EVENTS = new Set(HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS);
|
|
17130
17278
|
/**
|
|
17131
17279
|
* Whether an entry of the `hooks:` mapping is a hook-event list rather than one
|
|
17132
17280
|
* of its non-event siblings.
|
|
@@ -17215,7 +17363,8 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
|
|
|
17215
17363
|
}
|
|
17216
17364
|
for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
|
|
17217
17365
|
if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
|
|
17218
|
-
if (
|
|
17366
|
+
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.`);
|
|
17367
|
+
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
17368
|
setHermesHookEntries({
|
|
17220
17369
|
result,
|
|
17221
17370
|
event: nativeEvent,
|
|
@@ -22339,7 +22488,7 @@ function isCodexStdioServer(config) {
|
|
|
22339
22488
|
* @see https://learn.chatgpt.com/docs/extend/mcp
|
|
22340
22489
|
*/
|
|
22341
22490
|
function isValidRenamedArray(key, value) {
|
|
22342
|
-
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$
|
|
22491
|
+
return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$2(value);
|
|
22343
22492
|
}
|
|
22344
22493
|
/**
|
|
22345
22494
|
* Translate a server's `oauth` table from the canonical rulesync shape (Claude
|
|
@@ -22859,6 +23008,49 @@ const resolveCopilotcliServerType = (server) => {
|
|
|
22859
23008
|
return isRemoteMcpServer(server) ? "http" : "stdio";
|
|
22860
23009
|
};
|
|
22861
23010
|
/**
|
|
23011
|
+
* Copilot CLI's per-server tool allowlist. `tools` selects which of the
|
|
23012
|
+
* server's tools are exposed — `["*"]` (the default) for all, or a list of
|
|
23013
|
+
* names — so the canonical `enabledTools` is written under that name rather
|
|
23014
|
+
* than passed through as a key the CLI ignores.
|
|
23015
|
+
*
|
|
23016
|
+
* When a server carries both, the canonical `enabledTools` wins and the native
|
|
23017
|
+
* `tools` is dropped with a warning. rulesync owns the generated allowlist, and
|
|
23018
|
+
* the `tools` value most likely present is the upstream default `["*"]` — left
|
|
23019
|
+
* to win, it would silently discard a real allowlist in favour of "expose every
|
|
23020
|
+
* tool", which is the failure this mapping exists to prevent. That is the
|
|
23021
|
+
* "canonical wins" branch of the collision rule documented on `enabledTools` in
|
|
23022
|
+
* `src/types/mcp.ts`; `kiro-mcp.ts` merges instead (its native names are
|
|
23023
|
+
* redundant spellings of additive lists) and `codexcli-mcp.ts` refuses instead
|
|
23024
|
+
* (Codex's same-named key means a different thing).
|
|
23025
|
+
*
|
|
23026
|
+
* `disabledTools` has no counterpart upstream (expressing it would need the
|
|
23027
|
+
* server's full tool list), so the target keeps `supportsDisabledTools: false`
|
|
23028
|
+
* and the processor strips it before it reaches here.
|
|
23029
|
+
*
|
|
23030
|
+
* @see https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers
|
|
23031
|
+
*/
|
|
23032
|
+
function renameEnabledToolsToTools({ server, serverName, logger }) {
|
|
23033
|
+
const { enabledTools, ...rest } = server;
|
|
23034
|
+
if (enabledTools === void 0) return rest;
|
|
23035
|
+
if (rest.tools !== void 0) logger?.warn(`[CopilotcliMcp] MCP server "${serverName}" declares both 'tools' and 'enabledTools'; keeping 'enabledTools' and dropping 'tools'.`);
|
|
23036
|
+
return {
|
|
23037
|
+
...rest,
|
|
23038
|
+
tools: enabledTools
|
|
23039
|
+
};
|
|
23040
|
+
}
|
|
23041
|
+
/**
|
|
23042
|
+
* Copilot CLI's `tools` accepts more than the canonical `enabledTools` shape:
|
|
23043
|
+
* `--tools` documents `*`, a comma-separated list, or `""`, so a hand-written or
|
|
23044
|
+
* CLI-written entry can be a bare string. Canonical `enabledTools` is
|
|
23045
|
+
* `string[]`, and `RulesyncMcp` throws on a schema failure — carrying a string
|
|
23046
|
+
* through would reject the WHOLE imported MCP file rather than one key. Anything
|
|
23047
|
+
* that is not a string array is therefore left under `tools` untouched, matching
|
|
23048
|
+
* `codexcli-mcp.ts`'s `isValidRenamedArray` guard on the same class of rename.
|
|
23049
|
+
*/
|
|
23050
|
+
function isStringArray(value) {
|
|
23051
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
23052
|
+
}
|
|
23053
|
+
/**
|
|
22862
23054
|
* Resolves and sets the transport type for each MCP server config.
|
|
22863
23055
|
* GitHub Copilot CLI requires the "type" field for each server. A server it
|
|
22864
23056
|
* cannot express — one that names no transport, or names one it carries no way
|
|
@@ -22902,7 +23094,11 @@ function addTypeField(mcpServers, logger) {
|
|
|
22902
23094
|
}
|
|
22903
23095
|
const { httpUrl: _httpUrl, ...rest } = parsed;
|
|
22904
23096
|
result[name] = {
|
|
22905
|
-
...
|
|
23097
|
+
...renameEnabledToolsToTools({
|
|
23098
|
+
server: rest,
|
|
23099
|
+
serverName: name,
|
|
23100
|
+
logger
|
|
23101
|
+
}),
|
|
22906
23102
|
type,
|
|
22907
23103
|
url
|
|
22908
23104
|
};
|
|
@@ -22920,7 +23116,11 @@ function addTypeField(mcpServers, logger) {
|
|
|
22920
23116
|
continue;
|
|
22921
23117
|
}
|
|
22922
23118
|
result[name] = {
|
|
22923
|
-
...
|
|
23119
|
+
...renameEnabledToolsToTools({
|
|
23120
|
+
server: local,
|
|
23121
|
+
serverName: name,
|
|
23122
|
+
logger
|
|
23123
|
+
}),
|
|
22924
23124
|
type,
|
|
22925
23125
|
command: head,
|
|
22926
23126
|
...tail.length > 0 && { args: tail }
|
|
@@ -22934,11 +23134,19 @@ function addTypeField(mcpServers, logger) {
|
|
|
22934
23134
|
function removeTypeField(config) {
|
|
22935
23135
|
const result = {};
|
|
22936
23136
|
for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
|
|
22937
|
-
|
|
22938
|
-
|
|
23137
|
+
const { tools, ...withoutTools } = server;
|
|
23138
|
+
const restored = isStringArray(tools) ? {
|
|
23139
|
+
...withoutTools,
|
|
23140
|
+
enabledTools: tools
|
|
23141
|
+
} : {
|
|
23142
|
+
...withoutTools,
|
|
23143
|
+
...tools !== void 0 && { tools }
|
|
23144
|
+
};
|
|
23145
|
+
if (restored.type !== "stdio") {
|
|
23146
|
+
result[name] = restored;
|
|
22939
23147
|
continue;
|
|
22940
23148
|
}
|
|
22941
|
-
const { type: _, ...rest } =
|
|
23149
|
+
const { type: _, ...rest } = restored;
|
|
22942
23150
|
result[name] = rest;
|
|
22943
23151
|
}
|
|
22944
23152
|
return result;
|
|
@@ -23590,11 +23798,11 @@ function applyGooseStdioFields(ext, config) {
|
|
|
23590
23798
|
if (Array.isArray(command)) {
|
|
23591
23799
|
if (typeof command[0] === "string") ext.cmd = command[0];
|
|
23592
23800
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
23593
|
-
const args = isStringArray$
|
|
23801
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
23594
23802
|
if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
|
|
23595
23803
|
} else if (typeof command === "string") {
|
|
23596
23804
|
ext.cmd = command;
|
|
23597
|
-
if (isStringArray$
|
|
23805
|
+
if (isStringArray$2(config.args)) ext.args = config.args;
|
|
23598
23806
|
}
|
|
23599
23807
|
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
23600
23808
|
}
|
|
@@ -23696,7 +23904,7 @@ function convertFromGooseFormat(extensions) {
|
|
|
23696
23904
|
else if (type === "streamable_http") server.type = "http";
|
|
23697
23905
|
else server.type = "stdio";
|
|
23698
23906
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
23699
|
-
if (isStringArray$
|
|
23907
|
+
if (isStringArray$2(ext.args)) server.args = ext.args;
|
|
23700
23908
|
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
23701
23909
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
23702
23910
|
if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
@@ -23717,11 +23925,11 @@ function buildGoosePluginStdioServer(config) {
|
|
|
23717
23925
|
if (Array.isArray(command)) {
|
|
23718
23926
|
if (typeof command[0] === "string") server.command = command[0];
|
|
23719
23927
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
23720
|
-
const args = isStringArray$
|
|
23928
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
23721
23929
|
if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
|
|
23722
23930
|
} else if (typeof command === "string") {
|
|
23723
23931
|
server.command = command;
|
|
23724
|
-
if (isStringArray$
|
|
23932
|
+
if (isStringArray$2(config.args)) server.args = config.args;
|
|
23725
23933
|
}
|
|
23726
23934
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
23727
23935
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
@@ -24053,7 +24261,7 @@ function copyHermesOauth(source) {
|
|
|
24053
24261
|
"client_secret"
|
|
24054
24262
|
]) if (typeof source[key] === "string") oauth[key] = source[key];
|
|
24055
24263
|
if (typeof source.redirect_port === "number") oauth.redirect_port = source.redirect_port;
|
|
24056
|
-
if (isStringArray$
|
|
24264
|
+
if (isStringArray$2(source.scopes)) oauth.scopes = source.scopes;
|
|
24057
24265
|
return Object.keys(oauth).length > 0 ? oauth : void 0;
|
|
24058
24266
|
}
|
|
24059
24267
|
function copyHermesAdvancedFields(source, target) {
|
|
@@ -24062,7 +24270,7 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
24062
24270
|
target.auth = source.auth;
|
|
24063
24271
|
copied = true;
|
|
24064
24272
|
}
|
|
24065
|
-
if (typeof source.client_cert === "string" || isStringArray$
|
|
24273
|
+
if (typeof source.client_cert === "string" || isStringArray$2(source.client_cert)) {
|
|
24066
24274
|
target.client_cert = source.client_cert;
|
|
24067
24275
|
copied = true;
|
|
24068
24276
|
}
|
|
@@ -24131,8 +24339,8 @@ function copyHermesAdvancedFields(source, target) {
|
|
|
24131
24339
|
*/
|
|
24132
24340
|
function buildHermesToolsBlock(config) {
|
|
24133
24341
|
const tools = {};
|
|
24134
|
-
if (isStringArray$
|
|
24135
|
-
if (isStringArray$
|
|
24342
|
+
if (isStringArray$2(config.enabledTools)) tools.include = config.enabledTools;
|
|
24343
|
+
if (isStringArray$2(config.disabledTools)) tools.exclude = config.disabledTools;
|
|
24136
24344
|
if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
|
|
24137
24345
|
if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
|
|
24138
24346
|
return tools;
|
|
@@ -24144,8 +24352,8 @@ function buildHermesToolsBlock(config) {
|
|
|
24144
24352
|
* `promptsEnabled`/`resourcesEnabled` top-level toggles.
|
|
24145
24353
|
*/
|
|
24146
24354
|
function applyHermesToolsBlock(hermesTools, server) {
|
|
24147
|
-
if (isStringArray$
|
|
24148
|
-
if (isStringArray$
|
|
24355
|
+
if (isStringArray$2(hermesTools.include)) server.enabledTools = hermesTools.include;
|
|
24356
|
+
if (isStringArray$2(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
|
|
24149
24357
|
if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
|
|
24150
24358
|
if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
|
|
24151
24359
|
}
|
|
@@ -24171,11 +24379,11 @@ function convertServerToHermes(config) {
|
|
|
24171
24379
|
if (Array.isArray(command)) {
|
|
24172
24380
|
if (typeof command[0] === "string") out.command = command[0];
|
|
24173
24381
|
const rest = command.slice(1).filter((c) => typeof c === "string");
|
|
24174
|
-
const args = isStringArray$
|
|
24382
|
+
const args = isStringArray$2(config.args) ? config.args : [];
|
|
24175
24383
|
if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
|
|
24176
24384
|
} else if (typeof command === "string") {
|
|
24177
24385
|
out.command = command;
|
|
24178
|
-
if (isStringArray$
|
|
24386
|
+
if (isStringArray$2(config.args)) out.args = config.args;
|
|
24179
24387
|
}
|
|
24180
24388
|
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
24181
24389
|
} else if (url !== void 0) {
|
|
@@ -24225,7 +24433,7 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
24225
24433
|
if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord$1(config)) continue;
|
|
24226
24434
|
const server = {};
|
|
24227
24435
|
if (typeof config.command === "string") server.command = config.command;
|
|
24228
|
-
if (isStringArray$
|
|
24436
|
+
if (isStringArray$2(config.args)) server.args = config.args;
|
|
24229
24437
|
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
24230
24438
|
if (typeof config.url === "string") server.url = config.url;
|
|
24231
24439
|
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
@@ -25183,13 +25391,17 @@ function mergeToolLists(...lists) {
|
|
|
25183
25391
|
*
|
|
25184
25392
|
* Both native names are documented per-server fields, so a config that already
|
|
25185
25393
|
* spells them natively keeps working: the two lists are merged rather than
|
|
25186
|
-
* one overwriting the other.
|
|
25394
|
+
* one overwriting the other. That is the "merge" branch of the collision rule
|
|
25395
|
+
* documented on `enabledTools` in `src/types/mcp.ts` — it applies here because
|
|
25396
|
+
* the native names are redundant spellings of the same additive lists;
|
|
25397
|
+
* `copilotcli-mcp.ts` lets the canonical value win instead, and
|
|
25398
|
+
* `codexcli-mcp.ts` refuses the canonical value instead.
|
|
25187
25399
|
* @see https://kiro.dev/docs/mcp/configuration/
|
|
25188
25400
|
*/
|
|
25189
25401
|
function toKiroMcpServers(servers) {
|
|
25190
25402
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
25191
25403
|
const { kiroAutoApprove, kiroAutoBlock, disabledTools, ...rest } = server;
|
|
25192
|
-
const autoApprove = mergeToolLists(isStringArray$
|
|
25404
|
+
const autoApprove = mergeToolLists(isStringArray$2(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
|
|
25193
25405
|
const disabled = mergeToolLists(disabledTools, kiroAutoBlock);
|
|
25194
25406
|
return [name, {
|
|
25195
25407
|
...rest,
|
|
@@ -25213,7 +25425,7 @@ function fromKiroMcpServers(servers) {
|
|
|
25213
25425
|
return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
|
|
25214
25426
|
if (server === null || typeof server !== "object" || Array.isArray(server)) return [name, server];
|
|
25215
25427
|
const { autoApprove, ...rest } = server;
|
|
25216
|
-
if (!isStringArray$
|
|
25428
|
+
if (!isStringArray$2(autoApprove)) return [name, server];
|
|
25217
25429
|
return [name, {
|
|
25218
25430
|
...rest,
|
|
25219
25431
|
kiroAutoApprove: autoApprove
|
|
@@ -26355,7 +26567,7 @@ async function readRovodevConfigYaml({ outputRoot }) {
|
|
|
26355
26567
|
}
|
|
26356
26568
|
function disabledNamesOf(config) {
|
|
26357
26569
|
const mcpBlock = config && isRecord$1(config.mcp) ? config.mcp : {};
|
|
26358
|
-
return isStringArray$
|
|
26570
|
+
return isStringArray$2(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
|
|
26359
26571
|
}
|
|
26360
26572
|
/**
|
|
26361
26573
|
* Auxiliary writer for the `mcp:` block of `.rovodev/config.yml` (project) /
|
|
@@ -26488,7 +26700,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
26488
26700
|
return [];
|
|
26489
26701
|
}
|
|
26490
26702
|
const existingMcp = isRecord$1(existingParsed.mcp) ? { ...existingParsed.mcp } : {};
|
|
26491
|
-
const existingDisabled = isStringArray$
|
|
26703
|
+
const existingDisabled = isStringArray$2(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
|
|
26492
26704
|
const managedNameSet = new Set(managedNames);
|
|
26493
26705
|
const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
|
|
26494
26706
|
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 +27542,7 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
27330
27542
|
meta: {
|
|
27331
27543
|
supportsProject: true,
|
|
27332
27544
|
supportsGlobal: true,
|
|
27333
|
-
supportsEnabledTools:
|
|
27545
|
+
supportsEnabledTools: true,
|
|
27334
27546
|
supportsDisabledTools: false
|
|
27335
27547
|
}
|
|
27336
27548
|
}],
|
|
@@ -28154,6 +28366,34 @@ function convertAmpToRulesync({ disable, permissions }) {
|
|
|
28154
28366
|
//#endregion
|
|
28155
28367
|
//#region src/features/permissions/antigravity-cli-permissions.ts
|
|
28156
28368
|
/**
|
|
28369
|
+
* Top-level `~/.gemini/antigravity-cli/settings.json` keys the `antigravity-cli`
|
|
28370
|
+
* permissions override may author. They are siblings of `permissions`, passed
|
|
28371
|
+
* through verbatim in both directions.
|
|
28372
|
+
*/
|
|
28373
|
+
const ANTIGRAVITY_CLI_OVERRIDE_KEYS = [
|
|
28374
|
+
"toolPermission",
|
|
28375
|
+
"enableTerminalSandbox",
|
|
28376
|
+
"artifactReviewPolicy",
|
|
28377
|
+
"allowNonWorkspaceAccess",
|
|
28378
|
+
"agentMode"
|
|
28379
|
+
];
|
|
28380
|
+
const moduleLogger$2 = fallbackLogger;
|
|
28381
|
+
/**
|
|
28382
|
+
* Narrow an imported `settings.json` value to one of the documented values of
|
|
28383
|
+
* an enum-typed override key. The override schema declares `toolPermission`,
|
|
28384
|
+
* `artifactReviewPolicy` and `agentMode` as `z.enum`, so carrying an
|
|
28385
|
+
* unrecognized value through — a hand-written typo, or a preset a newer
|
|
28386
|
+
* Antigravity release added — would fail validation of the whole imported
|
|
28387
|
+
* `.rulesync/permissions.jsonc` instead of dropping just that key. A dropped
|
|
28388
|
+
* value is warned about rather than discarded silently, so a preset rulesync
|
|
28389
|
+
* does not know about yet is visible instead of quietly missing.
|
|
28390
|
+
*/
|
|
28391
|
+
function pickDocumentedValue({ key, value, documented }) {
|
|
28392
|
+
if (typeof value !== "string") return void 0;
|
|
28393
|
+
if (documented.includes(value)) return value;
|
|
28394
|
+
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.`);
|
|
28395
|
+
}
|
|
28396
|
+
/**
|
|
28157
28397
|
* Deliberate mapping from rulesync canonical permission categories to the
|
|
28158
28398
|
* Antigravity CLI Fine-Grained Permissions Engine action vocabulary
|
|
28159
28399
|
* (`read_file`, `write_file`, `read_url`, `command`, `mcp`, plus `execute_url` /
|
|
@@ -28228,9 +28468,10 @@ function buildPermissionEntry$1(toolName, pattern) {
|
|
|
28228
28468
|
* file (global scope only). The file holds other CLI settings besides
|
|
28229
28469
|
* permissions, so it is never deleted.
|
|
28230
28470
|
*
|
|
28231
|
-
*
|
|
28471
|
+
* Five CLI-only autonomy/sandbox knobs outside the allow/ask/deny arrays —
|
|
28232
28472
|
* `toolPermission` (the global autonomy preset), `enableTerminalSandbox`,
|
|
28233
|
-
* `artifactReviewPolicy` and `
|
|
28473
|
+
* `artifactReviewPolicy`, `allowNonWorkspaceAccess` and `agentMode` (the
|
|
28474
|
+
* baseline execution mode) — are authored and
|
|
28234
28475
|
* round-tripped through the `antigravity-cli` permissions override (see
|
|
28235
28476
|
* `AntigravityCliPermissionsOverrideSchema`).
|
|
28236
28477
|
*/
|
|
@@ -28294,10 +28535,10 @@ var AntigravityCliPermissions = class AntigravityCliPermissions extends ToolPerm
|
|
|
28294
28535
|
permissions: mergedPermissions
|
|
28295
28536
|
};
|
|
28296
28537
|
const override = config["antigravity-cli"];
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28538
|
+
for (const key of ANTIGRAVITY_CLI_OVERRIDE_KEYS) {
|
|
28539
|
+
const value = override?.[key];
|
|
28540
|
+
if (value !== void 0) merged[key] = value;
|
|
28541
|
+
}
|
|
28301
28542
|
const fileContent = JSON.stringify(merged, null, 2);
|
|
28302
28543
|
return new AntigravityCliPermissions({
|
|
28303
28544
|
outputRoot,
|
|
@@ -28322,10 +28563,26 @@ var AntigravityCliPermissions = class AntigravityCliPermissions extends ToolPerm
|
|
|
28322
28563
|
deny: permissions.deny ?? []
|
|
28323
28564
|
});
|
|
28324
28565
|
const override = {};
|
|
28325
|
-
|
|
28566
|
+
const toolPermission = pickDocumentedValue({
|
|
28567
|
+
key: "toolPermission",
|
|
28568
|
+
value: settings.toolPermission,
|
|
28569
|
+
documented: ANTIGRAVITY_CLI_TOOL_PERMISSIONS
|
|
28570
|
+
});
|
|
28571
|
+
if (toolPermission !== void 0) override.toolPermission = toolPermission;
|
|
28326
28572
|
if (typeof settings.enableTerminalSandbox === "boolean") override.enableTerminalSandbox = settings.enableTerminalSandbox;
|
|
28327
|
-
|
|
28573
|
+
const artifactReviewPolicy = pickDocumentedValue({
|
|
28574
|
+
key: "artifactReviewPolicy",
|
|
28575
|
+
value: settings.artifactReviewPolicy,
|
|
28576
|
+
documented: ANTIGRAVITY_CLI_ARTIFACT_REVIEW_POLICIES
|
|
28577
|
+
});
|
|
28578
|
+
if (artifactReviewPolicy !== void 0) override.artifactReviewPolicy = artifactReviewPolicy;
|
|
28328
28579
|
if (typeof settings.allowNonWorkspaceAccess === "boolean") override.allowNonWorkspaceAccess = settings.allowNonWorkspaceAccess;
|
|
28580
|
+
const agentMode = pickDocumentedValue({
|
|
28581
|
+
key: "agentMode",
|
|
28582
|
+
value: settings.agentMode,
|
|
28583
|
+
documented: ANTIGRAVITY_CLI_AGENT_MODES
|
|
28584
|
+
});
|
|
28585
|
+
if (agentMode !== void 0) override.agentMode = agentMode;
|
|
28329
28586
|
const result = { ...config };
|
|
28330
28587
|
if (Object.keys(override).length > 0) result["antigravity-cli"] = override;
|
|
28331
28588
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
@@ -29151,6 +29408,119 @@ function deepMergeRecords(base, patch) {
|
|
|
29151
29408
|
}
|
|
29152
29409
|
return merged;
|
|
29153
29410
|
}
|
|
29411
|
+
/**
|
|
29412
|
+
* `sandbox.*` paths Claude Code honors only from user settings, managed settings
|
|
29413
|
+
* and the `--settings` CLI flag. Written into a project `.claude/settings.json`
|
|
29414
|
+
* they are silently ignored, so a committed file would read as though it
|
|
29415
|
+
* enforced a sandbox policy while doing nothing — security-relevant for
|
|
29416
|
+
* `network.strictAllowlist` and the credential-masking keys in particular.
|
|
29417
|
+
* Generation therefore drops them at project scope with a per-key warning and
|
|
29418
|
+
* emits them only under `--global`.
|
|
29419
|
+
*
|
|
29420
|
+
* Deliberately NOT listed:
|
|
29421
|
+
* - `bwrapPath` / `socatPath`: v2.1.232 added them to the managed-settings
|
|
29422
|
+
* approval dialog, which is a consent prompt, not a project-scope rejection.
|
|
29423
|
+
* - `credentials.envVars` / `credentials.files`: the ignored-at-project-scope
|
|
29424
|
+
* unit is the individual entry's mode, not the settings key, and the same
|
|
29425
|
+
* lists carry `deny` entries that project settings *do* honor — dropping a
|
|
29426
|
+
* whole list would remove real restrictions. `stripProjectIgnoredMaskEntries`
|
|
29427
|
+
* filters those lists per entry instead.
|
|
29428
|
+
*
|
|
29429
|
+
* @see https://code.claude.com/docs/en/sandboxing
|
|
29430
|
+
* @see https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md — v2.1.232 scoped `sandbox.ripgrep`
|
|
29431
|
+
*/
|
|
29432
|
+
const CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS = [
|
|
29433
|
+
["filesystem", "disabled"],
|
|
29434
|
+
["network", "strictAllowlist"],
|
|
29435
|
+
["network", "tlsTerminate"],
|
|
29436
|
+
["credentials", "allowPlaintextInject"],
|
|
29437
|
+
["credentials", "awsPairs"],
|
|
29438
|
+
["credentials", "sigv4"],
|
|
29439
|
+
["allowAppleEvents"],
|
|
29440
|
+
["ripgrep"]
|
|
29441
|
+
];
|
|
29442
|
+
/**
|
|
29443
|
+
* Copy of the authored `sandbox` override with the user/managed-only paths
|
|
29444
|
+
* removed, warning once per dropped path. Only the override copy is filtered —
|
|
29445
|
+
* a value already hand-written in the target file is left untouched, matching
|
|
29446
|
+
* the `qwencode` `security.allowPrivateNetworkHooks` precedent.
|
|
29447
|
+
*/
|
|
29448
|
+
function stripGlobalOnlySandboxPaths({ sandbox, relativeFilePath, logger }) {
|
|
29449
|
+
const filtered = structuredClone(sandbox);
|
|
29450
|
+
for (const path of CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS) {
|
|
29451
|
+
const leaf = path.at(-1);
|
|
29452
|
+
if (leaf === void 0) continue;
|
|
29453
|
+
const parentPath = path.slice(0, -1);
|
|
29454
|
+
let parent = filtered;
|
|
29455
|
+
for (const segment of parentPath) {
|
|
29456
|
+
const next = parent[segment];
|
|
29457
|
+
if (!isPlainRecord(next)) {
|
|
29458
|
+
parent = {};
|
|
29459
|
+
break;
|
|
29460
|
+
}
|
|
29461
|
+
parent = next;
|
|
29462
|
+
}
|
|
29463
|
+
if (parent[leaf] === void 0) continue;
|
|
29464
|
+
delete parent[leaf];
|
|
29465
|
+
const [container] = parentPath;
|
|
29466
|
+
if (container !== void 0 && isPlainRecord(filtered[container])) {
|
|
29467
|
+
if (Object.keys(filtered[container]).length === 0) delete filtered[container];
|
|
29468
|
+
}
|
|
29469
|
+
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.`);
|
|
29470
|
+
}
|
|
29471
|
+
return filtered;
|
|
29472
|
+
}
|
|
29473
|
+
/** The `sandbox.credentials` lists whose entries accept `"mode": "mask"`. */
|
|
29474
|
+
const CLAUDECODE_MASKABLE_CREDENTIAL_LISTS = ["envVars", "files"];
|
|
29475
|
+
/**
|
|
29476
|
+
* Copy of the authored `sandbox` override with the `credentials.envVars` /
|
|
29477
|
+
* `credentials.files` entries whose `mode` is `"mask"` removed, warning once per
|
|
29478
|
+
* list.
|
|
29479
|
+
*
|
|
29480
|
+
* Masking authorizes the sandbox proxy to send the real credential to the listed
|
|
29481
|
+
* hosts, so Claude Code honors it only from user settings, managed settings and
|
|
29482
|
+
* the `--settings` CLI flag; a `mask` entry in a repository's
|
|
29483
|
+
* `.claude/settings.json` is ignored outright. Keeping it would read as though
|
|
29484
|
+
* the credential were masked while nothing protects it — the one project-scope
|
|
29485
|
+
* gap whose consequence is a live credential leaving the sandbox unmasked.
|
|
29486
|
+
*
|
|
29487
|
+
* Filtered per entry rather than per key, because the same lists carry `deny`
|
|
29488
|
+
* entries that project settings *do* honor. `mode` is matched exactly: an entry
|
|
29489
|
+
* Claude Code cannot read as `mask` is not treated as one either (it degrades
|
|
29490
|
+
* such entries to `deny`), so the "reads as masked but isn't" state this guards
|
|
29491
|
+
* against cannot slip through a differently-spelled value.
|
|
29492
|
+
*
|
|
29493
|
+
* Like `stripGlobalOnlySandboxPaths`, only the override copy is filtered — a
|
|
29494
|
+
* value already in the target file is left untouched, which is why the warning
|
|
29495
|
+
* points at it.
|
|
29496
|
+
*
|
|
29497
|
+
* @see https://code.claude.com/docs/en/sandboxing — "`mask` entries … are all
|
|
29498
|
+
* ignored in a repository's `.claude/settings.json` or
|
|
29499
|
+
* `.claude/settings.local.json`"; the file-entry section states the same
|
|
29500
|
+
* settings-source restriction applies there.
|
|
29501
|
+
*/
|
|
29502
|
+
function stripProjectIgnoredMaskEntries({ sandbox, relativeFilePath, logger }) {
|
|
29503
|
+
const credentials = sandbox.credentials;
|
|
29504
|
+
if (!isPlainRecord(credentials)) return sandbox;
|
|
29505
|
+
const filteredCredentials = { ...credentials };
|
|
29506
|
+
let changed = false;
|
|
29507
|
+
for (const listKey of CLAUDECODE_MASKABLE_CREDENTIAL_LISTS) {
|
|
29508
|
+
const list = filteredCredentials[listKey];
|
|
29509
|
+
if (!Array.isArray(list)) continue;
|
|
29510
|
+
const kept = list.filter((entry) => !(isPlainRecord(entry) && entry.mode === "mask"));
|
|
29511
|
+
if (kept.length === list.length) continue;
|
|
29512
|
+
changed = true;
|
|
29513
|
+
const dropped = list.length - kept.length;
|
|
29514
|
+
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.`);
|
|
29515
|
+
if (kept.length === 0) delete filteredCredentials[listKey];
|
|
29516
|
+
else filteredCredentials[listKey] = kept;
|
|
29517
|
+
}
|
|
29518
|
+
if (!changed) return sandbox;
|
|
29519
|
+
const filtered = { ...sandbox };
|
|
29520
|
+
if (Object.keys(filteredCredentials).length === 0) delete filtered.credentials;
|
|
29521
|
+
else filtered.credentials = filteredCredentials;
|
|
29522
|
+
return filtered;
|
|
29523
|
+
}
|
|
29154
29524
|
const CLAUDE_PATH_RULE_ALIASES = {
|
|
29155
29525
|
Write: "Edit",
|
|
29156
29526
|
NotebookEdit: "Edit",
|
|
@@ -29204,7 +29574,7 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29204
29574
|
validate
|
|
29205
29575
|
});
|
|
29206
29576
|
}
|
|
29207
|
-
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, logger }) {
|
|
29577
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
29208
29578
|
const paths = ClaudecodePermissions.getSettablePaths();
|
|
29209
29579
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
29210
29580
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
@@ -29228,7 +29598,18 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29228
29598
|
};
|
|
29229
29599
|
}
|
|
29230
29600
|
const overrideSandbox = config.claudecode?.sandbox;
|
|
29231
|
-
if (isPlainRecord(overrideSandbox))
|
|
29601
|
+
if (isPlainRecord(overrideSandbox)) {
|
|
29602
|
+
const scopedSandbox = global ? overrideSandbox : stripProjectIgnoredMaskEntries({
|
|
29603
|
+
sandbox: stripGlobalOnlySandboxPaths({
|
|
29604
|
+
sandbox: overrideSandbox,
|
|
29605
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29606
|
+
logger
|
|
29607
|
+
}),
|
|
29608
|
+
relativeFilePath: paths.relativeFilePath,
|
|
29609
|
+
logger
|
|
29610
|
+
});
|
|
29611
|
+
if (Object.keys(scopedSandbox).length > 0) settings.sandbox = deepMergeRecords(isPlainRecord(settings.sandbox) ? settings.sandbox : {}, scopedSandbox);
|
|
29612
|
+
}
|
|
29232
29613
|
const managedToolNames = managedClaudeToolNames(config);
|
|
29233
29614
|
const merged = applyPermissions({
|
|
29234
29615
|
settings,
|
|
@@ -31405,7 +31786,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
31405
31786
|
function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
31406
31787
|
const permission = {};
|
|
31407
31788
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
31408
|
-
const toolNames = isStringArray$
|
|
31789
|
+
const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
|
|
31409
31790
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
31410
31791
|
for (const toolName of toolNames) {
|
|
31411
31792
|
const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
|
|
@@ -31698,7 +32079,7 @@ const ACTION_RANK = {
|
|
|
31698
32079
|
* (mirrors the Cursor adapter's preservation of unmanaged types).
|
|
31699
32080
|
*/
|
|
31700
32081
|
function unmanagedEntries(existingPermission, key) {
|
|
31701
|
-
return (isStringArray$
|
|
32082
|
+
return (isStringArray$2(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
|
|
31702
32083
|
}
|
|
31703
32084
|
/**
|
|
31704
32085
|
* Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
|
|
@@ -31741,9 +32122,9 @@ function buildGrokPermissionArrays(config, existingPermission, logger) {
|
|
|
31741
32122
|
* described more than once resolves to the strictest action.
|
|
31742
32123
|
*/
|
|
31743
32124
|
function parseGrokPermissionArrays(permission) {
|
|
31744
|
-
const allow = isStringArray$
|
|
31745
|
-
const deny = isStringArray$
|
|
31746
|
-
const ask = isStringArray$
|
|
32125
|
+
const allow = isStringArray$2(permission.allow) ? permission.allow : void 0;
|
|
32126
|
+
const deny = isStringArray$2(permission.deny) ? permission.deny : void 0;
|
|
32127
|
+
const ask = isStringArray$2(permission.ask) ? permission.ask : void 0;
|
|
31747
32128
|
const rules = Array.isArray(permission[GROKCLI_PERMISSION_RULES_KEY]) ? permission[GROKCLI_PERMISSION_RULES_KEY] : void 0;
|
|
31748
32129
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
31749
32130
|
const result = {};
|
|
@@ -31925,20 +32306,20 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
31925
32306
|
const permission = clonePermissionBlock(provenance.permission);
|
|
31926
32307
|
reconcileCommandAllowlist({
|
|
31927
32308
|
permission,
|
|
31928
|
-
commandAllowlist: isStringArray$
|
|
32309
|
+
commandAllowlist: isStringArray$2(config.command_allowlist) ? config.command_allowlist : []
|
|
31929
32310
|
});
|
|
31930
32311
|
const approvals = isRecord$1(config.approvals) ? config.approvals : {};
|
|
31931
32312
|
reconcileNativeDenies({
|
|
31932
32313
|
permission,
|
|
31933
32314
|
category: "bash",
|
|
31934
|
-
patterns: isStringArray$
|
|
32315
|
+
patterns: isStringArray$2(approvals.deny) ? approvals.deny : []
|
|
31935
32316
|
});
|
|
31936
32317
|
const security = isRecord$1(config.security) ? config.security : {};
|
|
31937
32318
|
const websiteBlocklist = isRecord$1(security.website_blocklist) ? security.website_blocklist : {};
|
|
31938
32319
|
reconcileNativeDenies({
|
|
31939
32320
|
permission,
|
|
31940
32321
|
category: "webfetch",
|
|
31941
|
-
patterns: websiteBlocklist.enabled === true && isStringArray$
|
|
32322
|
+
patterns: websiteBlocklist.enabled === true && isStringArray$2(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
31942
32323
|
});
|
|
31943
32324
|
removeEmptyCategories(permission);
|
|
31944
32325
|
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
@@ -33366,6 +33747,115 @@ var OpencodePermissions = class OpencodePermissions extends ToolPermissions {
|
|
|
33366
33747
|
}
|
|
33367
33748
|
};
|
|
33368
33749
|
//#endregion
|
|
33750
|
+
//#region src/features/permissions/pi-permissions.ts
|
|
33751
|
+
/** The one `settings.json` key this feature owns. */
|
|
33752
|
+
const DEFAULT_TOOLS_KEY = "defaultTools";
|
|
33753
|
+
/**
|
|
33754
|
+
* Permissions generator for Pi Coding Agent.
|
|
33755
|
+
*
|
|
33756
|
+
* Pi exposes no allow/ask/deny rule surface, so no canonical permission
|
|
33757
|
+
* category maps onto it. Its one repository-syncable tool gate is `defaultTools` —
|
|
33758
|
+
* the built-in tools enabled at startup (v0.84.2) — authored through the `pi`
|
|
33759
|
+
* permissions override.
|
|
33760
|
+
*
|
|
33761
|
+
* Scope semantics are the **opposite** of the union-merge most targets use:
|
|
33762
|
+
* a project `defaultTools` array *replaces* the global array rather than adding
|
|
33763
|
+
* to it, so the two scopes are written independently and never combined.
|
|
33764
|
+
* Project scope writes `.pi/settings.json`; global scope writes
|
|
33765
|
+
* `~/.pi/agent/settings.json`.
|
|
33766
|
+
*
|
|
33767
|
+
* The file is hand-edited and holds many unrelated keys (`theme`,
|
|
33768
|
+
* `defaultModel`, `packages`, `sessionDir`, ...), so writes go through the
|
|
33769
|
+
* shared-config gateway with `defaultTools` as the only owned key, and the file
|
|
33770
|
+
* is never deleted.
|
|
33771
|
+
*
|
|
33772
|
+
* @see https://pi.dev/docs/latest/settings
|
|
33773
|
+
*/
|
|
33774
|
+
var PiPermissions = class PiPermissions extends ToolPermissions {
|
|
33775
|
+
constructor(params) {
|
|
33776
|
+
super({
|
|
33777
|
+
...params,
|
|
33778
|
+
fileContent: params.fileContent ?? "{}"
|
|
33779
|
+
});
|
|
33780
|
+
}
|
|
33781
|
+
/** `settings.json` holds unrelated user settings, so it must not be deleted. */
|
|
33782
|
+
isDeletable() {
|
|
33783
|
+
return false;
|
|
33784
|
+
}
|
|
33785
|
+
static getSettablePaths({ global = false } = {}) {
|
|
33786
|
+
return {
|
|
33787
|
+
relativeDirPath: global ? PI_AGENT_DIR_PATH : ".pi",
|
|
33788
|
+
relativeFilePath: PI_SETTINGS_FILE_NAME
|
|
33789
|
+
};
|
|
33790
|
+
}
|
|
33791
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
33792
|
+
const paths = PiPermissions.getSettablePaths({ global });
|
|
33793
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
|
|
33794
|
+
return new PiPermissions({
|
|
33795
|
+
outputRoot,
|
|
33796
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33797
|
+
relativeFilePath: paths.relativeFilePath,
|
|
33798
|
+
fileContent,
|
|
33799
|
+
validate,
|
|
33800
|
+
global
|
|
33801
|
+
});
|
|
33802
|
+
}
|
|
33803
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false }) {
|
|
33804
|
+
const paths = PiPermissions.getSettablePaths({ global });
|
|
33805
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
33806
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
33807
|
+
const defaultTools = rulesyncPermissions.getJson().pi?.defaultTools;
|
|
33808
|
+
const patch = {};
|
|
33809
|
+
if (defaultTools !== void 0) patch[DEFAULT_TOOLS_KEY] = defaultTools;
|
|
33810
|
+
return new PiPermissions({
|
|
33811
|
+
outputRoot,
|
|
33812
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33813
|
+
relativeFilePath: paths.relativeFilePath,
|
|
33814
|
+
fileContent: applySharedConfigPatch({
|
|
33815
|
+
fileKey: sharedConfigFileKey(paths),
|
|
33816
|
+
feature: "permissions",
|
|
33817
|
+
existingContent,
|
|
33818
|
+
patch,
|
|
33819
|
+
filePath
|
|
33820
|
+
}),
|
|
33821
|
+
validate: true,
|
|
33822
|
+
global
|
|
33823
|
+
});
|
|
33824
|
+
}
|
|
33825
|
+
toRulesyncPermissions() {
|
|
33826
|
+
const defaultTools = this.parseSettings()[DEFAULT_TOOLS_KEY];
|
|
33827
|
+
const config = { permission: {} };
|
|
33828
|
+
if (Array.isArray(defaultTools) && defaultTools.every((tool) => typeof tool === "string")) config.pi = { [DEFAULT_TOOLS_KEY]: defaultTools };
|
|
33829
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(config, null, 2) });
|
|
33830
|
+
}
|
|
33831
|
+
parseSettings() {
|
|
33832
|
+
try {
|
|
33833
|
+
return parseSharedConfig({
|
|
33834
|
+
format: "json",
|
|
33835
|
+
fileContent: this.getFileContent()
|
|
33836
|
+
});
|
|
33837
|
+
} catch (error) {
|
|
33838
|
+
throw new Error(`Failed to parse Pi settings in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
33839
|
+
}
|
|
33840
|
+
}
|
|
33841
|
+
validate() {
|
|
33842
|
+
return {
|
|
33843
|
+
success: true,
|
|
33844
|
+
error: null
|
|
33845
|
+
};
|
|
33846
|
+
}
|
|
33847
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
33848
|
+
return new PiPermissions({
|
|
33849
|
+
outputRoot,
|
|
33850
|
+
relativeDirPath,
|
|
33851
|
+
relativeFilePath,
|
|
33852
|
+
fileContent: "{}",
|
|
33853
|
+
validate: false,
|
|
33854
|
+
global
|
|
33855
|
+
});
|
|
33856
|
+
}
|
|
33857
|
+
};
|
|
33858
|
+
//#endregion
|
|
33369
33859
|
//#region src/features/permissions/qwencode-permissions.ts
|
|
33370
33860
|
/**
|
|
33371
33861
|
* Qwen Code uses a settings.json file in `.qwen/` (project) or `~/.qwen/` (global).
|
|
@@ -34429,7 +34919,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
34429
34919
|
permission[category] ??= {};
|
|
34430
34920
|
permission[category][CATCH_ALL_PATTERN$1] = levels.reduce((strictest, level) => strictestAction(strictest, level ?? implicitLevel), permission[category][CATCH_ALL_PATTERN$1]);
|
|
34431
34921
|
}
|
|
34432
|
-
if (isStringArray$
|
|
34922
|
+
if (isStringArray$2(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
34433
34923
|
permission.read ??= {};
|
|
34434
34924
|
permission.read[path] ??= "allow";
|
|
34435
34925
|
}
|
|
@@ -35406,8 +35896,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
|
|
|
35406
35896
|
const executionProfiles = isRecord$1(agents[EXECUTION_PROFILES_KEY]) ? agents[EXECUTION_PROFILES_KEY] : void 0;
|
|
35407
35897
|
const defaultProfile = executionProfiles && isRecord$1(executionProfiles[DEFAULT_PROFILE_KEY]) ? executionProfiles[DEFAULT_PROFILE_KEY] : void 0;
|
|
35408
35898
|
const config = convertWarpToRulesyncPermissions({
|
|
35409
|
-
allow: defaultProfile ? isStringArray$
|
|
35410
|
-
deny: defaultProfile ? isStringArray$
|
|
35899
|
+
allow: defaultProfile ? isStringArray$2(defaultProfile[PROFILE_ALLOWLIST_KEY]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$2(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
|
|
35900
|
+
deny: defaultProfile ? isStringArray$2(defaultProfile[PROFILE_DENYLIST_KEY]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$2(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
|
|
35411
35901
|
});
|
|
35412
35902
|
const warpOverride = {};
|
|
35413
35903
|
for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
|
|
@@ -36119,6 +36609,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
36119
36609
|
supportsImport: true
|
|
36120
36610
|
}
|
|
36121
36611
|
}],
|
|
36612
|
+
["pi", {
|
|
36613
|
+
class: PiPermissions,
|
|
36614
|
+
meta: {
|
|
36615
|
+
supportsProject: true,
|
|
36616
|
+
supportsGlobal: true,
|
|
36617
|
+
supportsImport: true
|
|
36618
|
+
}
|
|
36619
|
+
}],
|
|
36122
36620
|
["qwencode", {
|
|
36123
36621
|
class: QwencodePermissions,
|
|
36124
36622
|
meta: {
|
|
@@ -38430,6 +38928,7 @@ const CursorSkillFrontmatterSchema = z.looseObject({
|
|
|
38430
38928
|
description: z.string(),
|
|
38431
38929
|
paths: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
38432
38930
|
"disable-model-invocation": z.optional(z.boolean()),
|
|
38931
|
+
"user-invocable": z.optional(z.boolean()),
|
|
38433
38932
|
metadata: z.optional(z.looseObject({}))
|
|
38434
38933
|
});
|
|
38435
38934
|
/**
|
|
@@ -38484,6 +38983,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
|
|
|
38484
38983
|
const cursorSection = {
|
|
38485
38984
|
...frontmatter.paths !== void 0 && { paths: frontmatter.paths },
|
|
38486
38985
|
...frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] },
|
|
38986
|
+
...frontmatter["user-invocable"] !== void 0 && { "user-invocable": frontmatter["user-invocable"] },
|
|
38487
38987
|
...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata }
|
|
38488
38988
|
};
|
|
38489
38989
|
const rulesyncFrontmatter = {
|
|
@@ -38511,11 +39011,16 @@ var CursorSkill = class CursorSkill extends ToolSkill {
|
|
|
38511
39011
|
rootFrontmatter: rulesyncFrontmatter,
|
|
38512
39012
|
section: cursorSection
|
|
38513
39013
|
});
|
|
39014
|
+
const resolvedUserInvocable = resolveUserInvocable({
|
|
39015
|
+
rootFrontmatter: rulesyncFrontmatter,
|
|
39016
|
+
section: cursorSection
|
|
39017
|
+
});
|
|
38514
39018
|
const cursorFrontmatter = {
|
|
38515
39019
|
name: rulesyncFrontmatter.name,
|
|
38516
39020
|
description: rulesyncFrontmatter.description,
|
|
38517
39021
|
...cursorSection?.paths !== void 0 && { paths: cursorSection.paths },
|
|
38518
39022
|
...resolvedDisableModelInvocation !== void 0 && { "disable-model-invocation": resolvedDisableModelInvocation },
|
|
39023
|
+
...resolvedUserInvocable !== void 0 && { "user-invocable": resolvedUserInvocable },
|
|
38519
39024
|
...cursorSection?.metadata !== void 0 && { metadata: cursorSection.metadata }
|
|
38520
39025
|
};
|
|
38521
39026
|
return new CursorSkill({
|
|
@@ -46518,6 +47023,23 @@ var TaktSubagent = class TaktSubagent extends ToolSubagent {
|
|
|
46518
47023
|
};
|
|
46519
47024
|
//#endregion
|
|
46520
47025
|
//#region src/features/subagents/vibe-subagent.ts
|
|
47026
|
+
/**
|
|
47027
|
+
* Where Vibe reads a custom system prompt from: `.vibe/prompts/<id>.md`
|
|
47028
|
+
* (project) and `~/.vibe/prompts/<id>.md` (user), with the custom directories
|
|
47029
|
+
* winning over the builtins. The id must be a bare file name with no path
|
|
47030
|
+
* separators.
|
|
47031
|
+
* @see vibe/core/prompts/__init__.py
|
|
47032
|
+
*/
|
|
47033
|
+
const VIBE_PROMPTS_DIR_PATH = join(".vibe", "prompts");
|
|
47034
|
+
/**
|
|
47035
|
+
* Vibe resolves a `system_prompt_id` by file name, and
|
|
47036
|
+
* `VibeConfigSchema._check_system_prompt` evaluates it during validation — an
|
|
47037
|
+
* unresolvable id raises and the agent is dropped at discovery. The id must
|
|
47038
|
+
* therefore stay a bare slug with no path separators.
|
|
47039
|
+
*/
|
|
47040
|
+
function promptSlug(relativeFilePath) {
|
|
47041
|
+
return basename(relativeFilePath).replace(/\.(md|toml)$/, "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
47042
|
+
}
|
|
46521
47043
|
const VibeSubagentTomlSchema = z.looseObject({
|
|
46522
47044
|
agent_type: z.enum(["agent", "subagent"]),
|
|
46523
47045
|
display_name: z.optional(z.string()),
|
|
@@ -46534,14 +47056,22 @@ const VibeSubagentTomlSchema = z.looseObject({
|
|
|
46534
47056
|
});
|
|
46535
47057
|
var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
46536
47058
|
body;
|
|
46537
|
-
|
|
46538
|
-
|
|
47059
|
+
promptFile;
|
|
47060
|
+
resolvedSystemPrompt;
|
|
47061
|
+
constructor({ body, promptFile = false, resolvedSystemPrompt, ...rest }) {
|
|
47062
|
+
if (rest.validate !== false && !promptFile) try {
|
|
46539
47063
|
VibeSubagentTomlSchema.parse(smolToml.parse(body));
|
|
46540
47064
|
} catch (error) {
|
|
46541
47065
|
throw new Error(`Invalid TOML in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(error)}`, { cause: error });
|
|
46542
47066
|
}
|
|
46543
47067
|
super({ ...rest });
|
|
46544
47068
|
this.body = body;
|
|
47069
|
+
this.promptFile = promptFile;
|
|
47070
|
+
this.resolvedSystemPrompt = resolvedSystemPrompt;
|
|
47071
|
+
}
|
|
47072
|
+
/** True for the companion `.vibe/prompts/<id>.md` file. */
|
|
47073
|
+
isPromptFile() {
|
|
47074
|
+
return this.promptFile;
|
|
46545
47075
|
}
|
|
46546
47076
|
static getSettablePaths(_options = {}) {
|
|
46547
47077
|
return { relativeDirPath: join(".vibe", "agents") };
|
|
@@ -46556,7 +47086,9 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46556
47086
|
} catch (error) {
|
|
46557
47087
|
throw new Error(`Failed to parse TOML in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
46558
47088
|
}
|
|
46559
|
-
const { system_prompt, description, display_name, ...vibeSection } = parsed;
|
|
47089
|
+
const { system_prompt, system_prompt_id, description, display_name, ...vibeSection } = parsed;
|
|
47090
|
+
const resolvedBody = this.resolvedSystemPrompt ?? system_prompt;
|
|
47091
|
+
const unresolvedPromptId = this.resolvedSystemPrompt === void 0 ? system_prompt_id : void 0;
|
|
46560
47092
|
const fileStem = basename(this.getRelativeFilePath(), ".toml");
|
|
46561
47093
|
const rulesyncFrontmatter = {
|
|
46562
47094
|
targets: ["vibe"],
|
|
@@ -46565,13 +47097,14 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46565
47097
|
vibe: {
|
|
46566
47098
|
...display_name !== void 0 && { display_name },
|
|
46567
47099
|
...description !== void 0 && { description },
|
|
47100
|
+
...unresolvedPromptId !== void 0 && { system_prompt_id: unresolvedPromptId },
|
|
46568
47101
|
...vibeSection
|
|
46569
47102
|
}
|
|
46570
47103
|
};
|
|
46571
47104
|
return new RulesyncSubagent({
|
|
46572
47105
|
outputRoot: this.outputRoot,
|
|
46573
47106
|
frontmatter: rulesyncFrontmatter,
|
|
46574
|
-
body:
|
|
47107
|
+
body: resolvedBody ?? "",
|
|
46575
47108
|
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
46576
47109
|
relativeFilePath: this.getRelativeFilePath().replace(/\.toml$/, ".md"),
|
|
46577
47110
|
validate: true
|
|
@@ -46586,12 +47119,14 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46586
47119
|
"description",
|
|
46587
47120
|
"system_prompt"
|
|
46588
47121
|
]);
|
|
47122
|
+
const promptBody = rulesyncSubagent.getBody();
|
|
47123
|
+
const slug = promptSlug(rulesyncSubagent.getRelativeFilePath());
|
|
46589
47124
|
const tomlObj = {
|
|
46590
47125
|
agent_type: rawSection.agent_type === "agent" ? "agent" : "subagent",
|
|
46591
47126
|
display_name: typeof rawSection.display_name === "string" ? rawSection.display_name : frontmatter.name,
|
|
46592
47127
|
...frontmatter.description ? { description: frontmatter.description } : {},
|
|
46593
|
-
...
|
|
46594
|
-
...
|
|
47128
|
+
...vibeSection,
|
|
47129
|
+
...promptBody ? { system_prompt_id: slug } : {}
|
|
46595
47130
|
};
|
|
46596
47131
|
const body = smolToml.stringify(tomlObj);
|
|
46597
47132
|
const paths = this.getSettablePaths({ global });
|
|
@@ -46606,6 +47141,36 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46606
47141
|
global
|
|
46607
47142
|
});
|
|
46608
47143
|
}
|
|
47144
|
+
/**
|
|
47145
|
+
* Emits the agent TOML plus its companion prompt file. The prompt file MUST
|
|
47146
|
+
* be written together with the id: `VibeConfigSchema._check_system_prompt`
|
|
47147
|
+
* evaluates the property during validation, so an unresolvable
|
|
47148
|
+
* `system_prompt_id` raises and `AgentRegistry._try_load` drops the agent
|
|
47149
|
+
* with a warning.
|
|
47150
|
+
*/
|
|
47151
|
+
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false }) {
|
|
47152
|
+
return rulesyncSubagents.flatMap((rulesyncSubagent) => {
|
|
47153
|
+
const agentFile = VibeSubagent.fromRulesyncSubagent({
|
|
47154
|
+
outputRoot,
|
|
47155
|
+
relativeDirPath: VibeSubagent.getSettablePaths({ global }).relativeDirPath,
|
|
47156
|
+
rulesyncSubagent,
|
|
47157
|
+
global
|
|
47158
|
+
});
|
|
47159
|
+
const promptBody = rulesyncSubagent.getBody();
|
|
47160
|
+
if (!promptBody) return [agentFile];
|
|
47161
|
+
const slug = promptSlug(rulesyncSubagent.getRelativeFilePath());
|
|
47162
|
+
return [agentFile, new VibeSubagent({
|
|
47163
|
+
outputRoot,
|
|
47164
|
+
body: promptBody,
|
|
47165
|
+
relativeDirPath: VIBE_PROMPTS_DIR_PATH,
|
|
47166
|
+
relativeFilePath: `${slug}.md`,
|
|
47167
|
+
fileContent: promptBody,
|
|
47168
|
+
promptFile: true,
|
|
47169
|
+
validate: false,
|
|
47170
|
+
global
|
|
47171
|
+
})];
|
|
47172
|
+
});
|
|
47173
|
+
}
|
|
46609
47174
|
validate() {
|
|
46610
47175
|
try {
|
|
46611
47176
|
VibeSubagentTomlSchema.parse(smolToml.parse(this.body));
|
|
@@ -46630,6 +47195,10 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46630
47195
|
const paths = this.getSettablePaths({ global });
|
|
46631
47196
|
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
46632
47197
|
const fileContent = await readFileContent(filePath);
|
|
47198
|
+
const resolvedSystemPrompt = await VibeSubagent.readSystemPrompt({
|
|
47199
|
+
outputRoot,
|
|
47200
|
+
fileContent
|
|
47201
|
+
});
|
|
46633
47202
|
const subagent = new VibeSubagent({
|
|
46634
47203
|
outputRoot,
|
|
46635
47204
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -46637,7 +47206,8 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46637
47206
|
body: fileContent.trim(),
|
|
46638
47207
|
fileContent,
|
|
46639
47208
|
validate,
|
|
46640
|
-
global
|
|
47209
|
+
global,
|
|
47210
|
+
...resolvedSystemPrompt !== void 0 && { resolvedSystemPrompt }
|
|
46641
47211
|
});
|
|
46642
47212
|
if (validate) {
|
|
46643
47213
|
const result = subagent.validate();
|
|
@@ -46645,6 +47215,21 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
46645
47215
|
}
|
|
46646
47216
|
return subagent;
|
|
46647
47217
|
}
|
|
47218
|
+
/**
|
|
47219
|
+
* Read `.vibe/prompts/<id>.md` for an agent that names a `system_prompt_id`.
|
|
47220
|
+
* Returns `undefined` when there is no id or the file is missing, in which
|
|
47221
|
+
* case import falls back to the legacy `system_prompt` key.
|
|
47222
|
+
*/
|
|
47223
|
+
static async readSystemPrompt({ outputRoot, fileContent }) {
|
|
47224
|
+
let promptId;
|
|
47225
|
+
try {
|
|
47226
|
+
promptId = VibeSubagentTomlSchema.parse(smolToml.parse(fileContent)).system_prompt_id;
|
|
47227
|
+
} catch {
|
|
47228
|
+
return;
|
|
47229
|
+
}
|
|
47230
|
+
if (typeof promptId !== "string" || promptId === "" || /[\\/]/.test(promptId)) return;
|
|
47231
|
+
return await readFileContentOrNull(join(outputRoot, VIBE_PROMPTS_DIR_PATH, `${promptId}.md`)) ?? void 0;
|
|
47232
|
+
}
|
|
46648
47233
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
46649
47234
|
return new VibeSubagent({
|
|
46650
47235
|
outputRoot,
|
|
@@ -46687,7 +47272,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
46687
47272
|
const mode = super.toRooMode(rulesyncSubagent);
|
|
46688
47273
|
const frontmatter = rulesyncSubagent.getFrontmatter();
|
|
46689
47274
|
const zoocodeSection = isRecord$1(frontmatter.zoocode) ? frontmatter.zoocode : {};
|
|
46690
|
-
if (isStringArray$
|
|
47275
|
+
if (isStringArray$2(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
|
|
46691
47276
|
return mode;
|
|
46692
47277
|
}
|
|
46693
47278
|
toRulesyncSubagents() {
|
|
@@ -46699,7 +47284,7 @@ var ZoocodeSubagent = class extends RooSubagent {
|
|
|
46699
47284
|
targets: ["zoocode"],
|
|
46700
47285
|
roo: restRooSection
|
|
46701
47286
|
};
|
|
46702
|
-
if (isStringArray$
|
|
47287
|
+
if (isStringArray$2(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
|
|
46703
47288
|
return new RulesyncSubagent({
|
|
46704
47289
|
outputRoot: ".",
|
|
46705
47290
|
frontmatter: rebuilt,
|
|
@@ -49602,8 +50187,13 @@ const STRATEGIES = [
|
|
|
49602
50187
|
* file per rule under `.devin/rules/*.md` (Devin Desktop Cascade) with YAML
|
|
49603
50188
|
* frontmatter carrying a `trigger` (always_on | glob | manual | model_decision)
|
|
49604
50189
|
* plus companion `globs`/`description` fields.
|
|
49605
|
-
* - Global scope:
|
|
49606
|
-
*
|
|
50190
|
+
* - Global scope: mirrors the project layout. The root rule is a single
|
|
50191
|
+
* plain-markdown, always-on file (no frontmatter) at
|
|
50192
|
+
* `~/.config/devin/AGENTS.md`; non-root rules are one file per rule under
|
|
50193
|
+
* `~/.devin/rules/*.md` with the same `trigger`/`globs` frontmatter as project
|
|
50194
|
+
* scope. Note the directory split — the per-rule global directory is the home
|
|
50195
|
+
* `~/.devin/`, not the `~/.config/devin/` tree every other Devin global
|
|
50196
|
+
* surface uses. That is what the rules page documents.
|
|
49607
50197
|
*
|
|
49608
50198
|
* Trigger inference (when no explicit devin trigger is persisted):
|
|
49609
50199
|
* - Specific globs (non wildcard) → glob
|
|
@@ -49634,19 +50224,32 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49634
50224
|
relativeFilePath: DEVIN_GLOBAL_AGENTS_FILE_NAME
|
|
49635
50225
|
};
|
|
49636
50226
|
}
|
|
50227
|
+
/**
|
|
50228
|
+
* The always-on root rule file for a scope: the project-root `AGENTS.md`
|
|
50229
|
+
* Devin CLI / Devin Local reads, or `~/.config/devin/AGENTS.md` in global
|
|
50230
|
+
* scope. Single-sourced so `getSettablePaths`, `fromFile` and
|
|
50231
|
+
* `fromRulesyncRule` cannot disagree about it.
|
|
50232
|
+
*/
|
|
50233
|
+
static getRootPath(global, excludeToolDir) {
|
|
50234
|
+
return global ? DevinRule.getGlobalRootPath(excludeToolDir) : {
|
|
50235
|
+
relativeDirPath: ".",
|
|
50236
|
+
relativeFilePath: "AGENTS.md"
|
|
50237
|
+
};
|
|
50238
|
+
}
|
|
49637
50239
|
static getSettablePaths({ global = false, excludeToolDir } = {}) {
|
|
49638
|
-
if (global) return {
|
|
50240
|
+
if (global) return {
|
|
50241
|
+
root: DevinRule.getRootPath(true, excludeToolDir),
|
|
50242
|
+
nonRoot: { relativeDirPath: buildToolPath(DEVIN_DIR, "rules", excludeToolDir) }
|
|
50243
|
+
};
|
|
49639
50244
|
return {
|
|
49640
|
-
root:
|
|
49641
|
-
relativeDirPath: ".",
|
|
49642
|
-
relativeFilePath: "AGENTS.md"
|
|
49643
|
-
},
|
|
50245
|
+
root: DevinRule.getRootPath(false, excludeToolDir),
|
|
49644
50246
|
nonRoot: { relativeDirPath: buildToolPath(DEVIN_DIR, "rules", excludeToolDir) }
|
|
49645
50247
|
};
|
|
49646
50248
|
}
|
|
49647
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
49648
|
-
|
|
49649
|
-
|
|
50249
|
+
static async fromFile({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, validate = true, global = false }) {
|
|
50250
|
+
const nonRootDirPath = buildToolPath(DEVIN_DIR, "rules");
|
|
50251
|
+
if (relativeDirPath !== void 0 ? relativeDirPath !== nonRootDirPath : relativeFilePath === DevinRule.getRootPath(global).relativeFilePath) {
|
|
50252
|
+
const rootPath = DevinRule.getRootPath(global);
|
|
49650
50253
|
const fileContent = await readFileContent(join(outputRoot, rootPath.relativeDirPath, rootPath.relativeFilePath));
|
|
49651
50254
|
return new DevinRule({
|
|
49652
50255
|
outputRoot,
|
|
@@ -49658,19 +50261,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49658
50261
|
root: true
|
|
49659
50262
|
});
|
|
49660
50263
|
}
|
|
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
50264
|
const filePath = join(outputRoot, nonRootDirPath, relativeFilePath);
|
|
49675
50265
|
const { frontmatter, body } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
49676
50266
|
let parsedFrontmatter;
|
|
@@ -49690,8 +50280,9 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49690
50280
|
});
|
|
49691
50281
|
}
|
|
49692
50282
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
49693
|
-
|
|
49694
|
-
|
|
50283
|
+
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
50284
|
+
if (rulesyncFrontmatter.root) {
|
|
50285
|
+
const rootPath = DevinRule.getRootPath(global);
|
|
49695
50286
|
return new DevinRule({
|
|
49696
50287
|
outputRoot,
|
|
49697
50288
|
relativeDirPath: rootPath.relativeDirPath,
|
|
@@ -49702,16 +50293,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49702
50293
|
root: true
|
|
49703
50294
|
});
|
|
49704
50295
|
}
|
|
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
50296
|
const storedDevin = rulesyncFrontmatter.devin;
|
|
49716
50297
|
const normalized = normalizeStoredDevin(storedDevin);
|
|
49717
50298
|
const storedTrigger = storedDevin?.trigger;
|
|
@@ -49771,7 +50352,7 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49771
50352
|
error: null
|
|
49772
50353
|
};
|
|
49773
50354
|
}
|
|
49774
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath
|
|
50355
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
49775
50356
|
return new DevinRule({
|
|
49776
50357
|
outputRoot,
|
|
49777
50358
|
relativeDirPath,
|
|
@@ -49779,7 +50360,7 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
49779
50360
|
frontmatter: {},
|
|
49780
50361
|
body: "",
|
|
49781
50362
|
validate: false,
|
|
49782
|
-
root:
|
|
50363
|
+
root: relativeDirPath !== buildToolPath(DEVIN_DIR, "rules")
|
|
49783
50364
|
});
|
|
49784
50365
|
}
|
|
49785
50366
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
@@ -50412,6 +50993,11 @@ var KimiCodeRule = class KimiCodeRule extends ToolRule {
|
|
|
50412
50993
|
};
|
|
50413
50994
|
//#endregion
|
|
50414
50995
|
//#region src/features/rules/kiro-rule.ts
|
|
50996
|
+
/**
|
|
50997
|
+
* Kiro loads `AGENTS.md` as steering context from any directory in the
|
|
50998
|
+
* workspace tree (CLI 2.18.0 / IDE 1.0.309), not just the workspace root.
|
|
50999
|
+
*/
|
|
51000
|
+
const KIRO_NESTED_STEERING_FILE_NAME = "AGENTS.md";
|
|
50415
51001
|
const WILDCARD_GLOBS = /* @__PURE__ */ new Set([
|
|
50416
51002
|
"**/*",
|
|
50417
51003
|
"**",
|
|
@@ -50485,8 +51071,17 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50485
51071
|
};
|
|
50486
51072
|
return { nonRoot: { relativeDirPath: steeringDirPath } };
|
|
50487
51073
|
}
|
|
50488
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
51074
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
50489
51075
|
const paths = this.getSettablePaths({ global });
|
|
51076
|
+
const steeringDirPath = paths.nonRoot?.relativeDirPath ?? buildToolPath(".kiro", "steering");
|
|
51077
|
+
if (overrideDirPath !== void 0 && overrideDirPath !== steeringDirPath && overrideDirPath !== "." && relativeFilePath === KIRO_NESTED_STEERING_FILE_NAME) return new KiroRule({
|
|
51078
|
+
outputRoot,
|
|
51079
|
+
relativeDirPath: overrideDirPath,
|
|
51080
|
+
relativeFilePath,
|
|
51081
|
+
fileContent: await readFileContent(join(outputRoot, overrideDirPath, relativeFilePath)),
|
|
51082
|
+
validate,
|
|
51083
|
+
root: false
|
|
51084
|
+
});
|
|
50490
51085
|
const isRoot = "root" in paths && relativeFilePath === paths.root.relativeFilePath;
|
|
50491
51086
|
const relativeDirPath = paths.nonRoot?.relativeDirPath ?? buildToolPath(".kiro", "steering");
|
|
50492
51087
|
const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
|
|
@@ -50510,6 +51105,12 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50510
51105
|
});
|
|
50511
51106
|
if (params.root) return new KiroRule(params);
|
|
50512
51107
|
const frontmatter = rulesyncRule.getFrontmatter();
|
|
51108
|
+
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
51109
|
+
if (!global && subprojectPath) return new KiroRule({
|
|
51110
|
+
...params,
|
|
51111
|
+
relativeDirPath: join(subprojectPath),
|
|
51112
|
+
relativeFilePath: KIRO_NESTED_STEERING_FILE_NAME
|
|
51113
|
+
});
|
|
50513
51114
|
const inclusion = deriveKiroInclusion({
|
|
50514
51115
|
kiro: frontmatter.kiro,
|
|
50515
51116
|
globs: frontmatter.globs
|
|
@@ -50520,7 +51121,34 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50520
51121
|
fileContent: stringifyFrontmatter(rulesyncRule.getBody(), inclusion)
|
|
50521
51122
|
});
|
|
50522
51123
|
}
|
|
51124
|
+
/**
|
|
51125
|
+
* The subproject directory a nested `AGENTS.md` scopes, or `undefined` for the
|
|
51126
|
+
* root rule and for the `.kiro/steering/` files.
|
|
51127
|
+
*/
|
|
51128
|
+
getSubprojectPath() {
|
|
51129
|
+
if (this.isRoot() || this.getRelativeFilePath() !== KIRO_NESTED_STEERING_FILE_NAME) return;
|
|
51130
|
+
const relativeDirPath = toPosixPath(this.getRelativeDirPath());
|
|
51131
|
+
if (relativeDirPath === "." || relativeDirPath === "" || relativeDirPath.startsWith(".")) return;
|
|
51132
|
+
return relativeDirPath;
|
|
51133
|
+
}
|
|
50523
51134
|
toRulesyncRule() {
|
|
51135
|
+
const subprojectPath = this.getSubprojectPath();
|
|
51136
|
+
if (subprojectPath !== void 0) {
|
|
51137
|
+
const slug = subprojectPath.replaceAll("/", "-");
|
|
51138
|
+
return new RulesyncRule({
|
|
51139
|
+
outputRoot: this.getOutputRoot(),
|
|
51140
|
+
relativeDirPath: RulesyncRule.getSettablePaths().recommended.relativeDirPath,
|
|
51141
|
+
relativeFilePath: `${slug}-kiro.md`,
|
|
51142
|
+
frontmatter: {
|
|
51143
|
+
targets: ["kiro"],
|
|
51144
|
+
root: false,
|
|
51145
|
+
globs: [`${subprojectPath}/**/*`],
|
|
51146
|
+
agentsmd: { subprojectPath }
|
|
51147
|
+
},
|
|
51148
|
+
body: this.getFileContent(),
|
|
51149
|
+
validate: true
|
|
51150
|
+
});
|
|
51151
|
+
}
|
|
50524
51152
|
const { frontmatter, body } = parseFrontmatter(this.getFileContent(), this.getRelativeFilePath());
|
|
50525
51153
|
const inclusion = typeof frontmatter.inclusion === "string" ? frontmatter.inclusion : void 0;
|
|
50526
51154
|
if (!inclusion) return this.toRulesyncRuleDefault();
|
|
@@ -50553,6 +51181,26 @@ var KiroRule = class KiroRule extends ToolRule {
|
|
|
50553
51181
|
error: null
|
|
50554
51182
|
};
|
|
50555
51183
|
}
|
|
51184
|
+
/**
|
|
51185
|
+
* Nested `AGENTS.md` files anywhere in the workspace tree, which Kiro loads as
|
|
51186
|
+
* steering context alongside `.kiro/steering/`. Import-only, like every other
|
|
51187
|
+
* nested scan: these are hand-authored files outside a rulesync-owned
|
|
51188
|
+
* directory, so enumerating them for `--delete` would sweep away work rulesync
|
|
51189
|
+
* never wrote.
|
|
51190
|
+
* @see https://kiro.dev/docs/steering/
|
|
51191
|
+
*/
|
|
51192
|
+
static getNestedFilePatterns({ outputRoot }) {
|
|
51193
|
+
const root = toPosixPath(outputRoot);
|
|
51194
|
+
return {
|
|
51195
|
+
include: [`${root}/**/${KIRO_NESTED_STEERING_FILE_NAME}`],
|
|
51196
|
+
ignore: [
|
|
51197
|
+
`${root}/${KIRO_NESTED_STEERING_FILE_NAME}`,
|
|
51198
|
+
`${root}/**/.*/**`,
|
|
51199
|
+
...NESTED_SCAN_EXCLUDED_DIRS_ANY_DEPTH.map((dir) => `${root}/**/${dir}/**`),
|
|
51200
|
+
...NESTED_SCAN_EXCLUDED_ROOT_DIRS.map((dir) => `${root}/${dir}/**`)
|
|
51201
|
+
]
|
|
51202
|
+
};
|
|
51203
|
+
}
|
|
50556
51204
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
50557
51205
|
return new KiroRule({
|
|
50558
51206
|
outputRoot,
|
|
@@ -52021,6 +52669,18 @@ const resolveIncludeLocalRoot = (options) => {
|
|
|
52021
52669
|
return parsed.data.includeLocalRoot ?? true;
|
|
52022
52670
|
};
|
|
52023
52671
|
/**
|
|
52672
|
+
* Whether `candidate` is `base` itself or a class that extends it.
|
|
52673
|
+
*
|
|
52674
|
+
* Class-identity dispatch (`factory.class === Base`) is subclass-blind: a target
|
|
52675
|
+
* whose adapter merely narrows another one — `ZoocodeRule extends RooRule` — fails
|
|
52676
|
+
* a strict `===` and silently falls through. That is how `zoocode` ended up
|
|
52677
|
+
* declaring `localRootMode: "separate-local-file"` while never emitting
|
|
52678
|
+
* `AGENTS.local.md`, in both directions and without a warning.
|
|
52679
|
+
*/
|
|
52680
|
+
function isClassOrSubclassOf({ candidate, base }) {
|
|
52681
|
+
return candidate === base || Object.prototype.isPrototypeOf.call(base, candidate);
|
|
52682
|
+
}
|
|
52683
|
+
/**
|
|
52024
52684
|
* Factory Map mapping tool targets to their rule factories.
|
|
52025
52685
|
* Using Map to preserve insertion order for consistent iteration.
|
|
52026
52686
|
*/
|
|
@@ -52374,7 +53034,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
52374
53034
|
meta: {
|
|
52375
53035
|
extension: "md",
|
|
52376
53036
|
supportsGlobal: true,
|
|
52377
|
-
ruleDiscoveryMode: "auto"
|
|
53037
|
+
ruleDiscoveryMode: "auto",
|
|
53038
|
+
localRootMode: "separate-local-file",
|
|
53039
|
+
localRootFileName: "AGENTS.local.md"
|
|
52378
53040
|
}
|
|
52379
53041
|
}],
|
|
52380
53042
|
["zed", {
|
|
@@ -52661,7 +53323,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52661
53323
|
if (rootRule) rootRule.setFileContent(rootRule.getFileContent() + "\n\n" + localRootBody);
|
|
52662
53324
|
}
|
|
52663
53325
|
buildLocalRootFile({ factory, fileName, body, relativeDirPath, localRoot = false }) {
|
|
52664
|
-
if (
|
|
53326
|
+
if (isClassOrSubclassOf({
|
|
53327
|
+
candidate: factory.class,
|
|
53328
|
+
base: ClaudecodeRule
|
|
53329
|
+
})) {
|
|
52665
53330
|
const paths = ClaudecodeRule.getSettablePaths({ global: this.global });
|
|
52666
53331
|
return new ClaudecodeRule({
|
|
52667
53332
|
outputRoot: this.outputRoot,
|
|
@@ -52674,7 +53339,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52674
53339
|
localRoot
|
|
52675
53340
|
});
|
|
52676
53341
|
}
|
|
52677
|
-
if (
|
|
53342
|
+
if (isClassOrSubclassOf({
|
|
53343
|
+
candidate: factory.class,
|
|
53344
|
+
base: ClaudecodeLegacyRule
|
|
53345
|
+
})) {
|
|
52678
53346
|
const paths = ClaudecodeLegacyRule.getSettablePaths({ global: this.global });
|
|
52679
53347
|
return new ClaudecodeLegacyRule({
|
|
52680
53348
|
outputRoot: this.outputRoot,
|
|
@@ -52686,7 +53354,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52686
53354
|
localRoot
|
|
52687
53355
|
});
|
|
52688
53356
|
}
|
|
52689
|
-
if (
|
|
53357
|
+
if (isClassOrSubclassOf({
|
|
53358
|
+
candidate: factory.class,
|
|
53359
|
+
base: RovodevRule
|
|
53360
|
+
})) return new RovodevRule({
|
|
52690
53361
|
outputRoot: this.outputRoot,
|
|
52691
53362
|
relativeDirPath: relativeDirPath ?? ".",
|
|
52692
53363
|
relativeFilePath: fileName,
|
|
@@ -52695,7 +53366,10 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52695
53366
|
root: true,
|
|
52696
53367
|
localRoot
|
|
52697
53368
|
});
|
|
52698
|
-
if (
|
|
53369
|
+
if (isClassOrSubclassOf({
|
|
53370
|
+
candidate: factory.class,
|
|
53371
|
+
base: RooRule
|
|
53372
|
+
})) return new RooRule({
|
|
52699
53373
|
outputRoot: this.outputRoot,
|
|
52700
53374
|
relativeDirPath: relativeDirPath ?? ".",
|
|
52701
53375
|
relativeFilePath: fileName,
|
|
@@ -52704,7 +53378,23 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
52704
53378
|
root: true,
|
|
52705
53379
|
localRoot
|
|
52706
53380
|
});
|
|
52707
|
-
if (
|
|
53381
|
+
if (isClassOrSubclassOf({
|
|
53382
|
+
candidate: factory.class,
|
|
53383
|
+
base: DevinRule
|
|
53384
|
+
})) return new DevinRule({
|
|
53385
|
+
outputRoot: this.outputRoot,
|
|
53386
|
+
relativeDirPath: relativeDirPath ?? ".",
|
|
53387
|
+
relativeFilePath: fileName,
|
|
53388
|
+
frontmatter: {},
|
|
53389
|
+
body,
|
|
53390
|
+
validate: true,
|
|
53391
|
+
root: true,
|
|
53392
|
+
localRoot
|
|
53393
|
+
});
|
|
53394
|
+
if (isClassOrSubclassOf({
|
|
53395
|
+
candidate: factory.class,
|
|
53396
|
+
base: QwencodeRule
|
|
53397
|
+
})) return new QwencodeRule({
|
|
52708
53398
|
outputRoot: this.outputRoot,
|
|
52709
53399
|
relativeDirPath: relativeDirPath ?? ".qwen",
|
|
52710
53400
|
relativeFilePath: fileName,
|
|
@@ -54921,4 +55611,4 @@ async function importChecksCore(params) {
|
|
|
54921
55611
|
//#endregion
|
|
54922
55612
|
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
55613
|
|
|
54924
|
-
//# sourceMappingURL=import-
|
|
55614
|
+
//# sourceMappingURL=import-Dswkc7Ub.js.map
|