rulesync 43.1.2 → 43.3.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 +273 -65
- package/dist/cli/index.js +273 -65
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-D2uYjAdc.js → import-BQNLyW5s.js} +646 -81
- package/dist/{import-D2uYjAdc.js.map → import-BQNLyW5s.js.map} +1 -1
- package/dist/{import-BTqYh7nP.cjs → import-CedO1sFP.cjs} +645 -80
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -5522,24 +5522,27 @@ const AUGMENTCODE_HOOK_EVENTS = [
|
|
|
5522
5522
|
* Hook events supported by Mistral Vibe (mistral-vibe).
|
|
5523
5523
|
*
|
|
5524
5524
|
* Vibe exposes three hook events in `.vibe/hooks.toml`: `pre_tool` ←
|
|
5525
|
-
* `preToolUse`, `post_tool` ← `postToolUse`, and `post_agent` ← `stop`
|
|
5526
|
-
* after every assistant turn that ends
|
|
5527
|
-
*
|
|
5528
|
-
*
|
|
5529
|
-
*
|
|
5530
|
-
*
|
|
5531
|
-
* `
|
|
5525
|
+
* `preToolUse`, `post_tool` ← `postToolUse`, and `post_agent` ← `stop` /
|
|
5526
|
+
* `subagentStop`. `post_agent` fires after every assistant turn that ends
|
|
5527
|
+
* without pending tool calls, for the main agent and (since v3.0.2) for every
|
|
5528
|
+
* subagent. v3.0.3 lets a `post_agent` hook take a `match` on the agent that
|
|
5529
|
+
* finished its turn: the reserved alias `main` selects the main agent, so
|
|
5530
|
+
* canonical `stop` becomes `post_agent` with `match = "main"`, and a canonical
|
|
5531
|
+
* `subagentStop` with a matcher becomes `post_agent` with that agent name as
|
|
5532
|
+
* `match`. Only the tool events (`pre_tool`/`post_tool`) carry the `strict`
|
|
5533
|
+
* flag. Only `type: "command"` hooks are relevant.
|
|
5532
5534
|
*
|
|
5533
5535
|
* v2.21.0 graduated hooks from experimental and renamed all three types
|
|
5534
5536
|
* (`before_tool` → `pre_tool`, `after_tool` → `post_tool`, `post_agent_turn` →
|
|
5535
5537
|
* `post_agent`). `HookType` is a strict enum, so an entry using an old name is
|
|
5536
5538
|
* rejected outright and reported as a `HookConfigIssue`.
|
|
5537
|
-
* @see https://github.com/mistralai/mistral-vibe/blob/
|
|
5539
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
5538
5540
|
*/
|
|
5539
5541
|
const VIBE_HOOK_EVENTS = [
|
|
5540
5542
|
"preToolUse",
|
|
5541
5543
|
"postToolUse",
|
|
5542
|
-
"stop"
|
|
5544
|
+
"stop",
|
|
5545
|
+
"subagentStop"
|
|
5543
5546
|
];
|
|
5544
5547
|
/**
|
|
5545
5548
|
* Hook events supported by Codewhale.
|
|
@@ -7007,9 +7010,10 @@ const JUNIE_TO_CANONICAL_EVENT_NAMES = invertEventNames(CANONICAL_TO_JUNIE_EVENT
|
|
|
7007
7010
|
/**
|
|
7008
7011
|
* Map canonical camelCase event names to Mistral Vibe snake_case.
|
|
7009
7012
|
*
|
|
7010
|
-
* Vibe documents three hook events. The canonical `stop`
|
|
7011
|
-
* `post_agent` (fires after every assistant turn
|
|
7012
|
-
* calls)
|
|
7013
|
+
* Vibe documents three hook events. The canonical `stop` and `subagentStop`
|
|
7014
|
+
* events both map to Vibe's `post_agent` (fires after every assistant turn
|
|
7015
|
+
* ending without pending tool calls); the adapter tells them apart with the
|
|
7016
|
+
* hook's `match` on the agent name (`main` for the main agent).
|
|
7013
7017
|
*
|
|
7014
7018
|
* These are the v2.21.0 names; the pre-2.21.0 spellings (`before_tool`,
|
|
7015
7019
|
* `after_tool`, `post_agent_turn`) are rejected by Vibe's strict `HookType`
|
|
@@ -7019,7 +7023,8 @@ const JUNIE_TO_CANONICAL_EVENT_NAMES = invertEventNames(CANONICAL_TO_JUNIE_EVENT
|
|
|
7019
7023
|
const CANONICAL_TO_VIBE_EVENT_NAMES = {
|
|
7020
7024
|
preToolUse: "pre_tool",
|
|
7021
7025
|
postToolUse: "post_tool",
|
|
7022
|
-
stop: "post_agent"
|
|
7026
|
+
stop: "post_agent",
|
|
7027
|
+
subagentStop: "post_agent"
|
|
7023
7028
|
};
|
|
7024
7029
|
/**
|
|
7025
7030
|
* Map Mistral Vibe snake_case event names to canonical camelCase.
|
|
@@ -7032,6 +7037,7 @@ const CANONICAL_TO_VIBE_EVENT_NAMES = {
|
|
|
7032
7037
|
*/
|
|
7033
7038
|
const VIBE_TO_CANONICAL_EVENT_NAMES = {
|
|
7034
7039
|
...invertEventNames(CANONICAL_TO_VIBE_EVENT_NAMES),
|
|
7040
|
+
post_agent: "stop",
|
|
7035
7041
|
before_tool: "preToolUse",
|
|
7036
7042
|
after_tool: "postToolUse",
|
|
7037
7043
|
post_agent_turn: "stop"
|
|
@@ -8382,6 +8388,32 @@ const CopilotPermissionsOverrideSchema = z.looseObject({
|
|
|
8382
8388
|
sandbox: z.optional(CopilotSandboxSchema)
|
|
8383
8389
|
});
|
|
8384
8390
|
/**
|
|
8391
|
+
* The permission mode the GitHub Copilot CLI starts new interactive sessions
|
|
8392
|
+
* in (`defaultPermissionMode` of `~/.copilot/settings.json`): `manual` prompts
|
|
8393
|
+
* for every request not already allowed, `assisted` lets a model review
|
|
8394
|
+
* eligible requests (Assisted Permissions, non-experimental since v1.0.104),
|
|
8395
|
+
* and `allow-all` is equivalent to `--allow-all`. These are the CLI's own
|
|
8396
|
+
* values and differ from the VS Code `copilot` target's
|
|
8397
|
+
* {@link COPILOT_DEFAULT_PERMISSION_MODES}.
|
|
8398
|
+
*
|
|
8399
|
+
* @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
8400
|
+
*/
|
|
8401
|
+
const COPILOTCLI_DEFAULT_PERMISSION_MODES = [
|
|
8402
|
+
"manual",
|
|
8403
|
+
"assisted",
|
|
8404
|
+
"allow-all"
|
|
8405
|
+
];
|
|
8406
|
+
/**
|
|
8407
|
+
* Tool-scoped override block for the GitHub Copilot CLI.
|
|
8408
|
+
* `defaultPermissionMode` writes the user-scope `defaultPermissionMode` key of
|
|
8409
|
+
* `~/.copilot/settings.json`; it has no canonical category, and the repository
|
|
8410
|
+
* settings file does not accept it.
|
|
8411
|
+
*/
|
|
8412
|
+
const CopilotcliPermissionsOverrideSchema = z.looseObject({
|
|
8413
|
+
permission: z.optional(ToolScopedPermissionSchema),
|
|
8414
|
+
defaultPermissionMode: z.optional(z.enum(COPILOTCLI_DEFAULT_PERMISSION_MODES))
|
|
8415
|
+
});
|
|
8416
|
+
/**
|
|
8385
8417
|
* oh-my-pi's `tools.approvalMode` values: the tool tiers it auto-approves
|
|
8386
8418
|
* before prompting. `always-ask` auto-approves only read-only tools,
|
|
8387
8419
|
* `always-ask-cached` (Ask and remember) also prompts before file reads and
|
|
@@ -8398,11 +8430,14 @@ const OMP_APPROVAL_MODES = [
|
|
|
8398
8430
|
];
|
|
8399
8431
|
/**
|
|
8400
8432
|
* Tool-scoped override block for oh-my-pi (`omp`). `approvalMode` writes the
|
|
8401
|
-
* `tools.approvalMode` key of `config.yml`,
|
|
8433
|
+
* `tools.approvalMode` key of `config.yml`, and `restrictedFolders` the
|
|
8434
|
+
* `tools.restrictedFolders` list (folders where every action prompts in every
|
|
8435
|
+
* approval mode, `yolo` included); neither has a canonical category.
|
|
8402
8436
|
*/
|
|
8403
8437
|
const OmpPermissionsOverrideSchema = z.looseObject({
|
|
8404
8438
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
8405
|
-
approvalMode: z.optional(z.enum(OMP_APPROVAL_MODES))
|
|
8439
|
+
approvalMode: z.optional(z.enum(OMP_APPROVAL_MODES)),
|
|
8440
|
+
restrictedFolders: z.optional(z.array(z.string()))
|
|
8406
8441
|
});
|
|
8407
8442
|
const KimiCodePermissionsOverrideSchema = z.looseObject({
|
|
8408
8443
|
permission: z.optional(ToolScopedPermissionSchema),
|
|
@@ -8854,6 +8889,48 @@ const QoderPermissionsOverrideSchema = z.looseObject({
|
|
|
8854
8889
|
agentsMdExcludes: z.optional(z.array(z.string()))
|
|
8855
8890
|
});
|
|
8856
8891
|
/**
|
|
8892
|
+
* Tool-scoped override block for CodeBuddy Code. The shared `permission` block
|
|
8893
|
+
* continues to drive `permissions.allow`/`ask`/`deny` of
|
|
8894
|
+
* `.codebuddy/settings.json` (project) / `~/.codebuddy/settings.json` (global).
|
|
8895
|
+
*
|
|
8896
|
+
* The other documented settings with no canonical category are authored here
|
|
8897
|
+
* and merged into the same file, key by key, over what the file already
|
|
8898
|
+
* holds:
|
|
8899
|
+
*
|
|
8900
|
+
* - `autoReview` (`policy` replaces the built-in security policy of the `auto`
|
|
8901
|
+
* permission mode, `extraPolicy` is appended to it) and the legacy
|
|
8902
|
+
* `autoMode` rules (`environment` / `allow` / `soft_deny` / `hard_deny`).
|
|
8903
|
+
* CodeBuddy reads both from the user and project-local settings only, never
|
|
8904
|
+
* from the shared project file, so they are written by `--global` generate
|
|
8905
|
+
* alone.
|
|
8906
|
+
* - `memory.parentDiscovery`: whether project instruction files
|
|
8907
|
+
* (`CODEBUDDY.md` / `AGENTS.md`) are also discovered in parent directories.
|
|
8908
|
+
*
|
|
8909
|
+
* Only the keys listed here are written; any other key of these groups is
|
|
8910
|
+
* skipped with a warning. Removing a key from the override leaves the
|
|
8911
|
+
* previously written value in place.
|
|
8912
|
+
*
|
|
8913
|
+
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
8914
|
+
* @see https://www.codebuddy.ai/docs/cli/memory
|
|
8915
|
+
* @example
|
|
8916
|
+
* { "autoReview": { "extraPolicy": "Allow dev namespace releases" },
|
|
8917
|
+
* "memory": { "parentDiscovery": false } }
|
|
8918
|
+
*/
|
|
8919
|
+
const CodebuddyPermissionsOverrideSchema = z.looseObject({
|
|
8920
|
+
permission: z.optional(ToolScopedPermissionSchema),
|
|
8921
|
+
autoReview: z.optional(z.looseObject({
|
|
8922
|
+
policy: z.optional(z.string()),
|
|
8923
|
+
extraPolicy: z.optional(z.string())
|
|
8924
|
+
})),
|
|
8925
|
+
autoMode: z.optional(z.looseObject({
|
|
8926
|
+
environment: z.optional(z.array(z.string())),
|
|
8927
|
+
allow: z.optional(z.array(z.string())),
|
|
8928
|
+
soft_deny: z.optional(z.array(z.string())),
|
|
8929
|
+
hard_deny: z.optional(z.array(z.string()))
|
|
8930
|
+
})),
|
|
8931
|
+
memory: z.optional(z.looseObject({ parentDiscovery: z.optional(z.boolean()) }))
|
|
8932
|
+
});
|
|
8933
|
+
/**
|
|
8857
8934
|
* One native Factory Droid permission rule. `match.prefix` positions are either
|
|
8858
8935
|
* a single token or a non-empty list of alternative tokens.
|
|
8859
8936
|
*
|
|
@@ -9736,13 +9813,13 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
9736
9813
|
zed: z.optional(ZedPermissionsOverrideSchema),
|
|
9737
9814
|
devin: z.optional(DevinPermissionsOverrideSchema),
|
|
9738
9815
|
qoder: z.optional(QoderPermissionsOverrideSchema),
|
|
9816
|
+
copilotcli: z.optional(CopilotcliPermissionsOverrideSchema),
|
|
9739
9817
|
"antigravity-ide": z.optional(CanonicalPermissionsOverrideSchema),
|
|
9740
9818
|
continue: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9741
9819
|
copilot: z.optional(CopilotPermissionsOverrideSchema),
|
|
9742
|
-
codebuddy: z.optional(
|
|
9820
|
+
codebuddy: z.optional(CodebuddyPermissionsOverrideSchema),
|
|
9743
9821
|
commandcode: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9744
9822
|
lettacode: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9745
|
-
copilotcli: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9746
9823
|
crush: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9747
9824
|
goose: z.optional(CanonicalPermissionsOverrideSchema),
|
|
9748
9825
|
omp: z.optional(OmpPermissionsOverrideSchema),
|
|
@@ -10229,6 +10306,10 @@ const RulesyncRuleFrontmatterSchema = z.object({
|
|
|
10229
10306
|
enabled: z.optional(z.boolean()),
|
|
10230
10307
|
description: z.optional(z.string())
|
|
10231
10308
|
})),
|
|
10309
|
+
cline: z.optional(z.looseObject({
|
|
10310
|
+
disabled: z.optional(z.boolean()),
|
|
10311
|
+
enabled: z.optional(z.boolean())
|
|
10312
|
+
})),
|
|
10232
10313
|
continue: z.optional(z.looseObject({
|
|
10233
10314
|
alwaysApply: z.optional(z.boolean()),
|
|
10234
10315
|
description: z.optional(z.string()),
|
|
@@ -11565,7 +11646,8 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
11565
11646
|
license: z.optional(z.string()),
|
|
11566
11647
|
compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
|
|
11567
11648
|
metadata: z.optional(z.looseObject({})),
|
|
11568
|
-
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
11649
|
+
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
|
|
11650
|
+
"disable-model-invocation": z.optional(z.boolean())
|
|
11569
11651
|
})),
|
|
11570
11652
|
hermesagent: z.optional(z.looseObject({})),
|
|
11571
11653
|
vibe: z.optional(z.looseObject({
|
|
@@ -13708,7 +13790,11 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
13708
13790
|
},
|
|
13709
13791
|
permissions: {
|
|
13710
13792
|
kind: "replace-owned-keys",
|
|
13711
|
-
ownedKeys: [
|
|
13793
|
+
ownedKeys: [
|
|
13794
|
+
"allowedUrls",
|
|
13795
|
+
"deniedUrls",
|
|
13796
|
+
"defaultPermissionMode"
|
|
13797
|
+
]
|
|
13712
13798
|
}
|
|
13713
13799
|
}
|
|
13714
13800
|
},
|
|
@@ -13887,7 +13973,12 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
13887
13973
|
},
|
|
13888
13974
|
permissions: {
|
|
13889
13975
|
kind: "replace-owned-keys",
|
|
13890
|
-
ownedKeys: [
|
|
13976
|
+
ownedKeys: [
|
|
13977
|
+
"permissions",
|
|
13978
|
+
"autoReview",
|
|
13979
|
+
"autoMode",
|
|
13980
|
+
"memory"
|
|
13981
|
+
]
|
|
13891
13982
|
}
|
|
13892
13983
|
}
|
|
13893
13984
|
},
|
|
@@ -14197,7 +14288,8 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
14197
14288
|
"tools",
|
|
14198
14289
|
"enabled_tools",
|
|
14199
14290
|
"disabled_tools",
|
|
14200
|
-
"enable_subagents"
|
|
14291
|
+
"enable_subagents",
|
|
14292
|
+
"outside_workdir"
|
|
14201
14293
|
]
|
|
14202
14294
|
}
|
|
14203
14295
|
}
|
|
@@ -19509,7 +19601,8 @@ const AgentsSkillsSkillFrontmatterSchema = z.looseObject({
|
|
|
19509
19601
|
license: z.optional(z.string()),
|
|
19510
19602
|
compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
|
|
19511
19603
|
metadata: z.optional(z.looseObject({})),
|
|
19512
|
-
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
19604
|
+
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
|
|
19605
|
+
"disable-model-invocation": z.optional(z.boolean())
|
|
19513
19606
|
});
|
|
19514
19607
|
const COMPATIBILITY_MAX_LENGTH = 500;
|
|
19515
19608
|
const AGENT_SKILLS_SPEC = "the Agent Skills spec";
|
|
@@ -19650,6 +19743,38 @@ function toSpecConformantAgentSkillFields(section, { coerceMetadata = true } = {
|
|
|
19650
19743
|
...allowedTools !== void 0 && allowedTools.length > 0 && { "allowed-tools": allowedTools }
|
|
19651
19744
|
};
|
|
19652
19745
|
}
|
|
19746
|
+
/**
|
|
19747
|
+
* The opt-in `disable-model-invocation` flag of the `agentsskills` block.
|
|
19748
|
+
*
|
|
19749
|
+
* The key is not part of the Agent Skills specification, so `skills-ref
|
|
19750
|
+
* validate` rejects a `SKILL.md` that carries it, but several clients reading
|
|
19751
|
+
* `.agents/skills/` (e.g. pi) honor it. To keep the default output
|
|
19752
|
+
* spec-conformant, only an explicit `agentsskills.disable-model-invocation`
|
|
19753
|
+
* value is written; the root-level rulesync flag is deliberately not inherited.
|
|
19754
|
+
*
|
|
19755
|
+
* Kept apart from {@link toSpecConformantAgentSkillFields} because that helper
|
|
19756
|
+
* is shared with writers of other tools' own locations, which have their own
|
|
19757
|
+
* sections for tool-specific keys. Only writers of the shared `.agents/skills/`
|
|
19758
|
+
* root (see {@link isSharedAgentSkillsRoot}) — the native target, GitLab Duo's
|
|
19759
|
+
* project scope and the simulated `agentsmd` — spread this, so they all emit
|
|
19760
|
+
* the same frontmatter for that file.
|
|
19761
|
+
*/
|
|
19762
|
+
function toAgentsSkillsInvocationField(section) {
|
|
19763
|
+
const value = section?.["disable-model-invocation"];
|
|
19764
|
+
return value === void 0 ? {} : { "disable-model-invocation": value };
|
|
19765
|
+
}
|
|
19766
|
+
/**
|
|
19767
|
+
* Whether a skill directory lives in the cross-client `.agents/skills/` root
|
|
19768
|
+
* (project, or `~/.agents/skills/` in global scope). The
|
|
19769
|
+
* `agentsskills.disable-model-invocation` opt-in exists for the clients that
|
|
19770
|
+
* read that root, so it is written and imported only there — not by the
|
|
19771
|
+
* subclasses that reuse this class for a tool's own location (Trae, Qoder, Xum,
|
|
19772
|
+
* …), where the key would otherwise leak into the `agentsskills` section on
|
|
19773
|
+
* import and from there into every `.agents/skills/` output.
|
|
19774
|
+
*/
|
|
19775
|
+
function isSharedAgentSkillsRoot(relativeDirPath) {
|
|
19776
|
+
return toPosixPath(relativeDirPath) === toPosixPath(AGENTSMD_SKILLS_DIR_PATH);
|
|
19777
|
+
}
|
|
19653
19778
|
/** The `SKILL.md` a diagnostic should point at, in the scope it is written to. */
|
|
19654
19779
|
function agentSkillFilePath({ outputRoot, relativeDirPath, dirName }) {
|
|
19655
19780
|
return join(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME);
|
|
@@ -19731,7 +19856,8 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
|
|
|
19731
19856
|
...frontmatter.license !== void 0 && { license: frontmatter.license },
|
|
19732
19857
|
...frontmatter.compatibility !== void 0 && { compatibility: frontmatter.compatibility },
|
|
19733
19858
|
...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata },
|
|
19734
|
-
...allowedTools !== void 0 && allowedTools.length > 0 && { "allowed-tools": allowedTools }
|
|
19859
|
+
...allowedTools !== void 0 && allowedTools.length > 0 && { "allowed-tools": allowedTools },
|
|
19860
|
+
...isSharedAgentSkillsRoot(this.getRelativeDirPath()) && frontmatter["disable-model-invocation"] !== void 0 && { "disable-model-invocation": frontmatter["disable-model-invocation"] }
|
|
19735
19861
|
};
|
|
19736
19862
|
const rulesyncFrontmatter = {
|
|
19737
19863
|
name: frontmatter.name,
|
|
@@ -19757,7 +19883,8 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
|
|
|
19757
19883
|
const agentsSkillsFrontmatter = {
|
|
19758
19884
|
name: rulesyncFrontmatter.name,
|
|
19759
19885
|
description: rulesyncFrontmatter.description,
|
|
19760
|
-
...toSpecConformantAgentSkillFields(resolveAgentsSkillsSection(rulesyncFrontmatter))
|
|
19886
|
+
...toSpecConformantAgentSkillFields(resolveAgentsSkillsSection(rulesyncFrontmatter)),
|
|
19887
|
+
...isSharedAgentSkillsRoot(settablePaths.relativeDirPath) && toAgentsSkillsInvocationField(rulesyncFrontmatter.agentsskills)
|
|
19761
19888
|
};
|
|
19762
19889
|
AgentsSkillsSkill.reportSpecViolations({
|
|
19763
19890
|
outputRoot,
|
|
@@ -24831,7 +24958,8 @@ var AgentsmdSkill = class AgentsmdSkill extends SimulatedSkill {
|
|
|
24831
24958
|
const relativeDirPath = this.getSettablePaths().relativeDirPath;
|
|
24832
24959
|
const frontmatter = {
|
|
24833
24960
|
...defaults.frontmatter,
|
|
24834
|
-
...toSpecConformantAgentSkillFields(resolveAgentsSkillsSection(params.rulesyncSkill.getFrontmatter()))
|
|
24961
|
+
...toSpecConformantAgentSkillFields(resolveAgentsSkillsSection(params.rulesyncSkill.getFrontmatter())),
|
|
24962
|
+
...toAgentsSkillsInvocationField(params.rulesyncSkill.getFrontmatter().agentsskills)
|
|
24835
24963
|
};
|
|
24836
24964
|
AgentsSkillsSkill.reportSpecViolations({
|
|
24837
24965
|
outputRoot: params.outputRoot ?? process.cwd(),
|
|
@@ -30550,7 +30678,10 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
|
|
|
30550
30678
|
}
|
|
30551
30679
|
}
|
|
30552
30680
|
static getSettablePaths({ global: _global = false } = {}) {
|
|
30553
|
-
return {
|
|
30681
|
+
return {
|
|
30682
|
+
relativeDirPath: QWENCODE_SKILLS_DIR_PATH,
|
|
30683
|
+
importOnlySkillRoots: [AGENTSMD_SKILLS_DIR_PATH]
|
|
30684
|
+
};
|
|
30554
30685
|
}
|
|
30555
30686
|
getFrontmatter() {
|
|
30556
30687
|
return QwencodeSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
@@ -39571,6 +39702,16 @@ var VibePluginSubagent = class VibePluginSubagent extends ToolSubagent {
|
|
|
39571
39702
|
*/
|
|
39572
39703
|
const VIBE_PROMPTS_DIR_PATH = join(".vibe", "prompts");
|
|
39573
39704
|
/**
|
|
39705
|
+
* The agent name Vibe reserves for `post_agent` hook matching (v3.0.3+).
|
|
39706
|
+
* `AgentProfile.from_toml` refuses a file whose stem matches it case-insensitively, so an
|
|
39707
|
+
* agent written as `main.toml` would never load.
|
|
39708
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/vibe/core/agents/models.py
|
|
39709
|
+
*/
|
|
39710
|
+
const VIBE_RESERVED_AGENT_NAME = "main";
|
|
39711
|
+
function isReservedVibeAgentFileName(relativeFilePath) {
|
|
39712
|
+
return basename(relativeFilePath).replace(/\.(md|toml)$/, "").toLowerCase() === VIBE_RESERVED_AGENT_NAME;
|
|
39713
|
+
}
|
|
39714
|
+
/**
|
|
39574
39715
|
* Vibe resolves a `system_prompt_id` by file name, and
|
|
39575
39716
|
* `VibeConfigSchema._check_system_prompt` evaluates it during validation — an
|
|
39576
39717
|
* unresolvable id raises and the agent is dropped at discovery. The id must
|
|
@@ -39685,9 +39826,17 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
39685
39826
|
* evaluates the property during validation, so an unresolvable
|
|
39686
39827
|
* `system_prompt_id` raises and `AgentRegistry._try_load` drops the agent
|
|
39687
39828
|
* with a warning.
|
|
39829
|
+
*
|
|
39830
|
+
* A subagent named `main` is skipped with a warning: Vibe reserves that name
|
|
39831
|
+
* for hook matching and does not load an agent file called `main.toml`.
|
|
39688
39832
|
*/
|
|
39689
|
-
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false }) {
|
|
39833
|
+
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false, logger }) {
|
|
39690
39834
|
return rulesyncSubagents.flatMap((rulesyncSubagent) => {
|
|
39835
|
+
const sourcePath = rulesyncSubagent.getRelativeFilePath();
|
|
39836
|
+
if (isReservedVibeAgentFileName(sourcePath)) {
|
|
39837
|
+
logger?.warn(`Skipping Vibe subagent ${sourcePath}: Vibe reserves the agent name '${VIBE_RESERVED_AGENT_NAME}' for hook matching and does not load an agent file named '${VIBE_RESERVED_AGENT_NAME}'. Rename the subagent file.`);
|
|
39838
|
+
return [];
|
|
39839
|
+
}
|
|
39691
39840
|
const agentFile = VibeSubagent.fromRulesyncSubagent({
|
|
39692
39841
|
outputRoot,
|
|
39693
39842
|
relativeDirPath: VibeSubagent.getSettablePaths({ global }).relativeDirPath,
|
|
@@ -44974,17 +45123,49 @@ var ClaudecodeRule = class ClaudecodeRule extends ToolRule {
|
|
|
44974
45123
|
* - `alwaysApply: true`: force the rule to always load (equivalent to a file
|
|
44975
45124
|
* with no frontmatter at all).
|
|
44976
45125
|
* - `description`: label surfaced in the rule toggle UI.
|
|
45126
|
+
* - `disabled: true` (or `enabled: false`): the rule is switched off. Since
|
|
45127
|
+
* Cline v4.1.26 (cline/cline#14605) the rule toggle writes this flag into the
|
|
45128
|
+
* rule file itself instead of keeping it only in extension state.
|
|
44977
45129
|
*
|
|
44978
45130
|
* Rules without frontmatter are always active. Uses `z.looseObject()` so
|
|
44979
|
-
* forward-compatible fields added upstream are preserved on round-trip
|
|
45131
|
+
* forward-compatible fields added upstream are preserved on round-trip: every
|
|
45132
|
+
* key other than `paths` / `alwaysApply` / `description` is carried in the
|
|
45133
|
+
* rulesync `cline:` section.
|
|
44980
45134
|
*
|
|
44981
45135
|
* @see https://docs.cline.bot/customization/cline-rules
|
|
45136
|
+
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/extensions/config/user-instruction-config-loader.ts
|
|
44982
45137
|
*/
|
|
44983
45138
|
const ClineRuleFrontmatterSchema = z.looseObject({
|
|
44984
45139
|
paths: z.optional(z.union([z.array(z.string()), z.string()])),
|
|
44985
45140
|
alwaysApply: z.optional(z.boolean()),
|
|
44986
45141
|
description: z.optional(z.string())
|
|
44987
45142
|
});
|
|
45143
|
+
const CLINE_RULE_FIELD_TYPES = {
|
|
45144
|
+
disabled: "boolean",
|
|
45145
|
+
enabled: "boolean"
|
|
45146
|
+
};
|
|
45147
|
+
const CANONICAL_CLINE_RULE_KEYS = [
|
|
45148
|
+
"paths",
|
|
45149
|
+
"alwaysApply",
|
|
45150
|
+
"description"
|
|
45151
|
+
];
|
|
45152
|
+
/** Returns a copy of `record` without the canonical `paths` / `alwaysApply` / `description` keys. */
|
|
45153
|
+
function omitCanonicalClineRuleKeys(record) {
|
|
45154
|
+
return Object.fromEntries(Object.entries(record ?? {}).filter(([key]) => !CANONICAL_CLINE_RULE_KEYS.includes(key)));
|
|
45155
|
+
}
|
|
45156
|
+
/**
|
|
45157
|
+
* Splits a Cline rule frontmatter into the keys that have canonical rulesync
|
|
45158
|
+
* homes (`paths` / `alwaysApply` / `description`) and the rest, which are
|
|
45159
|
+
* lifted into the rulesync `cline:` section.
|
|
45160
|
+
*/
|
|
45161
|
+
function extractClineSection({ frontmatter, relativeFilePath }) {
|
|
45162
|
+
return Object.fromEntries(Object.entries(omitCanonicalClineRuleKeys(frontmatter)).filter(([key, value]) => {
|
|
45163
|
+
const expected = Object.hasOwn(CLINE_RULE_FIELD_TYPES, key) ? CLINE_RULE_FIELD_TYPES[key] : void 0;
|
|
45164
|
+
if (expected === void 0 || value === void 0 || typeof value === expected) return true;
|
|
45165
|
+
warnWithFallback(void 0, `Cline rules: dropping ${key} from ${relativeFilePath}: Cline rejects the whole rule unless it is a ${expected}.`);
|
|
45166
|
+
return false;
|
|
45167
|
+
}));
|
|
45168
|
+
}
|
|
44988
45169
|
function isNonRootParams$1(params) {
|
|
44989
45170
|
return "frontmatter" in params && "body" in params;
|
|
44990
45171
|
}
|
|
@@ -45059,6 +45240,10 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45059
45240
|
toRulesyncRule() {
|
|
45060
45241
|
if (this.isRoot()) return this.toRulesyncRuleDefault();
|
|
45061
45242
|
const globs = this.frontmatter?.alwaysApply === true ? ["**/*"] : normalizePaths$1(this.frontmatter?.paths);
|
|
45243
|
+
const cline = extractClineSection({
|
|
45244
|
+
frontmatter: this.frontmatter,
|
|
45245
|
+
relativeFilePath: this.getRelativeFilePath()
|
|
45246
|
+
});
|
|
45062
45247
|
return new RulesyncRule({
|
|
45063
45248
|
outputRoot: process.cwd(),
|
|
45064
45249
|
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
@@ -45067,7 +45252,8 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45067
45252
|
root: false,
|
|
45068
45253
|
targets: ["*"],
|
|
45069
45254
|
description: this.frontmatter?.description,
|
|
45070
|
-
globs
|
|
45255
|
+
globs,
|
|
45256
|
+
...Object.keys(cline).length > 0 && { cline }
|
|
45071
45257
|
},
|
|
45072
45258
|
body: this.body ?? this.getFileContent()
|
|
45073
45259
|
});
|
|
@@ -45112,16 +45298,22 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45112
45298
|
* universal match-everything globs mean "always", so they map to
|
|
45113
45299
|
* `alwaysApply: true` instead of a `paths` list; specific globs become
|
|
45114
45300
|
* conditional `paths`; an empty globs list leaves the rule always-active with
|
|
45115
|
-
* no `paths`/`alwaysApply`.
|
|
45301
|
+
* no `paths`/`alwaysApply`. Every key of the rulesync `cline:` section
|
|
45302
|
+
* (e.g. `disabled: true`) is written into the frontmatter too; the canonical
|
|
45303
|
+
* `paths` / `alwaysApply` / `description` win over same-named keys there.
|
|
45116
45304
|
*/
|
|
45117
45305
|
static buildNonRootRule({ outputRoot, rulesyncRule, validate, relativeDirPath }) {
|
|
45118
45306
|
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
45119
45307
|
const globs = normalizePaths$1(rulesyncFrontmatter.globs);
|
|
45120
45308
|
const isUniversal = globs.length > 0 && globs.every((glob) => glob === "**/*" || glob === "*");
|
|
45309
|
+
const rawClineSection = rulesyncFrontmatter.cline ?? {};
|
|
45310
|
+
for (const key of CANONICAL_CLINE_RULE_KEYS) if (Object.hasOwn(rawClineSection, key)) warnWithFallback(void 0, `Cline rules: ignoring cline.${key} in ${rulesyncRule.getRelativeFilePath()}: set the canonical ${key === "description" ? "description" : "globs"} instead.`);
|
|
45311
|
+
const clineSection = omitCanonicalClineRuleKeys(rawClineSection);
|
|
45121
45312
|
const frontmatter = {
|
|
45122
45313
|
paths: globs.length > 0 && !isUniversal ? globs : void 0,
|
|
45123
45314
|
alwaysApply: isUniversal ? true : void 0,
|
|
45124
|
-
description: rulesyncFrontmatter.description || void 0
|
|
45315
|
+
description: rulesyncFrontmatter.description || void 0,
|
|
45316
|
+
...clineSection
|
|
45125
45317
|
};
|
|
45126
45318
|
return new ClineRule({
|
|
45127
45319
|
outputRoot,
|
|
@@ -55808,6 +56000,14 @@ function applyCommandPrefix({ def, converterConfig }) {
|
|
|
55808
56000
|
if (isExecForm) return `${bracePlaceholder(converterConfig.projectDirVar)}/${stripSurroundingQuotes(relativeCommand)}`;
|
|
55809
56001
|
return anchorInline(`"${converterConfig.projectDirVar}"/${relativeCommand}`);
|
|
55810
56002
|
}
|
|
56003
|
+
/** Whether a field restricted to some events applies on this one. */
|
|
56004
|
+
function isEventApplicable({ events, eventName }) {
|
|
56005
|
+
return events === void 0 || events.has(eventName);
|
|
56006
|
+
}
|
|
56007
|
+
/** Quote and comma-join a set of names (events, hook types) for a warning. */
|
|
56008
|
+
function describeEvents(names) {
|
|
56009
|
+
return [...names].map((name) => `"${name}"`).join(", ");
|
|
56010
|
+
}
|
|
55811
56011
|
/**
|
|
55812
56012
|
* Whether a field registered for `command` hooks only applies to this hook.
|
|
55813
56013
|
* Applied on both export and import: a value imported into a canonical field
|
|
@@ -55829,7 +56029,7 @@ function isFieldApplicable({ commandOnly, hookType }) {
|
|
|
55829
56029
|
* silence — the mirror of the import side.
|
|
55830
56030
|
*/
|
|
55831
56031
|
function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn }) {
|
|
55832
|
-
for (const { canonical, tool, commandOnly, maxLength, collapseWhitespaceForLength, onlyWithFlag } of fields) {
|
|
56032
|
+
for (const { canonical, tool, commandOnly, maxLength, collapseWhitespaceForLength, onlyWithFlag, events } of fields) {
|
|
55833
56033
|
const value = def[canonical];
|
|
55834
56034
|
if (value === void 0) continue;
|
|
55835
56035
|
if (!isFieldApplicable({
|
|
@@ -55839,6 +56039,13 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55839
56039
|
warn?.(`Dropping "${canonical}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on "command" hooks only, so it is not generated.`);
|
|
55840
56040
|
continue;
|
|
55841
56041
|
}
|
|
56042
|
+
if (events !== void 0 && !isEventApplicable({
|
|
56043
|
+
events,
|
|
56044
|
+
eventName
|
|
56045
|
+
})) {
|
|
56046
|
+
if (value !== false) warn?.(`Dropping "${canonical}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${describeEvents(events)} only, so it is not generated.`);
|
|
56047
|
+
continue;
|
|
56048
|
+
}
|
|
55842
56049
|
if (!isValid({
|
|
55843
56050
|
value,
|
|
55844
56051
|
canonical
|
|
@@ -55852,9 +56059,12 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55852
56059
|
if (length > maxLength) warn?.(`"${canonical}" on a "${hookType}" hook on "${eventName}" is ${length} characters long${collapseWhitespaceForLength ? " after collapsing whitespace" : ""}; this tool caps "${tool}" at ${maxLength} characters and will shorten it.`);
|
|
55853
56060
|
}
|
|
55854
56061
|
}
|
|
55855
|
-
return Object.fromEntries(fields.filter(({ canonical, commandOnly }) => isFieldApplicable({
|
|
56062
|
+
return Object.fromEntries(fields.filter(({ canonical, commandOnly, events }) => isFieldApplicable({
|
|
55856
56063
|
commandOnly,
|
|
55857
56064
|
hookType
|
|
56065
|
+
}) && isEventApplicable({
|
|
56066
|
+
events,
|
|
56067
|
+
eventName
|
|
55858
56068
|
}) && isValid({
|
|
55859
56069
|
value: def[canonical],
|
|
55860
56070
|
canonical
|
|
@@ -55867,16 +56077,24 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55867
56077
|
* kind has a rule an authored file can plausibly violate — turns a rejected
|
|
55868
56078
|
* value into a warning instead of a silent drop.
|
|
55869
56079
|
*/
|
|
55870
|
-
function importPassthroughFields({ h, hookType, fields, isValid, describeInvalid, warn }) {
|
|
55871
|
-
const
|
|
56080
|
+
function importPassthroughFields({ h, hookType, eventName, fields, isValid, describeInvalid, warn }) {
|
|
56081
|
+
const typeApplicable = fields.filter(({ commandOnly }) => isFieldApplicable({
|
|
55872
56082
|
commandOnly,
|
|
55873
56083
|
hookType
|
|
55874
56084
|
}));
|
|
56085
|
+
const applicable = typeApplicable.filter(({ events }) => isEventApplicable({
|
|
56086
|
+
events,
|
|
56087
|
+
eventName
|
|
56088
|
+
}));
|
|
55875
56089
|
const skipped = fields.filter(({ commandOnly }) => !isFieldApplicable({
|
|
55876
56090
|
commandOnly,
|
|
55877
56091
|
hookType
|
|
55878
56092
|
}));
|
|
55879
56093
|
for (const { tool } of skipped) if (h[tool] !== void 0) warn?.(`Dropping "${tool}" from an imported "${hookType}" hook: this tool documents it on "command" hooks only, so it is not imported.`);
|
|
56094
|
+
for (const { tool, events } of typeApplicable) if (events !== void 0 && !isEventApplicable({
|
|
56095
|
+
events,
|
|
56096
|
+
eventName
|
|
56097
|
+
}) && h[tool] !== void 0 && h[tool] !== false) warn?.(`Dropping "${tool}" from an imported "${hookType}" hook on "${eventName}": this tool honours it on ${describeEvents(events)} only, so it is not imported.`);
|
|
55880
56098
|
for (const { tool, canonical } of applicable) if (describeInvalid !== void 0 && h[tool] !== void 0 && !isValid({
|
|
55881
56099
|
value: h[tool],
|
|
55882
56100
|
canonical
|
|
@@ -56044,11 +56262,11 @@ function isKeywordFieldApplicable({ spec, hookType, eventName, flags, direction,
|
|
|
56044
56262
|
return false;
|
|
56045
56263
|
}
|
|
56046
56264
|
if (hookTypes !== void 0 && !hookTypes.has(hookType)) {
|
|
56047
|
-
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on ${
|
|
56265
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on ${describeEvents(hookTypes)} hooks only, so it is not ${direction}.`);
|
|
56048
56266
|
return false;
|
|
56049
56267
|
}
|
|
56050
56268
|
if (events !== void 0 && !events.has(eventName)) {
|
|
56051
|
-
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${
|
|
56269
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${describeEvents(events)} only, so it is not ${direction}.`);
|
|
56052
56270
|
return false;
|
|
56053
56271
|
}
|
|
56054
56272
|
if (ignoredEvents?.has(eventName)) {
|
|
@@ -56494,6 +56712,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56494
56712
|
...importPassthroughFields({
|
|
56495
56713
|
h,
|
|
56496
56714
|
hookType,
|
|
56715
|
+
eventName,
|
|
56497
56716
|
fields: converterConfig.booleanPassthroughFields ?? [],
|
|
56498
56717
|
isValid: isBooleanValue,
|
|
56499
56718
|
warn
|
|
@@ -56508,6 +56727,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56508
56727
|
...importPassthroughFields({
|
|
56509
56728
|
h,
|
|
56510
56729
|
hookType,
|
|
56730
|
+
eventName,
|
|
56511
56731
|
fields: converterConfig.numberPassthroughFields ?? [],
|
|
56512
56732
|
isValid: isImportableNumber,
|
|
56513
56733
|
describeInvalid: describeInvalidScalar,
|
|
@@ -56516,6 +56736,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56516
56736
|
...importPassthroughFields({
|
|
56517
56737
|
h,
|
|
56518
56738
|
hookType,
|
|
56739
|
+
eventName,
|
|
56519
56740
|
fields: converterConfig.stringPassthroughFields ?? [],
|
|
56520
56741
|
isValid: isImportableString,
|
|
56521
56742
|
describeInvalid: describeInvalidScalar,
|
|
@@ -56524,6 +56745,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56524
56745
|
...importPassthroughFields({
|
|
56525
56746
|
h,
|
|
56526
56747
|
hookType,
|
|
56748
|
+
eventName,
|
|
56527
56749
|
fields: converterConfig.arrayPassthroughFields ?? [],
|
|
56528
56750
|
isValid: isImportableArray,
|
|
56529
56751
|
describeInvalid: describeInvalidArray,
|
|
@@ -56532,6 +56754,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56532
56754
|
...importPassthroughFields({
|
|
56533
56755
|
h,
|
|
56534
56756
|
hookType,
|
|
56757
|
+
eventName,
|
|
56535
56758
|
fields: converterConfig.recordPassthroughFields ?? [],
|
|
56536
56759
|
isValid: isEmittableRecord,
|
|
56537
56760
|
describeInvalid: describeInvalidRecord,
|
|
@@ -56540,6 +56763,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56540
56763
|
...importPassthroughFields({
|
|
56541
56764
|
h,
|
|
56542
56765
|
hookType,
|
|
56766
|
+
eventName,
|
|
56543
56767
|
fields: converterConfig.objectPassthroughFields ?? [],
|
|
56544
56768
|
isValid: isImportableObject,
|
|
56545
56769
|
describeInvalid: describeInvalidObject,
|
|
@@ -61372,6 +61596,11 @@ const GROKCLI_CONVERTER_CONFIG = {
|
|
|
61372
61596
|
tool: "env",
|
|
61373
61597
|
commandOnly: true
|
|
61374
61598
|
}],
|
|
61599
|
+
booleanPassthroughFields: [{
|
|
61600
|
+
canonical: "failClosed",
|
|
61601
|
+
tool: "failClosed",
|
|
61602
|
+
events: /* @__PURE__ */ new Set(["preToolUse", "beforeSubmitPrompt"])
|
|
61603
|
+
}],
|
|
61375
61604
|
arrayPassthroughFields: [{
|
|
61376
61605
|
canonical: "args",
|
|
61377
61606
|
tool: "args",
|
|
@@ -63021,6 +63250,15 @@ const LETTACODE_CONVERTER_CONFIG = {
|
|
|
63021
63250
|
timeoutUnit: "milliseconds"
|
|
63022
63251
|
};
|
|
63023
63252
|
/**
|
|
63253
|
+
* Letta Code documents settings-file hooks as deprecated in favor of Mods
|
|
63254
|
+
* (https://docs.letta.com/reference/deprecated/hooks) but still ships the
|
|
63255
|
+
* loader, so rulesync keeps emitting them and tells the user once per run.
|
|
63256
|
+
* Mods are not modeled: they are JS/TS code modules loaded only from
|
|
63257
|
+
* `~/.letta/mods/` or an agent's memory directory, with no declarative or
|
|
63258
|
+
* project-local form a canonical hook could be translated into.
|
|
63259
|
+
*/
|
|
63260
|
+
const LETTACODE_HOOKS_DEPRECATION_WARNING = "Letta Code documents settings.json hooks as deprecated in favor of Mods and may remove them in a future release; rulesync still writes them while Letta Code loads them. See https://docs.letta.com/reference/deprecated/hooks";
|
|
63261
|
+
/**
|
|
63024
63262
|
* Letta Code keeps a boolean `disabled` switch beside the event arrays inside
|
|
63025
63263
|
* `hooks`. rulesync owns the whole `hooks` key, so the switch is read back from
|
|
63026
63264
|
* the existing file and carried into the generated one instead of being lost.
|
|
@@ -63079,6 +63317,11 @@ function parseLettacodeSettings$1(fileContent, filePath) {
|
|
|
63079
63317
|
* and UI preferences, ...), so generation merges the `hooks` key into the
|
|
63080
63318
|
* existing file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it.
|
|
63081
63319
|
*
|
|
63320
|
+
* Letta Code marks this surface deprecated in favor of Mods (see
|
|
63321
|
+
* `LETTACODE_HOOKS_DEPRECATION_WARNING`); generation warns once per run when it
|
|
63322
|
+
* writes any hook.
|
|
63323
|
+
*
|
|
63324
|
+
* @see https://docs.letta.com/reference/deprecated/hooks
|
|
63082
63325
|
* @see https://github.com/letta-ai/letta-code/blob/main/src/hooks/types.ts
|
|
63083
63326
|
* @see https://github.com/letta-ai/letta-code/blob/main/src/hooks/loader.ts
|
|
63084
63327
|
*/
|
|
@@ -63123,6 +63366,7 @@ var LettacodeHooks = class LettacodeHooks extends ToolHooks {
|
|
|
63123
63366
|
converterConfig: LETTACODE_CONVERTER_CONFIG,
|
|
63124
63367
|
logger
|
|
63125
63368
|
});
|
|
63369
|
+
if (Object.keys(hooks).length > 0) warnOnceWithFallback(logger, LETTACODE_HOOKS_DEPRECATION_WARNING);
|
|
63126
63370
|
const fileContent = applySharedConfigPatch({
|
|
63127
63371
|
fileKey: sharedConfigFileKey(paths),
|
|
63128
63372
|
feature: "hooks",
|
|
@@ -65120,11 +65364,38 @@ const VIBE_DIR = ".vibe";
|
|
|
65120
65364
|
const VIBE_HOOKS_FILE_NAME = "hooks.toml";
|
|
65121
65365
|
/**
|
|
65122
65366
|
* Vibe scopes the tool-name `match` glob and the `strict` flag to tool hooks
|
|
65123
|
-
* (`pre_tool` / `post_tool`)
|
|
65124
|
-
*
|
|
65125
|
-
* @
|
|
65367
|
+
* (`pre_tool` / `post_tool`). `post_agent` takes a `match` too since v3.0.3,
|
|
65368
|
+
* but on the name of the agent that finished its turn, not a tool name (see
|
|
65369
|
+
* {@link VIBE_MAIN_AGENT_MATCH}).
|
|
65370
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
65126
65371
|
*/
|
|
65127
65372
|
const VIBE_TOOL_EVENTS = /* @__PURE__ */ new Set(["pre_tool", "post_tool"]);
|
|
65373
|
+
/** The Vibe event both canonical `stop` and `subagentStop` become. */
|
|
65374
|
+
const VIBE_POST_AGENT_EVENT = "post_agent";
|
|
65375
|
+
/**
|
|
65376
|
+
* The reserved agent alias a `post_agent` hook's `match` uses to run only
|
|
65377
|
+
* after the main agent. Without a `match`, `post_agent` runs after every
|
|
65378
|
+
* agent, subagents included (v3.0.2+), whereas canonical `stop` is
|
|
65379
|
+
* main-agent-only — so `stop` is written with this match.
|
|
65380
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
65381
|
+
*/
|
|
65382
|
+
const VIBE_MAIN_AGENT_MATCH = "main";
|
|
65383
|
+
/**
|
|
65384
|
+
* The `post_agent` `match` for a canonical `subagentStop` hook, or null to
|
|
65385
|
+
* skip it. Vibe has no selector for "every subagent but not the main agent",
|
|
65386
|
+
* so a matcher naming the subagent is required.
|
|
65387
|
+
*/
|
|
65388
|
+
function toVibeSubagentMatch({ matcher, logger }) {
|
|
65389
|
+
if (typeof matcher !== "string" || matcher === "" || matcher === "*") {
|
|
65390
|
+
logger?.warn("Vibe hooks: skipping a \"subagentStop\" hook without an agent-name matcher. Vibe's post_agent cannot select every subagent without also running after the main agent; set \"matcher\" to the subagent name.");
|
|
65391
|
+
return null;
|
|
65392
|
+
}
|
|
65393
|
+
if (matcher.toLowerCase() === VIBE_MAIN_AGENT_MATCH) {
|
|
65394
|
+
logger?.warn("Vibe hooks: skipping a \"subagentStop\" hook whose matcher is \"main\". Vibe reserves \"main\" for the main agent; use a \"stop\" hook instead.");
|
|
65395
|
+
return null;
|
|
65396
|
+
}
|
|
65397
|
+
return matcher;
|
|
65398
|
+
}
|
|
65128
65399
|
const SUPPORTED_VIBE_EVENTS = new Set(VIBE_HOOK_EVENTS);
|
|
65129
65400
|
/**
|
|
65130
65401
|
* The `match` a canonical `Bash` matcher becomes. Vibe tests `match` against a
|
|
@@ -65148,7 +65419,8 @@ function fromVibeHookMatch(match) {
|
|
|
65148
65419
|
/**
|
|
65149
65420
|
* Build the flat `[[hooks]]` array for `.vibe/hooks.toml` from a canonical
|
|
65150
65421
|
* hooks config. Vibe uses a flat array where each entry carries its own event
|
|
65151
|
-
* `type`,
|
|
65422
|
+
* `type`, `match` glob/regex (a tool name for tool hooks, an agent name
|
|
65423
|
+
* for `post_agent`), and `command`. Only `type: "command"`
|
|
65152
65424
|
* canonical hooks are emitted: a Vibe hook is a shell command line. (v2.25.1
|
|
65153
65425
|
* to v2.25.4 briefly exec'd it without a shell under the unified harness and
|
|
65154
65426
|
* skipped any command containing a backslash; v2.25.5 restored shell
|
|
@@ -65156,7 +65428,7 @@ function fromVibeHookMatch(match) {
|
|
|
65156
65428
|
* now run through the shell" — and dropped the backslash skip.)
|
|
65157
65429
|
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.2/README.md
|
|
65158
65430
|
*/
|
|
65159
|
-
function canonicalToVibeHooks({ config, toolOverride }) {
|
|
65431
|
+
function canonicalToVibeHooks({ config, toolOverride, logger }) {
|
|
65160
65432
|
const shared = {};
|
|
65161
65433
|
for (const [event, defs] of Object.entries(config.hooks)) if (SUPPORTED_VIBE_EVENTS.has(event)) shared[event] = defs;
|
|
65162
65434
|
const effective = {
|
|
@@ -65164,30 +65436,40 @@ function canonicalToVibeHooks({ config, toolOverride }) {
|
|
|
65164
65436
|
...toolOverride
|
|
65165
65437
|
};
|
|
65166
65438
|
const hooks = [];
|
|
65439
|
+
const nextIndexByVibeEvent = /* @__PURE__ */ new Map();
|
|
65167
65440
|
for (const [event, defs] of Object.entries(effective)) {
|
|
65168
65441
|
if (!SUPPORTED_VIBE_EVENTS.has(event)) continue;
|
|
65169
65442
|
const vibeEvent = lookupOwn({
|
|
65170
65443
|
record: CANONICAL_TO_VIBE_EVENT_NAMES,
|
|
65171
65444
|
key: event
|
|
65172
65445
|
}) ?? event;
|
|
65173
|
-
let index = 0;
|
|
65174
65446
|
for (const def of defs) {
|
|
65175
65447
|
if ((def.type ?? "command") !== "command") continue;
|
|
65176
65448
|
if (typeof def.command !== "string") continue;
|
|
65177
|
-
const name = typeof def.name === "string" ? def.name : `${vibeEvent}-${index}`;
|
|
65178
65449
|
const isToolEvent = VIBE_TOOL_EVENTS.has(vibeEvent);
|
|
65450
|
+
let match;
|
|
65451
|
+
if (isToolEvent) match = toVibeHookMatch(def.matcher);
|
|
65452
|
+
else if (event === "subagentStop") {
|
|
65453
|
+
const subagentMatch = toVibeSubagentMatch({
|
|
65454
|
+
matcher: def.matcher,
|
|
65455
|
+
logger
|
|
65456
|
+
});
|
|
65457
|
+
if (subagentMatch === null) continue;
|
|
65458
|
+
match = subagentMatch;
|
|
65459
|
+
} else match = VIBE_MAIN_AGENT_MATCH;
|
|
65460
|
+
const index = nextIndexByVibeEvent.get(vibeEvent) ?? 0;
|
|
65179
65461
|
const entry = {
|
|
65180
|
-
name
|
|
65462
|
+
name: typeof def.name === "string" ? def.name : `${vibeEvent}-${index}`,
|
|
65181
65463
|
type: vibeEvent,
|
|
65464
|
+
match,
|
|
65182
65465
|
command: def.command
|
|
65183
65466
|
};
|
|
65184
|
-
if (isToolEvent) entry.match = toVibeHookMatch(def.matcher);
|
|
65185
65467
|
if (typeof def.timeout === "number") entry.timeout = def.timeout;
|
|
65186
65468
|
const strict = def.strict;
|
|
65187
65469
|
if (isToolEvent && typeof strict === "boolean") entry.strict = strict;
|
|
65188
65470
|
if (typeof def.description === "string") entry.description = def.description;
|
|
65189
65471
|
hooks.push(entry);
|
|
65190
|
-
index
|
|
65472
|
+
nextIndexByVibeEvent.set(vibeEvent, index + 1);
|
|
65191
65473
|
}
|
|
65192
65474
|
}
|
|
65193
65475
|
return { hooks };
|
|
@@ -65199,14 +65481,22 @@ function vibeEntryToCanonicalDef(raw) {
|
|
|
65199
65481
|
const vibeEvent = typeof entry.type === "string" ? entry.type : void 0;
|
|
65200
65482
|
if (vibeEvent === void 0) return null;
|
|
65201
65483
|
if (isPrototypePollutionKey(vibeEvent)) return null;
|
|
65202
|
-
|
|
65484
|
+
let canonicalEvent = lookupOwn({
|
|
65203
65485
|
record: VIBE_TO_CANONICAL_EVENT_NAMES,
|
|
65204
65486
|
key: vibeEvent
|
|
65205
65487
|
}) ?? vibeEvent;
|
|
65206
65488
|
const def = { type: "command" };
|
|
65207
65489
|
if (typeof entry.command === "string") def.command = entry.command;
|
|
65208
|
-
|
|
65209
|
-
|
|
65490
|
+
if (vibeEvent === VIBE_POST_AGENT_EVENT) {
|
|
65491
|
+
const agentMatch = typeof entry.match === "string" ? entry.match : "";
|
|
65492
|
+
if (agentMatch !== "" && agentMatch !== "*" && agentMatch.toLowerCase() !== VIBE_MAIN_AGENT_MATCH) {
|
|
65493
|
+
canonicalEvent = "subagentStop";
|
|
65494
|
+
def.matcher = agentMatch;
|
|
65495
|
+
}
|
|
65496
|
+
} else {
|
|
65497
|
+
const matcher = fromVibeHookMatch(entry.match);
|
|
65498
|
+
if (matcher !== void 0) def.matcher = matcher;
|
|
65499
|
+
}
|
|
65210
65500
|
if (typeof entry.timeout === "number") def.timeout = entry.timeout;
|
|
65211
65501
|
if (typeof entry.name === "string") def.name = entry.name;
|
|
65212
65502
|
if (typeof entry.description === "string") def.description = entry.description;
|
|
@@ -65237,6 +65527,26 @@ function vibeHooksToCanonical(parsed) {
|
|
|
65237
65527
|
}
|
|
65238
65528
|
return canonical;
|
|
65239
65529
|
}
|
|
65530
|
+
/** Vibe reads a `match` with a `re:` prefix or glob metacharacters as a pattern. */
|
|
65531
|
+
const VIBE_PATTERN_MATCH = /^re:|[*?[]/;
|
|
65532
|
+
/**
|
|
65533
|
+
* Move the imported `subagentStop` hooks into the `vibe` override when any of
|
|
65534
|
+
* them matches agents by a regex or glob rather than by a literal name. Such a
|
|
65535
|
+
* pattern can also select the main agent (a main session answers to its mode
|
|
65536
|
+
* name, e.g. `accept-edits`), and other tools would read it as their own
|
|
65537
|
+
* matcher syntax, so it is kept Vibe-only. The override replaces the shared
|
|
65538
|
+
* list for its event, so the whole list moves to keep every entry on Vibe.
|
|
65539
|
+
*/
|
|
65540
|
+
function takeVibeOnlySubagentStop({ hooks, logger }) {
|
|
65541
|
+
const defs = lookupOwn({
|
|
65542
|
+
record: hooks,
|
|
65543
|
+
key: "subagentStop"
|
|
65544
|
+
});
|
|
65545
|
+
if (defs === void 0 || !defs.some((def) => typeof def.matcher === "string" && VIBE_PATTERN_MATCH.test(def.matcher))) return;
|
|
65546
|
+
delete hooks.subagentStop;
|
|
65547
|
+
logger?.warn("Vibe hooks: a post_agent hook matches agents by a pattern, so its subagentStop hooks are imported into the vibe override instead of the shared hooks.");
|
|
65548
|
+
return defs;
|
|
65549
|
+
}
|
|
65240
65550
|
function parseVibeToml(fileContent) {
|
|
65241
65551
|
const parsed = smolToml.parse(fileContent || smolToml.stringify({}));
|
|
65242
65552
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
@@ -65276,12 +65586,13 @@ var VibeHooks = class extends ToolHooks {
|
|
|
65276
65586
|
validate
|
|
65277
65587
|
});
|
|
65278
65588
|
}
|
|
65279
|
-
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false }) {
|
|
65589
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
65280
65590
|
const paths = this.getSettablePaths({ global });
|
|
65281
65591
|
const config = rulesyncHooks.getJson();
|
|
65282
65592
|
const vibeHooks = canonicalToVibeHooks({
|
|
65283
65593
|
config,
|
|
65284
|
-
toolOverride: config.vibe?.hooks
|
|
65594
|
+
toolOverride: config.vibe?.hooks,
|
|
65595
|
+
logger
|
|
65285
65596
|
});
|
|
65286
65597
|
const fileContent = smolToml.stringify(vibeHooks);
|
|
65287
65598
|
return new this({
|
|
@@ -65292,15 +65603,30 @@ var VibeHooks = class extends ToolHooks {
|
|
|
65292
65603
|
validate
|
|
65293
65604
|
});
|
|
65294
65605
|
}
|
|
65295
|
-
toRulesyncHooks() {
|
|
65606
|
+
toRulesyncHooks(options = {}) {
|
|
65296
65607
|
const hooks = vibeHooksToCanonical(this.parseFileContentOrThrow({
|
|
65297
65608
|
displayName: "Vibe",
|
|
65298
65609
|
parse: (fileContent) => parseVibeToml(fileContent)
|
|
65299
65610
|
}));
|
|
65300
|
-
|
|
65611
|
+
const vibeOnlySubagentStop = takeVibeOnlySubagentStop({
|
|
65612
|
+
hooks,
|
|
65613
|
+
logger: options.logger
|
|
65614
|
+
});
|
|
65615
|
+
const config = buildImportedHooksConfig({
|
|
65301
65616
|
hooks,
|
|
65302
65617
|
overrideKey: "vibe"
|
|
65303
|
-
})
|
|
65618
|
+
});
|
|
65619
|
+
if (vibeOnlySubagentStop !== void 0) {
|
|
65620
|
+
const override = config.vibe ?? {};
|
|
65621
|
+
config.vibe = {
|
|
65622
|
+
...override,
|
|
65623
|
+
hooks: {
|
|
65624
|
+
...override.hooks,
|
|
65625
|
+
subagentStop: vibeOnlySubagentStop
|
|
65626
|
+
}
|
|
65627
|
+
};
|
|
65628
|
+
}
|
|
65629
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(config, null, 2) });
|
|
65304
65630
|
}
|
|
65305
65631
|
validate() {
|
|
65306
65632
|
try {
|
|
@@ -76917,6 +77243,12 @@ const REASONIX_PLUGIN_FIELDS = [
|
|
|
76917
77243
|
"load",
|
|
76918
77244
|
"oauth_allow_missing_pkce_metadata"
|
|
76919
77245
|
];
|
|
77246
|
+
/**
|
|
77247
|
+
* The `load` mode that puts a server's tools in the provider schema. Reasonix
|
|
77248
|
+
* compares it case-insensitively after trimming (`normalizeMCPLoad`).
|
|
77249
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/studio-v2.53.0/internal/contract/config/mcp_load.go
|
|
77250
|
+
*/
|
|
77251
|
+
const REASONIX_MCP_LOAD_ALWAYS = "always";
|
|
76920
77252
|
/** The `[[plugins]]` key that canonical `disabledTools` is written to and read from. */
|
|
76921
77253
|
const REASONIX_DISABLED_TOOLS_KEY = "disabled_tools";
|
|
76922
77254
|
var ReasonixMcp = class ReasonixMcp extends ToolMcp {
|
|
@@ -77081,7 +77413,9 @@ function hasExpectedType(value, expected) {
|
|
|
77081
77413
|
* `concurrency` string decodes fine but is ignored in favor of the server-name
|
|
77082
77414
|
* default, so it is dropped too rather than written as a setting that does
|
|
77083
77415
|
* nothing. An unknown `load` string is read as `deferred`, the default, so it
|
|
77084
|
-
* passes through as authored.
|
|
77416
|
+
* passes through as authored. A project `load = "always"` also passes through,
|
|
77417
|
+
* but is warned about in `writePassthroughFields`: see the v2.53.0 note on
|
|
77418
|
+
* `REASONIX_PLUGIN_FIELDS`.
|
|
77085
77419
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/v2.31.0/internal/contract/config/plugin_entry.go
|
|
77086
77420
|
*/
|
|
77087
77421
|
function invalidPassthroughFieldReason(field, value) {
|
|
@@ -77121,6 +77455,7 @@ function writePassthroughFields({ name, server, plugin, global, logger }) {
|
|
|
77121
77455
|
continue;
|
|
77122
77456
|
}
|
|
77123
77457
|
if (field === "oauth_allow_missing_pkce_metadata" && value === true) logger?.warn(`Reasonix MCP: "${name}" sets "oauth_allow_missing_pkce_metadata", which lets OAuth proceed with an authorization server that does not advertise PKCE support.` + (global ? "" : ` Reasonix honours it only from the user config, not a project reasonix.toml.`));
|
|
77458
|
+
if (field === "load" && !global && server.disabled !== true && typeof value === "string" && value.trim().toLowerCase() === REASONIX_MCP_LOAD_ALWAYS) logger?.warn(`Reasonix MCP: "${name}" sets "load" to "always" in the project reasonix.toml. Reasonix 2.53.0 and later ignore a project-declared load mode and defer the server; set it in the user config (the server's "load" or [tools.mcp_load]) instead.`);
|
|
77124
77459
|
plugin[field] = value;
|
|
77125
77460
|
}
|
|
77126
77461
|
}
|
|
@@ -86059,6 +86394,41 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
86059
86394
|
// src/features/permissions/codebuddy-permissions.ts
|
|
86060
86395
|
/** Top-level key of `.codebuddy/settings.json` that rulesync owns. */
|
|
86061
86396
|
const CODEBUDDY_PERMISSIONS_KEY = "permissions";
|
|
86397
|
+
/**
|
|
86398
|
+
* The top-level settings groups the `codebuddy` override may author, each with
|
|
86399
|
+
* the only keys of that group rulesync writes and imports. A group's other
|
|
86400
|
+
* keys are kept as the file has them; an override key not listed here is
|
|
86401
|
+
* skipped with a warning.
|
|
86402
|
+
*
|
|
86403
|
+
* - `autoReview`: the `auto` permission mode's review policy (`policy`
|
|
86404
|
+
* replaces the built-in security policy, `extraPolicy` is appended to it).
|
|
86405
|
+
* - `autoMode`: the legacy natural-language `auto` rules, still read as
|
|
86406
|
+
* additional policy.
|
|
86407
|
+
* - `memory.parentDiscovery`: whether `CODEBUDDY.md` / `AGENTS.md` are also
|
|
86408
|
+
* discovered in parent directories of the launch directory.
|
|
86409
|
+
*
|
|
86410
|
+
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
86411
|
+
* @see https://www.codebuddy.ai/docs/cli/memory
|
|
86412
|
+
*/
|
|
86413
|
+
const CODEBUDDY_OVERRIDE_GROUP_KEYS = {
|
|
86414
|
+
autoReview: ["policy", "extraPolicy"],
|
|
86415
|
+
autoMode: [
|
|
86416
|
+
"environment",
|
|
86417
|
+
"allow",
|
|
86418
|
+
"soft_deny",
|
|
86419
|
+
"hard_deny"
|
|
86420
|
+
],
|
|
86421
|
+
memory: ["parentDiscovery"]
|
|
86422
|
+
};
|
|
86423
|
+
const CODEBUDDY_OVERRIDE_GROUPS = Object.keys(CODEBUDDY_OVERRIDE_GROUP_KEYS);
|
|
86424
|
+
/**
|
|
86425
|
+
* Groups CodeBuddy reads from the user and project-local settings only: the
|
|
86426
|
+
* shared project `.codebuddy/settings.json` is not read for them, so a checked-in
|
|
86427
|
+
* repository cannot relax the review of its own actions. They are written and
|
|
86428
|
+
* imported in global mode alone.
|
|
86429
|
+
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
86430
|
+
*/
|
|
86431
|
+
const CODEBUDDY_USER_ONLY_GROUPS = /* @__PURE__ */ new Set(["autoReview", "autoMode"]);
|
|
86062
86432
|
/** The rule lists under `permissions`, one per canonical action. */
|
|
86063
86433
|
const CODEBUDDY_RULE_LISTS = [
|
|
86064
86434
|
"allow",
|
|
@@ -86212,6 +86582,87 @@ function buildCodebuddyRuleLists({ config, existingPermissions, logger }) {
|
|
|
86212
86582
|
return lists;
|
|
86213
86583
|
}
|
|
86214
86584
|
/**
|
|
86585
|
+
* Whether a settings value has the shape the `codebuddy` override schema
|
|
86586
|
+
* accepts for that key, so an off-spec value is left in the file instead of
|
|
86587
|
+
* being written or making the imported permissions file fail validation.
|
|
86588
|
+
*/
|
|
86589
|
+
function isLiftableCodebuddyValue(key, value) {
|
|
86590
|
+
switch (key) {
|
|
86591
|
+
case "policy":
|
|
86592
|
+
case "extraPolicy": return typeof value === "string";
|
|
86593
|
+
case "parentDiscovery": return typeof value === "boolean";
|
|
86594
|
+
default: return isStringArray(value);
|
|
86595
|
+
}
|
|
86596
|
+
}
|
|
86597
|
+
/** Name every written override setting that can make the `auto` review more lenient. */
|
|
86598
|
+
function announceCodebuddyReviewSettings({ picked, group, where, logger }) {
|
|
86599
|
+
if (group === "autoReview" && typeof picked.policy === "string") logger?.warn(`CodeBuddy permissions: codebuddy.autoReview.policy was written to ${where}, so it replaces CodeBuddy's built-in security policy for the 'auto' permission mode.`);
|
|
86600
|
+
if (group === "autoReview" && typeof picked.extraPolicy === "string") logger?.warn(`CodeBuddy permissions: codebuddy.autoReview.extraPolicy was written to ${where}, so it is appended to the security policy CodeBuddy's 'auto' permission mode reviews actions against.`);
|
|
86601
|
+
if (group === "autoMode" && toStringArray$1(picked.allow).length > 0) logger?.warn(`CodeBuddy permissions: codebuddy.autoMode.allow was written to ${where}, so CodeBuddy's 'auto' permission mode leans toward approving the actions it describes.`);
|
|
86602
|
+
}
|
|
86603
|
+
/**
|
|
86604
|
+
* The top-level keys the `codebuddy` override contributes to the settings
|
|
86605
|
+
* patch, besides `permissions`. Each group is the existing group with the
|
|
86606
|
+
* authored keys laid over it; a group is only in the patch when authored, so
|
|
86607
|
+
* a hand-set value survives an unrelated regeneration.
|
|
86608
|
+
*/
|
|
86609
|
+
function buildCodebuddyOverridePatch({ override, existing, global, where, logger }) {
|
|
86610
|
+
const patch = {};
|
|
86611
|
+
if (override === void 0) return patch;
|
|
86612
|
+
for (const group of CODEBUDDY_OVERRIDE_GROUPS) {
|
|
86613
|
+
const authored = override[group];
|
|
86614
|
+
if (!isRecord(authored)) continue;
|
|
86615
|
+
if (CODEBUDDY_USER_ONLY_GROUPS.has(group) && !global) {
|
|
86616
|
+
logger?.warn(`CodeBuddy permissions: skipping codebuddy.${group} in project mode — CodeBuddy does not read ${group} from the shared project .codebuddy/settings.json. Run 'rulesync generate --global' to write it to ~/.codebuddy/settings.json.`);
|
|
86617
|
+
continue;
|
|
86618
|
+
}
|
|
86619
|
+
const allowedKeys = CODEBUDDY_OVERRIDE_GROUP_KEYS[group];
|
|
86620
|
+
const picked = {};
|
|
86621
|
+
for (const [key, value] of Object.entries(authored)) {
|
|
86622
|
+
if (isPrototypePollutionKey(key) || value === void 0) continue;
|
|
86623
|
+
if (!allowedKeys.includes(key)) {
|
|
86624
|
+
logger?.warn(`CodeBuddy permissions: skipping codebuddy.${group}.${key} — Rulesync writes only ${allowedKeys.map((k) => `${group}.${k}`).join(", ")} from the codebuddy override; set other keys in ${where} by hand.`);
|
|
86625
|
+
continue;
|
|
86626
|
+
}
|
|
86627
|
+
picked[key] = value;
|
|
86628
|
+
}
|
|
86629
|
+
if (Object.keys(picked).length === 0) continue;
|
|
86630
|
+
const existingValue = existing[group];
|
|
86631
|
+
if (existingValue !== void 0 && !isRecord(existingValue)) {
|
|
86632
|
+
logger?.warn(`CodeBuddy permissions: skipping codebuddy.${group} — ${group} in ${where} is not an object, so Rulesync leaves it as it is. Fix it by hand to let Rulesync write ${group}.`);
|
|
86633
|
+
continue;
|
|
86634
|
+
}
|
|
86635
|
+
patch[group] = {
|
|
86636
|
+
...existingValue,
|
|
86637
|
+
...picked
|
|
86638
|
+
};
|
|
86639
|
+
announceCodebuddyReviewSettings({
|
|
86640
|
+
picked,
|
|
86641
|
+
group,
|
|
86642
|
+
where,
|
|
86643
|
+
logger
|
|
86644
|
+
});
|
|
86645
|
+
}
|
|
86646
|
+
return patch;
|
|
86647
|
+
}
|
|
86648
|
+
/**
|
|
86649
|
+
* Lift the settings the `codebuddy` override authors back out of a settings
|
|
86650
|
+
* file. `autoReview` and `autoMode` are lifted only from the user file, the
|
|
86651
|
+
* one of the two rulesync manages that CodeBuddy reads them from.
|
|
86652
|
+
*/
|
|
86653
|
+
function liftCodebuddyOverride({ settings, global }) {
|
|
86654
|
+
const lifted = {};
|
|
86655
|
+
for (const group of CODEBUDDY_OVERRIDE_GROUPS) {
|
|
86656
|
+
if (CODEBUDDY_USER_ONLY_GROUPS.has(group) && !global) continue;
|
|
86657
|
+
const existingGroup = settings[group];
|
|
86658
|
+
if (!isRecord(existingGroup)) continue;
|
|
86659
|
+
const picked = {};
|
|
86660
|
+
for (const key of CODEBUDDY_OVERRIDE_GROUP_KEYS[group]) if (Object.hasOwn(existingGroup, key) && isLiftableCodebuddyValue(key, existingGroup[key])) picked[key] = existingGroup[key];
|
|
86661
|
+
if (Object.keys(picked).length > 0) lifted[group] = picked;
|
|
86662
|
+
}
|
|
86663
|
+
return lifted;
|
|
86664
|
+
}
|
|
86665
|
+
/**
|
|
86215
86666
|
* CodeBuddy Code permissions.
|
|
86216
86667
|
*
|
|
86217
86668
|
* Rules live under the `permissions` key of `.codebuddy/settings.json`
|
|
@@ -86222,7 +86673,10 @@ function buildCodebuddyRuleLists({ config, existingPermissions, logger }) {
|
|
|
86222
86673
|
* writes go through the shared-config gateway, the sibling keys of the rule
|
|
86223
86674
|
* lists (`defaultMode`, `additionalDirectories`, `trustedDirectories`, ...)
|
|
86224
86675
|
* are kept, and the file is never deleted. The gitignored
|
|
86225
|
-
* `.codebuddy/settings.local.json` layer is left to CodeBuddy.
|
|
86676
|
+
* `.codebuddy/settings.local.json` layer is left to CodeBuddy. The
|
|
86677
|
+
* `codebuddy` override's other settings (`autoReview`, `autoMode`,
|
|
86678
|
+
* `memory.parentDiscovery`) go through `buildCodebuddyOverridePatch` /
|
|
86679
|
+
* `liftCodebuddyOverride`.
|
|
86226
86680
|
*
|
|
86227
86681
|
* @see https://www.codebuddy.ai/docs/cli/permissions
|
|
86228
86682
|
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
@@ -86261,8 +86715,9 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86261
86715
|
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
86262
86716
|
});
|
|
86263
86717
|
const existingPermissions = isRecord(existing[CODEBUDDY_PERMISSIONS_KEY]) ? existing[CODEBUDDY_PERMISSIONS_KEY] : {};
|
|
86718
|
+
const config = rulesyncPermissions.getJson();
|
|
86264
86719
|
const lists = buildCodebuddyRuleLists({
|
|
86265
|
-
config
|
|
86720
|
+
config,
|
|
86266
86721
|
existingPermissions,
|
|
86267
86722
|
logger
|
|
86268
86723
|
});
|
|
@@ -86277,7 +86732,16 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86277
86732
|
fileKey: sharedConfigFileKey(paths),
|
|
86278
86733
|
feature: "permissions",
|
|
86279
86734
|
existingContent,
|
|
86280
|
-
patch: {
|
|
86735
|
+
patch: {
|
|
86736
|
+
[CODEBUDDY_PERMISSIONS_KEY]: Object.keys(permissions).length > 0 ? permissions : void 0,
|
|
86737
|
+
...buildCodebuddyOverridePatch({
|
|
86738
|
+
override: config.codebuddy,
|
|
86739
|
+
existing,
|
|
86740
|
+
global,
|
|
86741
|
+
where: `${join(paths.relativeDirPath, paths.relativeFilePath)}${global ? " in the home directory" : ""}`,
|
|
86742
|
+
logger
|
|
86743
|
+
})
|
|
86744
|
+
},
|
|
86281
86745
|
filePath
|
|
86282
86746
|
}),
|
|
86283
86747
|
validate: true,
|
|
@@ -86298,7 +86762,14 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86298
86762
|
const existing = rules[parsed.pattern];
|
|
86299
86763
|
if (existing === void 0 || PERMISSION_ACTION_PRIORITY[action] > PERMISSION_ACTION_PRIORITY[existing]) rules[parsed.pattern] = action;
|
|
86300
86764
|
}
|
|
86301
|
-
|
|
86765
|
+
const codebuddy = liftCodebuddyOverride({
|
|
86766
|
+
settings,
|
|
86767
|
+
global: this.global
|
|
86768
|
+
});
|
|
86769
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
86770
|
+
permission,
|
|
86771
|
+
...Object.keys(codebuddy).length > 0 && { codebuddy }
|
|
86772
|
+
}, null, 2) });
|
|
86302
86773
|
}
|
|
86303
86774
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
86304
86775
|
return new CodebuddyPermissions({
|
|
@@ -89264,7 +89735,7 @@ const AUTO_APPROVE_KEYS = {
|
|
|
89264
89735
|
*
|
|
89265
89736
|
* @see https://code.visualstudio.com/docs/agents/reference/ai-settings
|
|
89266
89737
|
*/
|
|
89267
|
-
const DEFAULT_PERMISSION_MODE_KEY = "chat.permissions.default";
|
|
89738
|
+
const DEFAULT_PERMISSION_MODE_KEY$1 = "chat.permissions.default";
|
|
89268
89739
|
/**
|
|
89269
89740
|
* The prefix every Agent Host sandbox setting shares. A `copilot.sandbox` leaf
|
|
89270
89741
|
* at path `a.b` is written to `chat.agent.sandbox.a.b`.
|
|
@@ -89552,7 +90023,7 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
89552
90023
|
});
|
|
89553
90024
|
}
|
|
89554
90025
|
const defaultPermissionMode = config.copilot?.defaultPermissionMode;
|
|
89555
|
-
if (defaultPermissionMode !== void 0) patch[DEFAULT_PERMISSION_MODE_KEY] = defaultPermissionMode;
|
|
90026
|
+
if (defaultPermissionMode !== void 0) patch[DEFAULT_PERMISSION_MODE_KEY$1] = defaultPermissionMode;
|
|
89556
90027
|
const sandbox = config.copilot?.sandbox;
|
|
89557
90028
|
if (sandbox !== void 0) {
|
|
89558
90029
|
Object.assign(patch, buildSandboxPatch({
|
|
@@ -89608,7 +90079,7 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
89608
90079
|
for (const [pattern, flag] of Object.entries(autoApprove)) rules[pattern] = flag ? "allow" : "deny";
|
|
89609
90080
|
if (Object.keys(rules).length > 0) permission[category] = rules;
|
|
89610
90081
|
}
|
|
89611
|
-
const defaultPermissionMode = settings[DEFAULT_PERMISSION_MODE_KEY];
|
|
90082
|
+
const defaultPermissionMode = settings[DEFAULT_PERMISSION_MODE_KEY$1];
|
|
89612
90083
|
const sandbox = readSandboxFromSettings(settings);
|
|
89613
90084
|
const copilot = {
|
|
89614
90085
|
...isCopilotDefaultPermissionMode(defaultPermissionMode) && { defaultPermissionMode },
|
|
@@ -89641,6 +90112,16 @@ const WEBFETCH_CATEGORY$1 = "webfetch";
|
|
|
89641
90112
|
/** `~/.copilot/settings.json` and `.github/copilot/settings.json` keys. */
|
|
89642
90113
|
const ALLOWED_URLS_KEY = "allowedUrls";
|
|
89643
90114
|
const DENIED_URLS_KEY = "deniedUrls";
|
|
90115
|
+
/**
|
|
90116
|
+
* `~/.copilot/settings.json` only: the startup permission mode for new
|
|
90117
|
+
* interactive sessions, authored through the `copilotcli.defaultPermissionMode`
|
|
90118
|
+
* override. It is a user setting — the repository settings file does not list
|
|
90119
|
+
* it among its repo-overridable keys.
|
|
90120
|
+
*/
|
|
90121
|
+
const DEFAULT_PERMISSION_MODE_KEY = "defaultPermissionMode";
|
|
90122
|
+
function isCopilotcliDefaultPermissionMode(value) {
|
|
90123
|
+
return COPILOTCLI_DEFAULT_PERMISSION_MODES.some((mode) => mode === value);
|
|
90124
|
+
}
|
|
89644
90125
|
function toUrlList(value) {
|
|
89645
90126
|
if (!Array.isArray(value)) return [];
|
|
89646
90127
|
return value.filter((entry) => typeof entry === "string");
|
|
@@ -89682,6 +90163,13 @@ function splitUrlRules(rules) {
|
|
|
89682
90163
|
* top-level keys in the user `settings.json`, so only documented keys are ever
|
|
89683
90164
|
* emitted there either.)
|
|
89684
90165
|
*
|
|
90166
|
+
* The `copilotcli.defaultPermissionMode` override (`manual` | `assisted` |
|
|
90167
|
+
* `allow-all`) writes the user-scope `defaultPermissionMode` key in global mode
|
|
90168
|
+
* only; in project mode it is skipped with a warning, since the repository
|
|
90169
|
+
* settings file does not accept it. It is patched only when authored, so a mode
|
|
90170
|
+
* the user picked through `/settings` otherwise survives. Writing `allow-all`
|
|
90171
|
+
* warns, because it makes new sessions skip every approval prompt.
|
|
90172
|
+
*
|
|
89685
90173
|
* @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
89686
90174
|
* @see https://github.com/github/copilot-cli/blob/main/changelog.md
|
|
89687
90175
|
*/
|
|
@@ -89724,7 +90212,8 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89724
90212
|
const paths = CopilotcliPermissions.getSettablePaths({ global });
|
|
89725
90213
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
89726
90214
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
89727
|
-
const
|
|
90215
|
+
const config = rulesyncPermissions.getJson();
|
|
90216
|
+
const rules = config.permission[WEBFETCH_CATEGORY$1];
|
|
89728
90217
|
const patch = {};
|
|
89729
90218
|
if (rules !== void 0) {
|
|
89730
90219
|
const { allowedUrls, deniedUrls } = splitUrlRules(rules);
|
|
@@ -89732,6 +90221,13 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89732
90221
|
if (global) patch[ALLOWED_URLS_KEY] = allowedUrls.length > 0 ? allowedUrls : void 0;
|
|
89733
90222
|
else if (allowedUrls.length > 0) logger?.warn(`Copilot CLI permissions: dropping ${allowedUrls.length} "webfetch" allow rule(s) at project scope — ${join(paths.relativeDirPath, paths.relativeFilePath)} does not accept "${ALLOWED_URLS_KEY}" (repository settings may only add denials). Author allow rules in global scope (\`--global\`) instead.`);
|
|
89734
90223
|
}
|
|
90224
|
+
const defaultPermissionMode = config.copilotcli?.defaultPermissionMode;
|
|
90225
|
+
if (defaultPermissionMode !== void 0) {
|
|
90226
|
+
if (global) {
|
|
90227
|
+
patch[DEFAULT_PERMISSION_MODE_KEY] = defaultPermissionMode;
|
|
90228
|
+
if (defaultPermissionMode === "allow-all") logger?.warn(`Copilot CLI permissions: writing "${DEFAULT_PERMISSION_MODE_KEY}": "allow-all" to ${join(paths.relativeDirPath, paths.relativeFilePath)} — new interactive sessions will start with every tool, path and URL approved without prompting (equivalent to \`--allow-all\`). Deny rules and enterprise policy still apply.`);
|
|
90229
|
+
} else logger?.warn(`Copilot CLI permissions: skipping "copilotcli.${DEFAULT_PERMISSION_MODE_KEY}" at project scope — ${join(paths.relativeDirPath, paths.relativeFilePath)} does not accept "${DEFAULT_PERMISSION_MODE_KEY}" (it is a user setting). Author it in global scope (\`--global\`) instead.`);
|
|
90230
|
+
}
|
|
89735
90231
|
return new CopilotcliPermissions({
|
|
89736
90232
|
outputRoot,
|
|
89737
90233
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -89753,7 +90249,11 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89753
90249
|
for (const pattern of toUrlList(settings[DENIED_URLS_KEY])) rules[pattern] = "deny";
|
|
89754
90250
|
if (this.global) for (const pattern of toUrlList(settings[ALLOWED_URLS_KEY])) rules[pattern] ??= "allow";
|
|
89755
90251
|
const permission = Object.keys(rules).length > 0 ? { [WEBFETCH_CATEGORY$1]: rules } : {};
|
|
89756
|
-
|
|
90252
|
+
const defaultPermissionMode = this.global ? settings[DEFAULT_PERMISSION_MODE_KEY] : void 0;
|
|
90253
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
90254
|
+
permission,
|
|
90255
|
+
...isCopilotcliDefaultPermissionMode(defaultPermissionMode) && { copilotcli: { defaultPermissionMode } }
|
|
90256
|
+
}, null, 2) });
|
|
89757
90257
|
}
|
|
89758
90258
|
parseSettings() {
|
|
89759
90259
|
const relativePath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
@@ -95258,6 +95758,7 @@ var MimocodePermissions = class extends OpencodePermissions {
|
|
|
95258
95758
|
// src/features/permissions/omp-permissions.ts
|
|
95259
95759
|
const CATCH_ALL_PATTERN$5 = "*";
|
|
95260
95760
|
const APPROVAL_MODE_KEY = "approvalMode";
|
|
95761
|
+
const RESTRICTED_FOLDERS_KEY = "restrictedFolders";
|
|
95261
95762
|
const ACTION_TO_OMP_APPROVAL = {
|
|
95262
95763
|
allow: "allow",
|
|
95263
95764
|
ask: "prompt",
|
|
@@ -95296,9 +95797,9 @@ const MANAGED_OMP_TOOLS = /* @__PURE__ */ new Set([
|
|
|
95296
95797
|
*
|
|
95297
95798
|
* - `tools.approval.<tool>: allow | prompt | deny` is a per-tool policy honored
|
|
95298
95799
|
* in every approval mode. A category's catch-all `*` rule maps here
|
|
95299
|
-
* (`ask` → `prompt`).
|
|
95300
|
-
* with pattern-specific rules collapses to one action (deny > ask >
|
|
95301
|
-
* an implicit `ask` when there is no catch-all) and a warning.
|
|
95800
|
+
* (`ask` → `prompt`). `tools.approval` has no per-path matcher, so a non-bash
|
|
95801
|
+
* category with pattern-specific rules collapses to one action (deny > ask >
|
|
95802
|
+
* allow, with an implicit `ask` when there is no catch-all) and a warning.
|
|
95302
95803
|
* - `bash.patterns: [{ match, approval }]` is an ordered, first-match-wins list
|
|
95303
95804
|
* of command globs (only `*` is a wildcard). Every `bash` rule except a
|
|
95304
95805
|
* catch-all `allow` is written there, ordered deny → prompt → allow so that
|
|
@@ -95315,12 +95816,15 @@ const MANAGED_OMP_TOOLS = /* @__PURE__ */ new Set([
|
|
|
95315
95816
|
*
|
|
95316
95817
|
* The `omp.approvalMode` override writes `tools.approvalMode` (`always-ask`,
|
|
95317
95818
|
* `always-ask-cached`, `write` or `yolo`); without it the key is left as it is in the file, and on
|
|
95318
|
-
* import a recognized value is read back into the override.
|
|
95819
|
+
* import a recognized value is read back into the override. Likewise the
|
|
95820
|
+
* `omp.restrictedFolders` override writes `tools.restrictedFolders` (folders
|
|
95821
|
+
* where every action prompts in every approval mode, `yolo` included), is left
|
|
95822
|
+
* alone when absent, and is read back on import.
|
|
95319
95823
|
*
|
|
95320
95824
|
* `config.yml` carries every other oh-my-pi setting, so only `tools.approval`
|
|
95321
95825
|
* (the keys rulesync manages), `bash.patterns` and an authored
|
|
95322
|
-
* `tools.approvalMode` are rewritten; sibling
|
|
95323
|
-
* kept, and the file is never deleted.
|
|
95826
|
+
* `tools.approvalMode` or `tools.restrictedFolders` are rewritten; sibling
|
|
95827
|
+
* keys such as `bash.enabled` are kept, and the file is never deleted.
|
|
95324
95828
|
*
|
|
95325
95829
|
* @see https://github.com/can1357/oh-my-pi/blob/main/docs/settings.md
|
|
95326
95830
|
* @see https://github.com/can1357/oh-my-pi/blob/main/docs/approval-mode.md
|
|
@@ -95369,6 +95873,7 @@ var OmpPermissions = class OmpPermissions extends ToolPermissions {
|
|
|
95369
95873
|
logger
|
|
95370
95874
|
});
|
|
95371
95875
|
const approvalMode = config.omp?.approvalMode;
|
|
95876
|
+
const restrictedFolders = config.omp?.restrictedFolders;
|
|
95372
95877
|
const existingTools = isRecord(existing.tools) ? existing.tools : {};
|
|
95373
95878
|
const nextApproval = {
|
|
95374
95879
|
...isRecord(existingTools.approval) ? Object.fromEntries(Object.entries(existingTools.approval).filter(([tool]) => !MANAGED_OMP_TOOLS.has(tool) && !Object.hasOwn(approval, tool)).map(([tool, value]) => [tool, raisedApproval(value, floor)])) : {},
|
|
@@ -95376,6 +95881,7 @@ var OmpPermissions = class OmpPermissions extends ToolPermissions {
|
|
|
95376
95881
|
};
|
|
95377
95882
|
const toolsSiblings = withoutKey(existingTools, "approval");
|
|
95378
95883
|
if (approvalMode !== void 0) toolsSiblings[APPROVAL_MODE_KEY] = approvalMode;
|
|
95884
|
+
if (restrictedFolders !== void 0) toolsSiblings[RESTRICTED_FOLDERS_KEY] = restrictedFolders;
|
|
95379
95885
|
const nextTools = Object.keys(nextApproval).length > 0 ? {
|
|
95380
95886
|
...toolsSiblings,
|
|
95381
95887
|
approval: nextApproval
|
|
@@ -95626,11 +96132,26 @@ function convertOmpToRulesync(config) {
|
|
|
95626
96132
|
}
|
|
95627
96133
|
if (Object.keys(bashRules).length > 0) permission.bash = bashRules;
|
|
95628
96134
|
const approvalMode = tools[APPROVAL_MODE_KEY];
|
|
96135
|
+
const restrictedFolders = importRestrictedFolders(tools[RESTRICTED_FOLDERS_KEY]);
|
|
96136
|
+
const omp = {
|
|
96137
|
+
...isOmpApprovalMode(approvalMode) && { approvalMode },
|
|
96138
|
+
...restrictedFolders !== void 0 && { restrictedFolders }
|
|
96139
|
+
};
|
|
95629
96140
|
return {
|
|
95630
96141
|
permission,
|
|
95631
|
-
...
|
|
96142
|
+
...Object.keys(omp).length > 0 && { omp }
|
|
95632
96143
|
};
|
|
95633
96144
|
}
|
|
96145
|
+
/**
|
|
96146
|
+
* `tools.restrictedFolders` as the override list: its string entries, or
|
|
96147
|
+
* `undefined` when it is unset or no entry survives (so a list of non-strings
|
|
96148
|
+
* is not imported as `[]` and then rewritten as an empty list).
|
|
96149
|
+
*/
|
|
96150
|
+
function importRestrictedFolders(value) {
|
|
96151
|
+
if (!Array.isArray(value)) return;
|
|
96152
|
+
const folders = value.filter((entry) => typeof entry === "string");
|
|
96153
|
+
return folders.length === 0 && value.length > 0 ? void 0 : folders;
|
|
96154
|
+
}
|
|
95634
96155
|
function isOmpApprovalMode(value) {
|
|
95635
96156
|
return OMP_APPROVAL_MODES.some((mode) => mode === value);
|
|
95636
96157
|
}
|
|
@@ -100727,19 +101248,31 @@ const CANONICAL_TO_VIBE_TOOL_NAMES = {
|
|
|
100727
101248
|
};
|
|
100728
101249
|
/**
|
|
100729
101250
|
* The tables earlier rulesync versions wrote for canonical categories that
|
|
100730
|
-
* Vibe 3.x
|
|
100731
|
-
* effect")
|
|
100732
|
-
* `agents
|
|
101251
|
+
* Vibe 3.x does not read: `[tools.grep]` (Vibe warns that it "has no
|
|
101252
|
+
* effect"), `[tools.task]` for the subagent tool v3.0.0 renamed to
|
|
101253
|
+
* `agents.*`, and `[tools.external_directory]` — Vibe has no such tool, and
|
|
101254
|
+
* its unused-settings check skips unknown tool names as custom/MCP tools, so
|
|
101255
|
+
* that deny was silently inert. A regenerate that configures the category removes them — and
|
|
100733
101256
|
* the matching `disabled_tools` entry — instead of leaving an inert table.
|
|
100734
101257
|
* Import still reads them (see {@link VIBE_TO_CANONICAL_TOOL_NAMES}), so a
|
|
100735
101258
|
* Vibe 2.x config is lifted into the canonical categories rather than lost.
|
|
100736
101259
|
*/
|
|
100737
101260
|
const VIBE_RETIRED_TOOL_TABLES = {
|
|
100738
101261
|
grep: "grep",
|
|
100739
|
-
agent: "task"
|
|
101262
|
+
agent: "task",
|
|
101263
|
+
external_directory: "external_directory"
|
|
100740
101264
|
};
|
|
100741
101265
|
/** The canonical category Vibe's `enable_subagents` switch expresses. */
|
|
100742
101266
|
const VIBE_SUBAGENT_CATEGORY = "agent";
|
|
101267
|
+
/** The canonical category Vibe's top-level `outside_workdir` setting expresses. */
|
|
101268
|
+
const VIBE_OUTSIDE_WORKDIR_CATEGORY = "external_directory";
|
|
101269
|
+
/**
|
|
101270
|
+
* The values of Vibe's `outside_workdir` (v3.0.3+). There is no allow value:
|
|
101271
|
+
* access outside the working directory is granted per path through a tool's
|
|
101272
|
+
* `allowlist` (`vibe-path:...`) or `--add-dir`.
|
|
101273
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
101274
|
+
*/
|
|
101275
|
+
const VIBE_OUTSIDE_WORKDIR_VALUES = /* @__PURE__ */ new Set(["ask", "deny"]);
|
|
100743
101276
|
const VIBE_TO_CANONICAL_TOOL_NAMES = {
|
|
100744
101277
|
bash: "bash",
|
|
100745
101278
|
read_file: "read",
|
|
@@ -100815,7 +101348,8 @@ const CANONICAL_PERMISSION_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
100815
101348
|
"grep",
|
|
100816
101349
|
"glob",
|
|
100817
101350
|
"notebookedit",
|
|
100818
|
-
"agent"
|
|
101351
|
+
"agent",
|
|
101352
|
+
"external_directory"
|
|
100819
101353
|
]);
|
|
100820
101354
|
var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
100821
101355
|
toml;
|
|
@@ -100896,7 +101430,11 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
100896
101430
|
rules: permission[VIBE_SUBAGENT_CATEGORY],
|
|
100897
101431
|
logger
|
|
100898
101432
|
});
|
|
100899
|
-
const
|
|
101433
|
+
const outsideWorkdirPatch = resolveOutsideWorkdirPatch({
|
|
101434
|
+
rules: permission[VIBE_OUTSIDE_WORKDIR_CATEGORY],
|
|
101435
|
+
logger
|
|
101436
|
+
});
|
|
101437
|
+
const tableCategories = Object.entries(permission).filter(([category]) => category !== VIBE_SUBAGENT_CATEGORY && category !== VIBE_OUTSIDE_WORKDIR_CATEGORY);
|
|
100900
101438
|
for (const [category, rules] of tableCategories) {
|
|
100901
101439
|
const vibeToolNames = resolveNames(category);
|
|
100902
101440
|
if (vibeToolNames === void 0) {
|
|
@@ -100970,7 +101508,8 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
100970
101508
|
enabledTools
|
|
100971
101509
|
}),
|
|
100972
101510
|
disabled_tools: disabledTools.size > 0 ? [...disabledTools].toSorted() : void 0,
|
|
100973
|
-
...enableSubagentsPatch
|
|
101511
|
+
...enableSubagentsPatch,
|
|
101512
|
+
...outsideWorkdirPatch
|
|
100974
101513
|
},
|
|
100975
101514
|
filePath
|
|
100976
101515
|
}),
|
|
@@ -101014,6 +101553,8 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
101014
101553
|
}
|
|
101015
101554
|
for (const category of silencedCategories) ensurePermission(permission, category)["*"] = "deny";
|
|
101016
101555
|
if (this.toml.enable_subagents === false) ensurePermission(permission, VIBE_SUBAGENT_CATEGORY)["*"] = "deny";
|
|
101556
|
+
const outsideWorkdir = this.toml.outside_workdir;
|
|
101557
|
+
if (typeof outsideWorkdir === "string" && VIBE_OUTSIDE_WORKDIR_VALUES.has(outsideWorkdir)) ensurePermission(permission, VIBE_OUTSIDE_WORKDIR_CATEGORY)["*"] = outsideWorkdir === "deny" ? "deny" : "ask";
|
|
101017
101558
|
warnOnUnresolvableDisabledTools({
|
|
101018
101559
|
entries: disabledToolEntries,
|
|
101019
101560
|
tools: this.toml.tools
|
|
@@ -101090,7 +101631,7 @@ function retireLegacyToolTables({ permission, tools, disabledTools, logger }) {
|
|
|
101090
101631
|
}
|
|
101091
101632
|
if (disabledTools.delete(legacyToolName)) removed.push(`disabled_tools entry '${legacyToolName}'`);
|
|
101092
101633
|
}
|
|
101093
|
-
if (removed.length > 0) logger?.warn(`Removed ${removed.join(", ")} from the Vibe config: Vibe 3.x
|
|
101634
|
+
if (removed.length > 0) logger?.warn(`Removed ${removed.join(", ")} from the Vibe config: Vibe 3.x does not read [tools.grep], the former 'task' subagent tool or an 'external_directory' tool (the 'external_directory' category is written to outside_workdir instead), so those entries had no effect.`);
|
|
101094
101635
|
}
|
|
101095
101636
|
/**
|
|
101096
101637
|
* Report the rules of a category that has no Vibe tool table. `grep` gets its
|
|
@@ -101126,6 +101667,30 @@ function resolveEnableSubagentsPatch({ rules, logger }) {
|
|
|
101126
101667
|
return { enable_subagents: action === "deny" ? false : void 0 };
|
|
101127
101668
|
}
|
|
101128
101669
|
/**
|
|
101670
|
+
* Translate the canonical `external_directory` category into Vibe's top-level
|
|
101671
|
+
* `outside_workdir` setting (v3.0.3+), which decides whether a file or shell
|
|
101672
|
+
* tool call reaching outside the working directory prompts (`"ask"`, the
|
|
101673
|
+
* default) or is refused with a tool error (`"deny"`). A `*` deny or ask writes
|
|
101674
|
+
* that value; a `*` allow has no Vibe value, so it is warned about and the key
|
|
101675
|
+
* is removed, leaving Vibe's default prompt. Without a `*` rule the key is left
|
|
101676
|
+
* as the file has it. Pattern rules are skipped with a warning: path grants go
|
|
101677
|
+
* in a tool's `allowlist` (`vibe-path:...`) instead.
|
|
101678
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
101679
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/vibe/app_server/_unified_permissions.py
|
|
101680
|
+
*/
|
|
101681
|
+
function resolveOutsideWorkdirPatch({ rules, logger }) {
|
|
101682
|
+
if (rules === void 0) return {};
|
|
101683
|
+
const patternCount = Object.keys(rules).filter((pattern) => pattern !== "*").length;
|
|
101684
|
+
if (patternCount > 0) logger?.warn(`Vibe's outside_workdir applies to every path outside the working directory; skipping ${patternCount} pattern rule(s) of the '${VIBE_OUTSIDE_WORKDIR_CATEGORY}' category. Grant a path through a tool's allowlist ("vibe-path:...") in the Vibe config instead.`);
|
|
101685
|
+
const action = rules["*"];
|
|
101686
|
+
if (action === void 0) return {};
|
|
101687
|
+
if (action === "allow") {
|
|
101688
|
+
logger?.warn(`Vibe's outside_workdir only takes "ask" or "deny"; the '${VIBE_OUTSIDE_WORKDIR_CATEGORY}' category's "*": "allow" leaves Vibe's default, which prompts before reaching outside the working directory.`);
|
|
101689
|
+
return { outside_workdir: void 0 };
|
|
101690
|
+
}
|
|
101691
|
+
return { outside_workdir: action };
|
|
101692
|
+
}
|
|
101693
|
+
/**
|
|
101129
101694
|
* Apply the Vibe-scoped override's per-tool `sensitive_patterns` (patterns that
|
|
101130
101695
|
* escalate to ASK even when the base permission is ALWAYS). rulesync owns this
|
|
101131
101696
|
* list for any category the override names: a present list is set, an empty
|
|
@@ -106475,4 +107040,4 @@ async function importChecksCore(params) {
|
|
|
106475
107040
|
}
|
|
106476
107041
|
export { RulesyncPermissions as $, formatError as $n, isSymlink as $t, CODEBUDDY_DIR as A, MAX_FILE_SIZE as An, withFallbackLoggerTarget as At, CLAUDECODE_LOCAL_RULE_FILE_NAME as B, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Bn, assertTreeContainsNoSymlinks as Bt, QWENCODE_LOCAL_RULE_FILE_NAME as C, PACKAGING_TOOL_TARGETS as Cn, getSourceTransport as Ct, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as D, DEPRECATED_FEATURE_REPLACEMENTS as Dn, WarningCollectingLogger as Dt, FACTORYDROID_DIR as E, ALL_FEATURES_WITH_WILDCARD as En, JsonLogger as Et, groupSpellingsByCaseFoldedIdentity as F, RULESYNC_CONFIG_SCHEMA_URL as Fn, PathOutsideRootError as Ft, toInstructionBudgetFile as G, RULESYNC_PERMISSIONS_SCHEMA_URL as Gn, ensureDir as Gt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as H, RULESYNC_MCP_SCHEMA_URL as Hn, checkPathTraversal as Ht, CODEXCLI_BASH_RULES_FILE_NAME as I, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as In, applyFileMode as It, RulesyncSubagentFrontmatterSchema as J, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Jn, findFilesByGlobs as Jt, getLocalSkillDirNames as K, RULESYNC_RELATIVE_DIR_PATH as Kn, fileExists as Kt, CODEXCLI_DIR as L, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Ln, assertDirectoryIfExists as Lt, AUGMENTCODE_DIR as M, RULESYNC_CHECKS_RELATIVE_DIR_PATH as Mn, withWarnOnceScope as Mt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as N, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Nn, CLIError as Nt, COMMANDCODE_DIR as O, CURATED_RULES_FEATURE_SUBDIR as On, fallbackLogger as Ot, caseFoldIdentity as P, RULESYNC_CONFIG_RELATIVE_FILE_PATH as Pn, ErrorCodes as Pt, RulesyncRuleFrontmatterSchema as Q, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as Qn, isFileSystemError as Qt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as R, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Rn, assertNoSymlinkBelowRoot as Rt, QWENCODE_DIR as S, ALL_TOOL_TARGETS_WITH_WILDCARD as Sn, assertTargetsFeaturesExclusive as St, LETTACODE_SETTINGS_LOCAL_FILE_NAME as T, ALL_FEATURES as Tn, ConsoleLogger as Tt, CLAUDECODE_SKILLS_DIR_PATH as U, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Un, createTempDirectory as Ut, CLAUDECODE_MEMORIES_DIR_NAME as V, RULESYNC_MCP_RELATIVE_FILE_PATH as Vn, assertWritablePathInsideRoot as Vt, findProjectInstructionBudgetFindings as W, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Wn, directoryExists as Wt, RulesyncSkillFrontmatterSchema as X, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Xn, getHomeDirectory as Xt, RulesyncSkill as Y, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Yn, getFileSizeAndExecutableBit as Yt, RulesyncRule as Z, RULESYNC_USER_CONFIG_DIR_NAME as Zn, isFileNotFoundError as Zt, SubagentsProcessor as _, toPosixPath as _n, resolveEffectiveInputRoots as _t, convertFromTool as a, readFileContent as an, stripControlCharactersKeepingLineFeeds as ar, parseJsonc as at, CommandsProcessor as b, mapWithConcurrency as bn, GITIGNORE_DESTINATION_KEY as bt, parseCommaSeparatedList as c, removeDirectoryStrict as cn, RulesyncCheck as ct, shortenToWidth as d, removeFileWithoutFollowingSymlinks as dn, loadYaml as dt, listDirectoryEntryNames as en, truncateText as er, RulesyncMcp as et, McpProcessor as f, removeTempDirectory as fn, isRecord as ft, RulesProcessor as g, splitPathSegments as gn, mergeInputRootConfigs as gt, ChecksProcessor as h, runWithDirectoryRollback as hn, ConfigResolver as ht, inspectInputRoots as i, posixRelativePathEscapesRoot as in, stripControlCharacters as ir, resolveRulesyncSourceWritePath as it, CODEBUDDY_LOCAL_RULE_FILE_NAME as j, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as jn, resetRunWarningState as jt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as k, FEATURE_SOURCE_TREE_ENTRIES as kn, warnOnConflictingFlags as kt, ELLIPSIS_WIDTH as l, removeFile as ln, RulesyncCheckFrontmatterSchema as lt, HooksProcessor as m, resolvedRelativePath as mn, SKILL_FILE_NAME as mt, formatSourceLoadFailure as n, listSubdirectoryNames as nn, hasEnclosingMarkOutsideKeycap as nr, RulesyncHooks as nt, isPackagingToolTarget as o, readFileContentOrNull as on, stripControlCharactersKeepingLineFeedsAndTabs as or, RulesyncCommand as ot, IgnoreProcessor as p, resolvePath as pn, SHARED_USER_MANAGED_CONFIG_PATHS as pt, RulesyncSubagent as q, RULESYNC_RULES_RELATIVE_DIR_PATH as qn, fileExistsStrict as qt, generate as r, pathEscapesRoot as rn, quoteForLog as rr, getRulesyncSourceCandidates as rt, getProcessorRegistryEntry as s, removeDirectory as sn, stripHiddenCharacters as sr, RulesyncCommandFrontmatterSchema as st, importFromTool as t, listFilePathsRecursively as tn, hasDeceptiveHiddenCharacters as tr, RulesyncIgnore as tt, displayWidthOf as u, removeFileStrict as un, stringifyFrontmatter as ut, SkillsProcessor as v, writeFileBuffer as vn, CONFLICTING_TARGET_PAIRS as vt, LETTACODE_DIR as w, ToolTargetSchema as wn, findControlCharacter as wt, REASONIX_LOCAL_RULE_FILE_NAME as x, ALL_TOOL_TARGETS as xn, SourceEntrySchema as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, writeFileContent as yn, ConfigFileSchema as yt, CLAUDECODE_DIR as z, RULESYNC_IGNORE_RELATIVE_FILE_PATH as zn, assertPathResolvesInsideRoot as zt };
|
|
106477
107042
|
|
|
106478
|
-
//# sourceMappingURL=import-
|
|
107043
|
+
//# sourceMappingURL=import-BQNLyW5s.js.map
|