rulesync 16.14.0 → 16.15.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/dist/cli/index.cjs +6 -19
- package/dist/cli/index.js +4 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-Ds0QeVp6.js → import-BPTCMtUS.js} +542 -32
- package/dist/import-BPTCMtUS.js.map +1 -0
- package/dist/{import-DsiSzsJH.cjs → import-nfDFI5X1.cjs} +546 -30
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +15 -15
- package/dist/import-Ds0QeVp6.js.map +0 -1
|
@@ -4397,11 +4397,26 @@ const KiloPermissionsOverrideSchema = z.looseObject({
|
|
|
4397
4397
|
* rather than which commands are permitted — so it is a loose passthrough on
|
|
4398
4398
|
* the same terms, merged into the top level of `.claude/settings.json`.
|
|
4399
4399
|
*
|
|
4400
|
+
* Any other key is a plain top-level `settings.json` key (`editorMode`, `env`,
|
|
4401
|
+
* `model`, ...), deep-merged into the generated file verbatim so settings
|
|
4402
|
+
* Claude Code adds faster than an allowlist can track stay authorable.
|
|
4403
|
+
* The exceptions are the keys another feature owns (`hooks`) and `$schema`.
|
|
4404
|
+
* Keys the target file cannot honor — `Managed`-only and `~/.claude.json`-only
|
|
4405
|
+
* keys in either scope, plus user-scope keys at project scope — are dropped
|
|
4406
|
+
* with a warning rather than written where they would never apply. So are the
|
|
4407
|
+
* keys whose value is a command Claude Code executes (`apiKeyHelper`,
|
|
4408
|
+
* `statusLine`, ...): this file is shareable via `rulesync fetch`, and a file
|
|
4409
|
+
* named for restricting things is not where a command belongs — author those
|
|
4410
|
+
* in `.rulesync/hooks.jsonc` instead.
|
|
4411
|
+
*
|
|
4400
4412
|
* @example
|
|
4401
4413
|
* { "permissions": { "defaultMode": "acceptEdits", "additionalDirectories": ["../shared"] } }
|
|
4402
4414
|
* @example
|
|
4403
4415
|
* { "sandbox": { "network": { "allowedDomains": ["example.com"], "strictAllowlist": true } } }
|
|
4416
|
+
* @example
|
|
4417
|
+
* { "editorMode": "vim", "env": { "MY_VAR": "1" } }
|
|
4404
4418
|
* @see https://code.claude.com/docs/en/sandboxing
|
|
4419
|
+
* @see https://code.claude.com/docs/en/settings-reference
|
|
4405
4420
|
*/
|
|
4406
4421
|
const ClaudecodePermissionsOverrideSchema = z.looseObject({
|
|
4407
4422
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
@@ -5076,6 +5091,15 @@ const CodexcliPermissionsOverrideSchema = z.looseObject({
|
|
|
5076
5091
|
sandbox_workspace_write: z.optional(z.looseObject({})),
|
|
5077
5092
|
apps: z.optional(z.looseObject({})),
|
|
5078
5093
|
approvals_reviewer: z.optional(z.union([CodexApprovalsReviewerSchema, z.looseObject({})])),
|
|
5094
|
+
/**
|
|
5095
|
+
* The `[tui]` table of `config.toml` (e.g. `vim_mode_default`, `keymap.*`).
|
|
5096
|
+
* Not a permission surface, but like `apps` it is a top-level table with no
|
|
5097
|
+
* canonical category, and Codex adds keys to it faster than an explicit model
|
|
5098
|
+
* could track — so it is a loose passthrough written verbatim.
|
|
5099
|
+
*
|
|
5100
|
+
* @see https://developers.openai.com/codex/config-reference
|
|
5101
|
+
*/
|
|
5102
|
+
tui: z.optional(z.looseObject({})),
|
|
5079
5103
|
git_write_rules: z.optional(z.boolean())
|
|
5080
5104
|
});
|
|
5081
5105
|
/**
|
|
@@ -7463,7 +7487,8 @@ const CODEXCLI_OVERRIDE_KEYS = [
|
|
|
7463
7487
|
"sandbox_mode",
|
|
7464
7488
|
"sandbox_workspace_write",
|
|
7465
7489
|
"apps",
|
|
7466
|
-
"approvals_reviewer"
|
|
7490
|
+
"approvals_reviewer",
|
|
7491
|
+
"tui"
|
|
7467
7492
|
];
|
|
7468
7493
|
//#endregion
|
|
7469
7494
|
//#region src/features/shared/shared-config-gateway.ts
|
|
@@ -29380,6 +29405,27 @@ function convertAugmentToRulesyncPermissions({ entries, logger }) {
|
|
|
29380
29405
|
return { permission };
|
|
29381
29406
|
}
|
|
29382
29407
|
//#endregion
|
|
29408
|
+
//#region src/utils/control-characters.ts
|
|
29409
|
+
/**
|
|
29410
|
+
* Matches C0 controls, DEL, the C1 range (which includes the 8-bit CSI
|
|
29411
|
+
* introducer U+009B), the bidirectional overrides and isolates, and the Unicode
|
|
29412
|
+
* line and paragraph separators, and the plain LRM/RLM marks. A name or value
|
|
29413
|
+
* copied out of an untrusted config file, a fetched repository, or a tool's own
|
|
29414
|
+
* settings file must never reach the terminal with these intact: they let the
|
|
29415
|
+
* text forge log lines, reorder what is printed around them, or inject escape
|
|
29416
|
+
* sequences. LRM/RLM open no bidi scope of their own, but they still reorder the
|
|
29417
|
+
* neutral characters beside them, so they go too — a diagnostic line is not the
|
|
29418
|
+
* place to preserve the typography of a right-to-left name.
|
|
29419
|
+
*/
|
|
29420
|
+
const CONTROL_CHARACTERS_PATTERN = /[\u0000-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069\u2028\u2029]/g;
|
|
29421
|
+
/**
|
|
29422
|
+
* Removes every control character from `text` so it is safe to splice into a
|
|
29423
|
+
* log line or other terminal output.
|
|
29424
|
+
*/
|
|
29425
|
+
function stripControlCharacters(text) {
|
|
29426
|
+
return text.replace(CONTROL_CHARACTERS_PATTERN, "");
|
|
29427
|
+
}
|
|
29428
|
+
//#endregion
|
|
29383
29429
|
//#region src/features/permissions/claudecode-permissions.ts
|
|
29384
29430
|
/**
|
|
29385
29431
|
* Mapping from rulesync canonical tool category names (lowercase) to Claude Code tool names (PascalCase).
|
|
@@ -29464,8 +29510,9 @@ function deepMergeRecords(base, patch) {
|
|
|
29464
29510
|
* emits them only under `--global`.
|
|
29465
29511
|
*
|
|
29466
29512
|
* Deliberately NOT listed:
|
|
29467
|
-
* - `bwrapPath` / `socatPath`:
|
|
29468
|
-
*
|
|
29513
|
+
* - `ripgrep` / `bwrapPath` / `socatPath`: each names an executable, so
|
|
29514
|
+
* `stripCommandExecutingSandboxPaths` refuses them in both scopes rather than
|
|
29515
|
+
* emitting them under `--global`.
|
|
29469
29516
|
* - `credentials.envVars` / `credentials.files`: the ignored-at-project-scope
|
|
29470
29517
|
* unit is the individual entry's mode, not the settings key, and the same
|
|
29471
29518
|
* lists carry `deny` entries that project settings *do* honor — dropping a
|
|
@@ -29473,7 +29520,6 @@ function deepMergeRecords(base, patch) {
|
|
|
29473
29520
|
* filters those lists per entry instead.
|
|
29474
29521
|
*
|
|
29475
29522
|
* @see https://code.claude.com/docs/en/sandboxing
|
|
29476
|
-
* @see https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md — v2.1.232 scoped `sandbox.ripgrep`
|
|
29477
29523
|
*/
|
|
29478
29524
|
const CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS = [
|
|
29479
29525
|
["filesystem", "disabled"],
|
|
@@ -29482,10 +29528,228 @@ const CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS = [
|
|
|
29482
29528
|
["credentials", "allowPlaintextInject"],
|
|
29483
29529
|
["credentials", "awsPairs"],
|
|
29484
29530
|
["credentials", "sigv4"],
|
|
29485
|
-
["allowAppleEvents"]
|
|
29486
|
-
|
|
29531
|
+
["allowAppleEvents"]
|
|
29532
|
+
];
|
|
29533
|
+
/**
|
|
29534
|
+
* Walks `segments` from `root`, returning the record they name or `undefined` if
|
|
29535
|
+
* any step is missing or not a record. Shared by everything below that addresses
|
|
29536
|
+
* a `sandbox` path, so a nested path added to one of the tables is actually
|
|
29537
|
+
* traversed rather than silently skipped.
|
|
29538
|
+
*/
|
|
29539
|
+
function resolveSandboxParent({ root, segments }) {
|
|
29540
|
+
let parent = root;
|
|
29541
|
+
for (const segment of segments) {
|
|
29542
|
+
const next = parent[segment];
|
|
29543
|
+
if (!isPlainRecord(next)) return void 0;
|
|
29544
|
+
parent = next;
|
|
29545
|
+
}
|
|
29546
|
+
return parent;
|
|
29547
|
+
}
|
|
29548
|
+
/**
|
|
29549
|
+
* Deletes `path` from `target` in place and reports whether anything was there,
|
|
29550
|
+
* dropping a container the removal emptied so no `"network": {}` noise is left
|
|
29551
|
+
* behind.
|
|
29552
|
+
*/
|
|
29553
|
+
function deleteSandboxPath({ target, path }) {
|
|
29554
|
+
const leaf = path.at(-1);
|
|
29555
|
+
if (leaf === void 0) return false;
|
|
29556
|
+
const parentPath = path.slice(0, -1);
|
|
29557
|
+
const parent = resolveSandboxParent({
|
|
29558
|
+
root: target,
|
|
29559
|
+
segments: parentPath
|
|
29560
|
+
});
|
|
29561
|
+
if (parent === void 0 || parent[leaf] === void 0) return false;
|
|
29562
|
+
delete parent[leaf];
|
|
29563
|
+
for (let depth = parentPath.length; depth > 0; depth--) {
|
|
29564
|
+
const container = resolveSandboxParent({
|
|
29565
|
+
root: target,
|
|
29566
|
+
segments: parentPath.slice(0, depth)
|
|
29567
|
+
});
|
|
29568
|
+
if (container === void 0 || Object.keys(container).length > 0) break;
|
|
29569
|
+
const holder = resolveSandboxParent({
|
|
29570
|
+
root: target,
|
|
29571
|
+
segments: parentPath.slice(0, depth - 1)
|
|
29572
|
+
});
|
|
29573
|
+
const name = parentPath[depth - 1];
|
|
29574
|
+
if (holder === void 0 || name === void 0) break;
|
|
29575
|
+
delete holder[name];
|
|
29576
|
+
}
|
|
29577
|
+
return true;
|
|
29578
|
+
}
|
|
29579
|
+
/**
|
|
29580
|
+
* The `permissions.defaultMode` values that start a session with fewer prompts
|
|
29581
|
+
* than the default. `plan` and `default` are absent because they do not widen
|
|
29582
|
+
* anything.
|
|
29583
|
+
*/
|
|
29584
|
+
const CLAUDECODE_WIDENING_DEFAULT_MODES = {
|
|
29585
|
+
acceptEdits: "every file edit is then applied without a prompt",
|
|
29586
|
+
auto: "shell commands are then auto-approved by a classifier rather than by you",
|
|
29587
|
+
bypassPermissions: "every session then starts with no permission prompts at all"
|
|
29588
|
+
};
|
|
29589
|
+
/**
|
|
29590
|
+
* The `permissions` fields that widen rather than restrict: a `defaultMode` that
|
|
29591
|
+
* removes prompts, and `additionalDirectories`, which moves the
|
|
29592
|
+
* working-directory boundary. Warned for the same reason `disableAllHooks` is: a
|
|
29593
|
+
* shareable permissions file should not loosen the permission system quietly.
|
|
29594
|
+
*/
|
|
29595
|
+
function warnOnWideningPermissionFields({ fields, relativeFilePath, logger }) {
|
|
29596
|
+
const defaultMode = fields.defaultMode;
|
|
29597
|
+
if (typeof defaultMode === "string" && Object.hasOwn(CLAUDECODE_WIDENING_DEFAULT_MODES, defaultMode)) logger?.warn(`Claude Code permissions: writing 'permissions.defaultMode: "${defaultMode}"' to ${relativeFilePath}; ${CLAUDECODE_WIDENING_DEFAULT_MODES[defaultMode]}. Review it as you would a hook, especially if this permissions file came from 'rulesync fetch'.`);
|
|
29598
|
+
const additionalDirectories = fields.additionalDirectories;
|
|
29599
|
+
if (additionalDirectories !== void 0 && !(Array.isArray(additionalDirectories) && additionalDirectories.length === 0)) logger?.warn(`Claude Code permissions: writing 'permissions.additionalDirectories' to ${relativeFilePath}; it moves the boundary of what Claude Code may read and edit outside the project. Review the paths, especially if this permissions file came from 'rulesync fetch'.`);
|
|
29600
|
+
}
|
|
29601
|
+
/**
|
|
29602
|
+
* `sandbox` paths whose value names a binary Claude Code runs. `sandbox` has its
|
|
29603
|
+
* own merge branch, so the top-level refusal in `stripUnhonoredTopLevelKeys`
|
|
29604
|
+
* never sees them — they are refused here on the same grounds, in both scopes:
|
|
29605
|
+
* a fetched `.rulesync/permissions.jsonc` must not be able to point Claude Code
|
|
29606
|
+
* at an executable of its choosing.
|
|
29607
|
+
*
|
|
29608
|
+
* @see https://code.claude.com/docs/en/sandboxing
|
|
29609
|
+
*/
|
|
29610
|
+
const CLAUDECODE_COMMAND_EXECUTING_SANDBOX_PATHS = [
|
|
29611
|
+
["ripgrep"],
|
|
29612
|
+
["bwrapPath"],
|
|
29613
|
+
["socatPath"]
|
|
29614
|
+
];
|
|
29615
|
+
/**
|
|
29616
|
+
* `sandbox` paths that loosen the sandbox rather than naming something to run:
|
|
29617
|
+
* they let commands out of it, weaken the isolation it provides, or redirect
|
|
29618
|
+
* where its traffic goes. They are written like `env` is — the ordinary uses are
|
|
29619
|
+
* too common to refuse — but never silently, because a fetched override should
|
|
29620
|
+
* not be able to open the sandbox without saying so. `widens` keeps the warning
|
|
29621
|
+
* to the value that actually loosens the policy, so authoring the restrictive
|
|
29622
|
+
* value (`allowUnsandboxedCommands: false`, an empty `excludedCommands`) stays
|
|
29623
|
+
* quiet. The `allow*` lists are here for a structural reason: Claude Code merges
|
|
29624
|
+
* a list across every settings scope rather than replacing it, so a project file
|
|
29625
|
+
* can only ever add to them. Their counterparts — `denyRead`, `denyWrite`,
|
|
29626
|
+
* `deniedDomains` — merge the same way, but adding to a deny list only ever
|
|
29627
|
+
* narrows the policy, so they are absent.
|
|
29628
|
+
*
|
|
29629
|
+
* @see https://code.claude.com/docs/en/sandboxing
|
|
29630
|
+
*/
|
|
29631
|
+
const CLAUDECODE_TRUST_AFFECTING_SANDBOX_PATHS = [
|
|
29632
|
+
{
|
|
29633
|
+
path: ["allowAppleEvents"],
|
|
29634
|
+
reason: "lets sandboxed commands send Apple Events, which removes code-execution isolation",
|
|
29635
|
+
widens: (value) => value === true
|
|
29636
|
+
},
|
|
29637
|
+
{
|
|
29638
|
+
path: ["allowUnsandboxedCommands"],
|
|
29639
|
+
reason: "controls whether Claude may retry a blocked command outside the sandbox",
|
|
29640
|
+
widens: (value) => value !== false
|
|
29641
|
+
},
|
|
29642
|
+
{
|
|
29643
|
+
path: ["autoAllowBashIfSandboxed"],
|
|
29644
|
+
reason: "controls whether every Bash command the sandbox accepts runs without a prompt",
|
|
29645
|
+
widens: (value) => value !== false
|
|
29646
|
+
},
|
|
29647
|
+
{
|
|
29648
|
+
path: ["enableWeakerNestedSandbox"],
|
|
29649
|
+
reason: "runs the Linux sandbox inside an unprivileged container, which weakens it",
|
|
29650
|
+
widens: (value) => value === true
|
|
29651
|
+
},
|
|
29652
|
+
{
|
|
29653
|
+
path: ["enableWeakerNetworkIsolation"],
|
|
29654
|
+
reason: "weakens the sandbox's network isolation on macOS",
|
|
29655
|
+
widens: (value) => value === true
|
|
29656
|
+
},
|
|
29657
|
+
{
|
|
29658
|
+
path: ["enabled"],
|
|
29659
|
+
reason: "turns the sandbox on, and sandboxed Bash commands then run without a permission prompt unless `autoAllowBashIfSandboxed` is false",
|
|
29660
|
+
widens: (value) => value === true
|
|
29661
|
+
},
|
|
29662
|
+
{
|
|
29663
|
+
path: ["excludedCommands"],
|
|
29664
|
+
reason: "names commands that always run outside the sandbox, with no sandbox policy applied",
|
|
29665
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29666
|
+
},
|
|
29667
|
+
{
|
|
29668
|
+
path: ["filesystem", "allowRead"],
|
|
29669
|
+
reason: "re-opens reading inside a region the sandbox's `denyRead` blocks",
|
|
29670
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29671
|
+
},
|
|
29672
|
+
{
|
|
29673
|
+
path: ["filesystem", "allowWrite"],
|
|
29674
|
+
reason: "adds paths sandboxed commands may write to, outside the working directory",
|
|
29675
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29676
|
+
},
|
|
29677
|
+
{
|
|
29678
|
+
path: ["ignoreViolations"],
|
|
29679
|
+
reason: "hides the sandbox violations it names, so a blocked access stops being reported",
|
|
29680
|
+
widens: (value) => isPlainRecord(value) ? Object.keys(value).length > 0 : value !== false
|
|
29681
|
+
},
|
|
29682
|
+
{
|
|
29683
|
+
path: ["network", "allowAllUnixSockets"],
|
|
29684
|
+
reason: "lets sandboxed commands connect to every Unix socket",
|
|
29685
|
+
widens: (value) => value === true
|
|
29686
|
+
},
|
|
29687
|
+
{
|
|
29688
|
+
path: ["network", "allowedDomains"],
|
|
29689
|
+
reason: "pre-allows domains sandboxed commands may reach without a prompt",
|
|
29690
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29691
|
+
},
|
|
29692
|
+
{
|
|
29693
|
+
path: ["network", "allowLocalBinding"],
|
|
29694
|
+
reason: "lets sandboxed commands bind local ports",
|
|
29695
|
+
widens: (value) => value === true
|
|
29696
|
+
},
|
|
29697
|
+
{
|
|
29698
|
+
path: ["network", "allowMachLookup"],
|
|
29699
|
+
reason: "names the macOS services sandboxed commands may reach, and `*` means every service",
|
|
29700
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29701
|
+
},
|
|
29702
|
+
{
|
|
29703
|
+
path: ["network", "allowUnixSockets"],
|
|
29704
|
+
reason: "names Unix sockets sandboxed commands may reach, and one such as `/var/run/docker.sock` is host access",
|
|
29705
|
+
widens: (value) => !Array.isArray(value) || value.length > 0
|
|
29706
|
+
},
|
|
29707
|
+
{
|
|
29708
|
+
path: ["network", "httpProxyPort"],
|
|
29709
|
+
reason: "routes the sandbox's HTTP traffic through the port it names",
|
|
29710
|
+
widens: () => true
|
|
29711
|
+
},
|
|
29712
|
+
{
|
|
29713
|
+
path: ["network", "socksProxyPort"],
|
|
29714
|
+
reason: "routes the sandbox's SOCKS traffic through the port it names",
|
|
29715
|
+
widens: () => true
|
|
29716
|
+
}
|
|
29487
29717
|
];
|
|
29488
29718
|
/**
|
|
29719
|
+
* Warns once per authored `sandbox` path that loosens the sandbox. Nothing is
|
|
29720
|
+
* removed — the value is written, just not silently. Called on the filtered
|
|
29721
|
+
* `sandbox` so it never claims to be writing a path the scope filters dropped.
|
|
29722
|
+
*/
|
|
29723
|
+
function warnOnTrustAffectingSandboxPaths({ sandbox, relativeFilePath, logger }) {
|
|
29724
|
+
for (const { path, reason, widens } of CLAUDECODE_TRUST_AFFECTING_SANDBOX_PATHS) {
|
|
29725
|
+
const leaf = path.at(-1);
|
|
29726
|
+
if (leaf === void 0) continue;
|
|
29727
|
+
const parent = resolveSandboxParent({
|
|
29728
|
+
root: sandbox,
|
|
29729
|
+
segments: path.slice(0, -1)
|
|
29730
|
+
});
|
|
29731
|
+
if (parent === void 0) continue;
|
|
29732
|
+
const value = parent[leaf];
|
|
29733
|
+
if (value === void 0 || !widens(value)) continue;
|
|
29734
|
+
logger?.warn(`Claude Code permissions: writing 'sandbox.${path.join(".")}' to ${relativeFilePath}; it ${reason}. Review the value as you would a hook, especially if this permissions file came from 'rulesync fetch'.`);
|
|
29735
|
+
}
|
|
29736
|
+
}
|
|
29737
|
+
/**
|
|
29738
|
+
* Copy of the authored `sandbox` override with the paths that name an
|
|
29739
|
+
* executable removed, warning once per dropped path.
|
|
29740
|
+
*/
|
|
29741
|
+
function stripCommandExecutingSandboxPaths({ sandbox, relativeFilePath, logger }) {
|
|
29742
|
+
const filtered = structuredClone(sandbox);
|
|
29743
|
+
for (const path of CLAUDECODE_COMMAND_EXECUTING_SANDBOX_PATHS) {
|
|
29744
|
+
if (!deleteSandboxPath({
|
|
29745
|
+
target: filtered,
|
|
29746
|
+
path
|
|
29747
|
+
})) continue;
|
|
29748
|
+
logger?.warn(`Claude Code permissions: 'sandbox.${path.join(".")}' names an executable Claude Code runs, so rulesync does not write it to ${relativeFilePath}. A permissions file is shareable — 'rulesync fetch' copies one into a project — and is not where a reviewer looks for a command to run; set this path in ${relativeFilePath} by hand.`);
|
|
29749
|
+
}
|
|
29750
|
+
return filtered;
|
|
29751
|
+
}
|
|
29752
|
+
/**
|
|
29489
29753
|
* Copy of the authored `sandbox` override with the user/managed-only paths
|
|
29490
29754
|
* removed, warning once per dropped path. Only the override copy is filtered —
|
|
29491
29755
|
* a value already hand-written in the target file is left untouched, matching
|
|
@@ -29494,24 +29758,10 @@ const CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS = [
|
|
|
29494
29758
|
function stripGlobalOnlySandboxPaths({ sandbox, relativeFilePath, logger }) {
|
|
29495
29759
|
const filtered = structuredClone(sandbox);
|
|
29496
29760
|
for (const path of CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS) {
|
|
29497
|
-
|
|
29498
|
-
|
|
29499
|
-
|
|
29500
|
-
|
|
29501
|
-
for (const segment of parentPath) {
|
|
29502
|
-
const next = parent[segment];
|
|
29503
|
-
if (!isPlainRecord(next)) {
|
|
29504
|
-
parent = {};
|
|
29505
|
-
break;
|
|
29506
|
-
}
|
|
29507
|
-
parent = next;
|
|
29508
|
-
}
|
|
29509
|
-
if (parent[leaf] === void 0) continue;
|
|
29510
|
-
delete parent[leaf];
|
|
29511
|
-
const [container] = parentPath;
|
|
29512
|
-
if (container !== void 0 && isPlainRecord(filtered[container])) {
|
|
29513
|
-
if (Object.keys(filtered[container]).length === 0) delete filtered[container];
|
|
29514
|
-
}
|
|
29761
|
+
if (!deleteSandboxPath({
|
|
29762
|
+
target: filtered,
|
|
29763
|
+
path
|
|
29764
|
+
})) continue;
|
|
29515
29765
|
logger?.warn(`Claude Code permissions: 'sandbox.${path.join(".")}' is only honored in user/managed/--settings settings, so it is not written to the project-scoped ${relativeFilePath}. Author it in the global scope instead, and check that file for a stale value an earlier generate may have left there.`);
|
|
29516
29766
|
}
|
|
29517
29767
|
return filtered;
|
|
@@ -29567,6 +29817,215 @@ function stripProjectIgnoredMaskEntries({ sandbox, relativeFilePath, logger }) {
|
|
|
29567
29817
|
else filtered.credentials = filteredCredentials;
|
|
29568
29818
|
return filtered;
|
|
29569
29819
|
}
|
|
29820
|
+
/**
|
|
29821
|
+
* Top-level `.claude/settings.json` keys another feature owns, derived from
|
|
29822
|
+
* {@link SHARED_CONFIG_OWNERSHIP} rather than restated here so a feature that
|
|
29823
|
+
* starts owning a new key is excluded from the passthrough automatically
|
|
29824
|
+
* (today: `hooks`, from the hooks feature). Only `replace-owned-keys` entries
|
|
29825
|
+
* name keys; the `custom` policies on this file (`ignore`, `permissions`) own
|
|
29826
|
+
* entries *inside* `permissions`, which the passthrough excludes wholesale.
|
|
29827
|
+
*/
|
|
29828
|
+
const CLAUDECODE_FEATURE_OWNED_SETTINGS_KEYS = Object.entries(SHARED_CONFIG_OWNERSHIP[".claude/settings.json"]?.features ?? {}).flatMap(([feature, policy]) => feature !== "permissions" && policy.kind === "replace-owned-keys" ? policy.ownedKeys : []);
|
|
29829
|
+
/**
|
|
29830
|
+
* Top-level `.claude/settings.json` keys the generic `claudecode` override
|
|
29831
|
+
* passthrough must not carry. `permissions` and `sandbox` have their own merge
|
|
29832
|
+
* branches (the managed `allow`/`ask`/`deny` arrays and the scope filtering
|
|
29833
|
+
* respectively), `permission` is rulesync's own canonical tool-scoped block
|
|
29834
|
+
* rather than a settings key, `$schema` is an editor pointer rather than a
|
|
29835
|
+
* Claude Code setting, and the rest belong to the other features writing this
|
|
29836
|
+
* shared file.
|
|
29837
|
+
*/
|
|
29838
|
+
const CLAUDECODE_NON_PASSTHROUGH_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
|
|
29839
|
+
"permission",
|
|
29840
|
+
"permissions",
|
|
29841
|
+
"sandbox",
|
|
29842
|
+
"$schema",
|
|
29843
|
+
...CLAUDECODE_FEATURE_OWNED_SETTINGS_KEYS
|
|
29844
|
+
]);
|
|
29845
|
+
/**
|
|
29846
|
+
* Top-level settings keys Claude Code reads only from user settings, managed
|
|
29847
|
+
* settings and the `--settings` CLI flag — the same restriction
|
|
29848
|
+
* `CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS` records for the `sandbox` subtree, and
|
|
29849
|
+
* the reason the passthrough drops them at project scope instead of committing
|
|
29850
|
+
* a setting that never applies. `rulesync generate --global` writes the user
|
|
29851
|
+
* settings file, so they are emitted there.
|
|
29852
|
+
*
|
|
29853
|
+
* Derived from the per-key **Scope** column of the settings reference: every
|
|
29854
|
+
* top-level key documented as `User or managed` or `User, local, or managed`.
|
|
29855
|
+
*
|
|
29856
|
+
* @see https://code.claude.com/docs/en/settings-reference
|
|
29857
|
+
*/
|
|
29858
|
+
const CLAUDECODE_USER_SCOPE_ONLY_KEYS = /* @__PURE__ */ new Set([
|
|
29859
|
+
"askUserQuestionTimeout",
|
|
29860
|
+
"autoMode",
|
|
29861
|
+
"dialogExpiry",
|
|
29862
|
+
"enableArtifact",
|
|
29863
|
+
"footerLinksRegexes",
|
|
29864
|
+
"pluginConfigs",
|
|
29865
|
+
"skipAutoPermissionPrompt",
|
|
29866
|
+
"skipDangerousModePermissionPrompt",
|
|
29867
|
+
"spellcheck",
|
|
29868
|
+
"sshConfigs",
|
|
29869
|
+
"syncClaudeAiSkills",
|
|
29870
|
+
"useAutoModeDuringPlan",
|
|
29871
|
+
"vimInsertModeRemaps"
|
|
29872
|
+
]);
|
|
29873
|
+
/**
|
|
29874
|
+
* Top-level settings keys neither file rulesync writes can honor, with the file
|
|
29875
|
+
* that does. `Managed` keys are read only from the settings file an organization
|
|
29876
|
+
* deploys, and `Global config` keys only from `~/.claude.json` — rulesync writes
|
|
29877
|
+
* `.claude/settings.json` and `~/.claude/settings.json`, so authoring either
|
|
29878
|
+
* kind through the override would produce a policy that silently never applies.
|
|
29879
|
+
*
|
|
29880
|
+
* Derived from the per-key **Scope** column of the settings reference.
|
|
29881
|
+
*
|
|
29882
|
+
* @see https://code.claude.com/docs/en/settings-reference
|
|
29883
|
+
*/
|
|
29884
|
+
const CLAUDECODE_UNHONORED_KEY_SOURCES = {
|
|
29885
|
+
allowAllClaudeAiMcps: "managed settings",
|
|
29886
|
+
allowedChannelPlugins: "managed settings",
|
|
29887
|
+
allowManagedHooksOnly: "managed settings",
|
|
29888
|
+
allowManagedMcpServersOnly: "managed settings",
|
|
29889
|
+
allowManagedPermissionRulesOnly: "managed settings",
|
|
29890
|
+
autoConnectIde: "~/.claude.json",
|
|
29891
|
+
autoInstallIdeExtension: "~/.claude.json",
|
|
29892
|
+
blockedMarketplaces: "managed settings",
|
|
29893
|
+
browserExternalPageTools: "managed settings",
|
|
29894
|
+
channelsEnabled: "managed settings",
|
|
29895
|
+
claudeMd: "managed settings",
|
|
29896
|
+
diffTool: "~/.claude.json",
|
|
29897
|
+
disableBrowserExternalNavigation: "managed settings",
|
|
29898
|
+
disableCommandPluginSources: "managed settings",
|
|
29899
|
+
disableMobileSimulatorTools: "managed settings",
|
|
29900
|
+
disableSideloadFlags: "managed settings",
|
|
29901
|
+
externalEditorContext: "~/.claude.json",
|
|
29902
|
+
forceLoginGatewayUrl: "managed settings",
|
|
29903
|
+
forceRemoteSettingsRefresh: "managed settings",
|
|
29904
|
+
parentSettingsBehavior: "managed settings",
|
|
29905
|
+
permissionExplainerEnabled: "~/.claude.json",
|
|
29906
|
+
pluginSuggestionMarketplaces: "managed settings",
|
|
29907
|
+
pluginTrustMessage: "managed settings",
|
|
29908
|
+
requiredMaximumVersion: "managed settings",
|
|
29909
|
+
requiredMinimumVersion: "managed settings",
|
|
29910
|
+
sshHostAllowlist: "managed settings",
|
|
29911
|
+
strictKnownMarketplaces: "managed settings",
|
|
29912
|
+
strictPluginOnlyCustomization: "managed settings",
|
|
29913
|
+
teammateDefaultModel: "~/.claude.json",
|
|
29914
|
+
wslInheritsWindowsSettings: "managed settings"
|
|
29915
|
+
};
|
|
29916
|
+
/**
|
|
29917
|
+
* Top-level settings keys whose value Claude Code **executes**. The generic
|
|
29918
|
+
* passthrough refuses them outright rather than warning: `.rulesync/*` files
|
|
29919
|
+
* are shareable — `rulesync fetch` copies a third party's `permissions.jsonc`
|
|
29920
|
+
* straight into a project — and a file named for *restricting* what an agent
|
|
29921
|
+
* may do is not somewhere a reviewer looks for a command to run. Commands
|
|
29922
|
+
* belong in `.rulesync/hooks.jsonc` and `.rulesync/.mcp.json`, which are read
|
|
29923
|
+
* as executable by anyone reviewing them. Set these by hand in the settings
|
|
29924
|
+
* file if you need them.
|
|
29925
|
+
*
|
|
29926
|
+
* The value is the reason, spliced into the warning.
|
|
29927
|
+
*
|
|
29928
|
+
* @see https://code.claude.com/docs/en/settings-reference
|
|
29929
|
+
*/
|
|
29930
|
+
const CLAUDECODE_COMMAND_EXECUTING_KEYS = {
|
|
29931
|
+
apiKeyHelper: "runs the script it names to mint an API key",
|
|
29932
|
+
awsAuthRefresh: "runs the command it names to refresh AWS credentials",
|
|
29933
|
+
awsCredentialExport: "runs the command it names to export AWS credentials",
|
|
29934
|
+
fileSuggestion: "runs its `command` on every `@` file completion",
|
|
29935
|
+
gcpAuthRefresh: "runs the command it names to refresh Google Cloud credentials",
|
|
29936
|
+
otelHeadersHelper: "runs the script it names to build OpenTelemetry headers",
|
|
29937
|
+
policyHelper: "runs the executable it names to compute the managed settings",
|
|
29938
|
+
processWrapper: "wraps every process Claude Code spawns",
|
|
29939
|
+
statusLine: "runs its `command` on every status-line render",
|
|
29940
|
+
subagentStatusLine: "runs its `command` on every subagent status row"
|
|
29941
|
+
};
|
|
29942
|
+
/**
|
|
29943
|
+
* Top-level settings keys the passthrough does write, but never silently: each
|
|
29944
|
+
* one widens what Claude Code trusts or where it sends data, so a value that
|
|
29945
|
+
* arrived with a fetched `.rulesync/permissions.jsonc` should be looked at
|
|
29946
|
+
* deliberately. Warning on write follows the precedent set for Warp's
|
|
29947
|
+
* `command_denylist`, which also replaces a protection when rulesync writes it.
|
|
29948
|
+
*
|
|
29949
|
+
* The value is the reason, spliced into the warning.
|
|
29950
|
+
*/
|
|
29951
|
+
const CLAUDECODE_TRUST_AFFECTING_KEYS = {
|
|
29952
|
+
agent: "starts every session as the named subagent, with that subagent's prompt, tools and model",
|
|
29953
|
+
allowedHttpHookUrls: "limits which URLs an HTTP hook may target, and an empty list means every URL",
|
|
29954
|
+
allowedMcpServers: "allowlists the MCP servers that may be used, and entries from every settings file merge into one list, so an entry here widens an allowlist deployed elsewhere",
|
|
29955
|
+
autoMode: "auto-approves shell commands with a classifier rather than with a prompt",
|
|
29956
|
+
disableAllHooks: "controls whether hooks run at all",
|
|
29957
|
+
disableSkillShellExecution: "re-opens the inline shell commands in a skill or custom command that a user setting had turned off",
|
|
29958
|
+
enableAllProjectMcpServers: "auto-approves every server in the project `.mcp.json`",
|
|
29959
|
+
enabledMcpjsonServers: "auto-approves the named servers in the project `.mcp.json`",
|
|
29960
|
+
enabledPlugins: "enables plugins, which can ship their own hooks",
|
|
29961
|
+
env: "sets environment variables for every process Claude Code spawns, so a value such as `NODE_OPTIONS` or `PATH` runs code and `ANTHROPIC_BASE_URL` redirects every prompt",
|
|
29962
|
+
extraKnownMarketplaces: "registers plugin marketplace sources",
|
|
29963
|
+
httpHookAllowedEnvVars: "controls which environment variables an HTTP hook may put in a request header, credentials included",
|
|
29964
|
+
outputStyle: "replaces the system prompt every session runs with",
|
|
29965
|
+
skipAutoPermissionPrompt: "removes the confirmation shown before auto-approval mode starts",
|
|
29966
|
+
skipDangerousModePermissionPrompt: "removes the confirmation shown before the mode that skips every permission check starts"
|
|
29967
|
+
};
|
|
29968
|
+
/**
|
|
29969
|
+
* The keys from the table above that only widen at one particular value.
|
|
29970
|
+
* `disableSkillShellExecution: true` turns inline shell execution off, which
|
|
29971
|
+
* restricts; the `false` that turns it back on is what a fetched override could
|
|
29972
|
+
* use to undo a user setting, so only that value is warned about.
|
|
29973
|
+
*/
|
|
29974
|
+
const CLAUDECODE_TRUST_KEY_WIDENING_VALUES = { disableSkillShellExecution: (value) => value === false };
|
|
29975
|
+
/**
|
|
29976
|
+
* A key name is authored data that ends up in a log line, so strip the control
|
|
29977
|
+
* characters that would let it forge a line or hide the warnings beside it, and
|
|
29978
|
+
* cap the length.
|
|
29979
|
+
*/
|
|
29980
|
+
function displayKey(key) {
|
|
29981
|
+
const stripped = stripControlCharacters(key);
|
|
29982
|
+
return stripped.length > 80 ? `${stripped.slice(0, 80)}…` : stripped;
|
|
29983
|
+
}
|
|
29984
|
+
/**
|
|
29985
|
+
* Alternate spellings Claude Code accepts for a top-level settings key, mapped
|
|
29986
|
+
* to the canonical key whose **Scope** the alias inherits: "In any settings
|
|
29987
|
+
* file that accepts the canonical key, Claude Code reads the alias exactly as
|
|
29988
|
+
* it reads the canonical key." Resolving through this map before the scope
|
|
29989
|
+
* check keeps an alias from slipping past a restriction its canonical spelling
|
|
29990
|
+
* is caught by — `allowedMarketplaces` is `Managed`, like
|
|
29991
|
+
* `strictKnownMarketplaces`. Both aliases require Claude Code v2.1.232+.
|
|
29992
|
+
*
|
|
29993
|
+
* @see https://code.claude.com/docs/en/settings-reference#marketplace-key-aliases
|
|
29994
|
+
*/
|
|
29995
|
+
const CLAUDECODE_SETTINGS_KEY_ALIASES = {
|
|
29996
|
+
additionalMarketplaces: "extraKnownMarketplaces",
|
|
29997
|
+
allowedMarketplaces: "strictKnownMarketplaces"
|
|
29998
|
+
};
|
|
29999
|
+
/**
|
|
30000
|
+
* Copy of the authored top-level passthrough with the keys the target file
|
|
30001
|
+
* cannot honor removed, warning once per dropped key. Like
|
|
30002
|
+
* `stripGlobalOnlySandboxPaths`, only the override copy is filtered — a value
|
|
30003
|
+
* already hand-written in the target file is left untouched, which is why the
|
|
30004
|
+
* warning points at it.
|
|
30005
|
+
*/
|
|
30006
|
+
function stripUnhonoredTopLevelKeys({ overrides, global, relativeFilePath, logger }) {
|
|
30007
|
+
const filtered = {};
|
|
30008
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
30009
|
+
const shown = displayKey(key);
|
|
30010
|
+
const canonicalKey = Object.hasOwn(CLAUDECODE_SETTINGS_KEY_ALIASES, key) ? CLAUDECODE_SETTINGS_KEY_ALIASES[key] : key;
|
|
30011
|
+
if (Object.hasOwn(CLAUDECODE_COMMAND_EXECUTING_KEYS, canonicalKey)) {
|
|
30012
|
+
logger?.warn(`Claude Code permissions: '${shown}' ${CLAUDECODE_COMMAND_EXECUTING_KEYS[canonicalKey]}, so rulesync does not write it to ${relativeFilePath}. A permissions file is shareable — 'rulesync fetch' copies one into a project — and is not where a reviewer looks for a command to run; author commands in .rulesync/hooks.jsonc, or set this key in ${relativeFilePath} by hand.`);
|
|
30013
|
+
continue;
|
|
30014
|
+
}
|
|
30015
|
+
if (Object.hasOwn(CLAUDECODE_UNHONORED_KEY_SOURCES, canonicalKey)) {
|
|
30016
|
+
logger?.warn(`Claude Code permissions: '${shown}' is only honored in ${CLAUDECODE_UNHONORED_KEY_SOURCES[canonicalKey]}, which rulesync does not generate, so it is not written to ${relativeFilePath}. Set it in that file by hand, and check ${relativeFilePath} for a stale value an earlier generate may have left there.`);
|
|
30017
|
+
continue;
|
|
30018
|
+
}
|
|
30019
|
+
if (!global && CLAUDECODE_USER_SCOPE_ONLY_KEYS.has(canonicalKey)) {
|
|
30020
|
+
logger?.warn(`Claude Code permissions: '${shown}' is not honored in the project-scoped ${relativeFilePath}, so it is not written there — Claude Code reads it from user, local or managed settings. Author it in the global scope instead, and check that file for a stale value an earlier generate may have left there.`);
|
|
30021
|
+
continue;
|
|
30022
|
+
}
|
|
30023
|
+
const widensAtValue = CLAUDECODE_TRUST_KEY_WIDENING_VALUES[canonicalKey];
|
|
30024
|
+
if (Object.hasOwn(CLAUDECODE_TRUST_AFFECTING_KEYS, canonicalKey) && (widensAtValue === void 0 || widensAtValue(value))) logger?.warn(`Claude Code permissions: writing '${shown}' to ${relativeFilePath}; it ${CLAUDECODE_TRUST_AFFECTING_KEYS[canonicalKey]}. Review the value as you would a hook, especially if this permissions file came from 'rulesync fetch'.`);
|
|
30025
|
+
filtered[key] = value;
|
|
30026
|
+
}
|
|
30027
|
+
return filtered;
|
|
30028
|
+
}
|
|
29570
30029
|
const CLAUDE_PATH_RULE_ALIASES = {
|
|
29571
30030
|
Write: "Edit",
|
|
29572
30031
|
NotebookEdit: "Edit",
|
|
@@ -29637,7 +30096,13 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29637
30096
|
});
|
|
29638
30097
|
const overridePermissions = config.claudecode?.permissions;
|
|
29639
30098
|
if (overridePermissions && typeof overridePermissions === "object") {
|
|
29640
|
-
const { allow: _a, ask: _k, deny: _d, ...
|
|
30099
|
+
const { allow: _a, ask: _k, deny: _d, ...rest } = overridePermissions;
|
|
30100
|
+
const nonListFields = Object.fromEntries(Object.entries(rest).filter(([key]) => !PROTOTYPE_POLLUTION_KEYS.has(key)));
|
|
30101
|
+
warnOnWideningPermissionFields({
|
|
30102
|
+
fields: nonListFields,
|
|
30103
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30104
|
+
logger
|
|
30105
|
+
});
|
|
29641
30106
|
settings.permissions = {
|
|
29642
30107
|
...settings.permissions,
|
|
29643
30108
|
...nonListFields
|
|
@@ -29645,17 +30110,41 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29645
30110
|
}
|
|
29646
30111
|
const overrideSandbox = config.claudecode?.sandbox;
|
|
29647
30112
|
if (isPlainRecord(overrideSandbox)) {
|
|
29648
|
-
const
|
|
30113
|
+
const executableFreeSandbox = stripCommandExecutingSandboxPaths({
|
|
30114
|
+
sandbox: overrideSandbox,
|
|
30115
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30116
|
+
logger
|
|
30117
|
+
});
|
|
30118
|
+
const scopedSandbox = global ? executableFreeSandbox : stripProjectIgnoredMaskEntries({
|
|
29649
30119
|
sandbox: stripGlobalOnlySandboxPaths({
|
|
29650
|
-
sandbox:
|
|
30120
|
+
sandbox: executableFreeSandbox,
|
|
29651
30121
|
relativeFilePath: paths.relativeFilePath,
|
|
29652
30122
|
logger
|
|
29653
30123
|
}),
|
|
29654
30124
|
relativeFilePath: paths.relativeFilePath,
|
|
29655
30125
|
logger
|
|
29656
30126
|
});
|
|
30127
|
+
warnOnTrustAffectingSandboxPaths({
|
|
30128
|
+
sandbox: scopedSandbox,
|
|
30129
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30130
|
+
logger
|
|
30131
|
+
});
|
|
29657
30132
|
if (Object.keys(scopedSandbox).length > 0) settings.sandbox = deepMergeRecords(isPlainRecord(settings.sandbox) ? settings.sandbox : {}, scopedSandbox);
|
|
29658
30133
|
}
|
|
30134
|
+
const overrideTopLevel = {};
|
|
30135
|
+
for (const [key, value] of Object.entries(config.claudecode ?? {})) {
|
|
30136
|
+
if (CLAUDECODE_NON_PASSTHROUGH_OVERRIDE_KEYS.has(key)) continue;
|
|
30137
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
30138
|
+
if (value === void 0) continue;
|
|
30139
|
+
overrideTopLevel[key] = value;
|
|
30140
|
+
}
|
|
30141
|
+
const scopedTopLevel = stripUnhonoredTopLevelKeys({
|
|
30142
|
+
overrides: overrideTopLevel,
|
|
30143
|
+
global,
|
|
30144
|
+
relativeFilePath: paths.relativeFilePath,
|
|
30145
|
+
logger
|
|
30146
|
+
});
|
|
30147
|
+
if (Object.keys(scopedTopLevel).length > 0) settings = deepMergeRecords(settings, scopedTopLevel);
|
|
29659
30148
|
const managedToolNames = managedClaudeToolNames(config);
|
|
29660
30149
|
const merged = applyPermissions({
|
|
29661
30150
|
settings,
|
|
@@ -29688,12 +30177,33 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
|
|
|
29688
30177
|
ask: permissions.ask ?? [],
|
|
29689
30178
|
deny: permissions.deny ?? []
|
|
29690
30179
|
});
|
|
29691
|
-
const { allow: _a, ask: _k, deny: _d, ...
|
|
30180
|
+
const { allow: _a, ask: _k, deny: _d, ...permissionsRest } = permissions;
|
|
30181
|
+
const nonListFields = Object.fromEntries(Object.entries(permissionsRest).filter(([key]) => !PROTOTYPE_POLLUTION_KEYS.has(key)));
|
|
29692
30182
|
if (Object.keys(nonListFields).length > 0) config.claudecode = { permissions: nonListFields };
|
|
29693
30183
|
const { sandbox } = settings;
|
|
29694
|
-
if (isPlainRecord(sandbox)
|
|
30184
|
+
if (isPlainRecord(sandbox)) {
|
|
30185
|
+
const importedSandbox = structuredClone(sandbox);
|
|
30186
|
+
for (const path of CLAUDECODE_COMMAND_EXECUTING_SANDBOX_PATHS) deleteSandboxPath({
|
|
30187
|
+
target: importedSandbox,
|
|
30188
|
+
path
|
|
30189
|
+
});
|
|
30190
|
+
if (Object.keys(importedSandbox).length > 0) config.claudecode = {
|
|
30191
|
+
...config.claudecode,
|
|
30192
|
+
sandbox: importedSandbox
|
|
30193
|
+
};
|
|
30194
|
+
}
|
|
30195
|
+
const topLevelPassthrough = {};
|
|
30196
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
30197
|
+
if (CLAUDECODE_NON_PASSTHROUGH_OVERRIDE_KEYS.has(key)) continue;
|
|
30198
|
+
const canonicalKey = Object.hasOwn(CLAUDECODE_SETTINGS_KEY_ALIASES, key) ? CLAUDECODE_SETTINGS_KEY_ALIASES[key] : key;
|
|
30199
|
+
if (Object.hasOwn(CLAUDECODE_COMMAND_EXECUTING_KEYS, canonicalKey)) continue;
|
|
30200
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
30201
|
+
if (value === void 0) continue;
|
|
30202
|
+
topLevelPassthrough[key] = value;
|
|
30203
|
+
}
|
|
30204
|
+
if (Object.keys(topLevelPassthrough).length > 0) config.claudecode = {
|
|
29695
30205
|
...config.claudecode,
|
|
29696
|
-
|
|
30206
|
+
...topLevelPassthrough
|
|
29697
30207
|
};
|
|
29698
30208
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(config, null, 2) });
|
|
29699
30209
|
}
|
|
@@ -55891,6 +56401,6 @@ async function importChecksCore(params) {
|
|
|
55891
56401
|
return writtenCount;
|
|
55892
56402
|
}
|
|
55893
56403
|
//#endregion
|
|
55894
|
-
export {
|
|
56404
|
+
export { ConsoleLogger as $, RULESYNC_PERMISSIONS_FILE_NAME as $t, RulesyncRule as A, ALL_TOOL_TARGETS as At, RulesyncCommandFrontmatterSchema as B, RULESYNC_CONFIG_SCHEMA_URL as Bt, CODEXCLI_BASH_RULES_FILE_NAME as C, removeFileStrict as Ct, RulesyncSubagentFrontmatterSchema as D, toPosixPath as Dt, RulesyncSubagent as E, runWithDirectoryRollback as Et, RulesyncHooks as F, RULESYNC_AIIGNORE_FILE_NAME as Ft, SHARED_USER_MANAGED_CONFIG_PATHS as G, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Gt, RulesyncCheckFrontmatterSchema as H, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Ht, getRulesyncSourceCandidates as I, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as It, CONFLICTING_TARGET_PAIRS as J, RULESYNC_MCP_FILE_NAME as Jt, SKILL_FILE_NAME as K, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Kt, resolveRulesyncSourceWritePath as L, RULESYNC_CHECKS_RELATIVE_DIR_PATH as Lt, RulesyncPermissions as M, PACKAGING_TOOL_TARGETS as Mt, RulesyncMcp as N, ToolTargetSchema as Nt, RulesyncSkill as O, writeFileBuffer as Ot, RulesyncIgnore as P, MAX_FILE_SIZE as Pt, findControlCharacter as Q, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Qt, parseJsonc as R, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Rt, ChecksProcessor as S, removeFile as St, getLocalSkillDirNames as T, resolvePath as Tt, stringifyFrontmatter as U, RULESYNC_HOOKS_FILE_NAME as Ut, RulesyncCheck as V, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Vt, loadYaml as W, RULESYNC_HOOKS_LEGACY_FILE_NAME as Wt, GITIGNORE_DESTINATION_KEY as X, RULESYNC_MCP_RELATIVE_FILE_PATH as Xt, ConfigFileSchema as Y, RULESYNC_MCP_LEGACY_FILE_NAME as Yt, SourceEntrySchema as Z, RULESYNC_MCP_SCHEMA_URL as Zt, CLAUDECODE_DIR as _, listDirectoryFiles as _t, convertFromTool as a, RULESYNC_SKILLS_RELATIVE_DIR_PATH as an, assertDirectoryIfExists as at, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as b, removeDirectory as bt, SubagentsProcessor as c, ALL_FEATURES as cn, checkPathTraversal as ct, McpProcessor as d, formatError as dn, ensureDir as dt, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as en, JsonLogger as et, IgnoreProcessor as f, fileExists as ft, QWENCODE_LOCAL_RULE_FILE_NAME as g, isSymlink as gt, QWENCODE_DIR as h, getHomeDirectory as ht, getProcessorRegistryEntry as i, RULESYNC_RULES_RELATIVE_DIR_PATH as in, ErrorCodes as it, RulesyncRuleFrontmatterSchema as j, ALL_TOOL_TARGETS_WITH_WILDCARD as jt, RulesyncSkillFrontmatterSchema as k, writeFileContent as kt, SkillsProcessor as l, ALL_FEATURES_WITH_WILDCARD as ln, createTempDirectory as lt, CommandsProcessor as m, getFileSize as mt, checkRulesyncDirExists as n, RULESYNC_PERMISSIONS_SCHEMA_URL as nn, warnOnConflictingFlags as nt, isPackagingToolTarget as o, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as on, assertTreeContainsNoSymlinks as ot, HooksProcessor as p, findFilesByGlobs as pt, ConfigResolver as q, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as qt, generate as r, RULESYNC_RELATIVE_DIR_PATH as rn, CLIError as rt, RulesProcessor as s, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as sn, assertWritablePathInsideRoot as st, importFromTool as t, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as tn, fallbackLogger as tt, stripControlCharacters as u, DEPRECATED_FEATURE_REPLACEMENTS as un, directoryExists as ut, CLAUDECODE_LOCAL_RULE_FILE_NAME as v, readFileContent as vt, CODEXCLI_DIR as w, removeTempDirectory as wt, CLAUDECODE_SKILLS_DIR_PATH as x, removeDirectoryStrict as xt, CLAUDECODE_MEMORIES_DIR_NAME as y, readFileContentOrNull as yt, RulesyncCommand as z, RULESYNC_CONFIG_RELATIVE_FILE_PATH as zt };
|
|
55895
56405
|
|
|
55896
|
-
//# sourceMappingURL=import-
|
|
56406
|
+
//# sourceMappingURL=import-BPTCMtUS.js.map
|