rulesync 43.1.1 → 43.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/index.cjs +4 -4
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-BiQjbRR6.cjs → import-DAT3ge4z.cjs} +680 -113
- package/dist/{import-COu_2AUF.js → import-DPXjeK8q.js} +681 -114
- package/dist/{import-COu_2AUF.js.map → import-DPXjeK8q.js.map} +1 -1
- package/dist/index.cjs +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()),
|
|
@@ -13708,7 +13789,11 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
13708
13789
|
},
|
|
13709
13790
|
permissions: {
|
|
13710
13791
|
kind: "replace-owned-keys",
|
|
13711
|
-
ownedKeys: [
|
|
13792
|
+
ownedKeys: [
|
|
13793
|
+
"allowedUrls",
|
|
13794
|
+
"deniedUrls",
|
|
13795
|
+
"defaultPermissionMode"
|
|
13796
|
+
]
|
|
13712
13797
|
}
|
|
13713
13798
|
}
|
|
13714
13799
|
},
|
|
@@ -13887,7 +13972,12 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
13887
13972
|
},
|
|
13888
13973
|
permissions: {
|
|
13889
13974
|
kind: "replace-owned-keys",
|
|
13890
|
-
ownedKeys: [
|
|
13975
|
+
ownedKeys: [
|
|
13976
|
+
"permissions",
|
|
13977
|
+
"autoReview",
|
|
13978
|
+
"autoMode",
|
|
13979
|
+
"memory"
|
|
13980
|
+
]
|
|
13891
13981
|
}
|
|
13892
13982
|
}
|
|
13893
13983
|
},
|
|
@@ -14197,7 +14287,8 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
14197
14287
|
"tools",
|
|
14198
14288
|
"enabled_tools",
|
|
14199
14289
|
"disabled_tools",
|
|
14200
|
-
"enable_subagents"
|
|
14290
|
+
"enable_subagents",
|
|
14291
|
+
"outside_workdir"
|
|
14201
14292
|
]
|
|
14202
14293
|
}
|
|
14203
14294
|
}
|
|
@@ -15867,17 +15958,24 @@ function toolSkillSearchRoots(paths) {
|
|
|
15867
15958
|
return [paths.relativeDirPath, ...paths.alternativeSkillRoots ?? []];
|
|
15868
15959
|
}
|
|
15869
15960
|
/**
|
|
15870
|
-
*
|
|
15871
|
-
*
|
|
15872
|
-
*
|
|
15873
|
-
*
|
|
15874
|
-
*
|
|
15875
|
-
*
|
|
15876
|
-
*
|
|
15877
|
-
*
|
|
15878
|
-
*
|
|
15961
|
+
* Skill roots that every tool scanning them imports leniently (skip-and-warn
|
|
15962
|
+
* per skill) instead of failing fast, regardless of the tool's own
|
|
15963
|
+
* `lenientImport` flag.
|
|
15964
|
+
*
|
|
15965
|
+
* - The Agent Skills interoperability roots: project `.agents/skills/`, global
|
|
15966
|
+
* `~/.agents/skills/` (same relative path, home output root), and Amp's
|
|
15967
|
+
* global equivalent `~/.config/agents/skills/`. Foreign-authored —
|
|
15968
|
+
* potentially non-conformant — skills are the most likely to live here, so
|
|
15969
|
+
* they are imported leniently per the Agent Skills client-implementation
|
|
15970
|
+
* guide, while each tool keeps fail-fast behavior for its own native root.
|
|
15971
|
+
* https://agentskills.io/client-implementation/adding-skills-support
|
|
15972
|
+
* - Amp's read-only global discovery root `~/.config/amp/skills/`, which is not
|
|
15973
|
+
* an interop root but follows the same skip-and-warn rule.
|
|
15974
|
+
*
|
|
15975
|
+
* Other import-only roots (e.g. GitLab Duo's legacy skills root, Kimi Code's
|
|
15976
|
+
* shared root) are not listed here and stay fail-fast.
|
|
15879
15977
|
*/
|
|
15880
|
-
const
|
|
15978
|
+
const LENIENT_IMPORT_SKILL_ROOTS = /* @__PURE__ */ new Set([
|
|
15881
15979
|
toPosixPath(AGENTSMD_SKILLS_DIR_PATH),
|
|
15882
15980
|
toPosixPath(AMP_SKILLS_GLOBAL_DIR),
|
|
15883
15981
|
toPosixPath(AMP_SKILLS_NATIVE_GLOBAL_DIR)
|
|
@@ -15902,8 +16000,8 @@ function warnOnEmptyLoadedDescription({ skillFilePath, description }) {
|
|
|
15902
16000
|
if (typeof description !== "string" || description.length > 0) return;
|
|
15903
16001
|
warnOnceWithFallback(void 0, `${stripControlCharacters(toPosixPath(skillFilePath))}: ${EMPTY_SKILL_DESCRIPTION_VIOLATION}. Rulesync imports it anyway so the content is not lost; fill it in before relying on this skill.`);
|
|
15904
16002
|
}
|
|
15905
|
-
function
|
|
15906
|
-
return
|
|
16003
|
+
function isLenientImportSkillRoot(relativeDirPath) {
|
|
16004
|
+
return LENIENT_IMPORT_SKILL_ROOTS.has(toPosixPath(relativeDirPath));
|
|
15907
16005
|
}
|
|
15908
16006
|
/** Ordered import roots: managed roots first, followed by read-only discovery roots. */
|
|
15909
16007
|
function toolSkillImportRoots(paths) {
|
|
@@ -30543,7 +30641,10 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
|
|
|
30543
30641
|
}
|
|
30544
30642
|
}
|
|
30545
30643
|
static getSettablePaths({ global: _global = false } = {}) {
|
|
30546
|
-
return {
|
|
30644
|
+
return {
|
|
30645
|
+
relativeDirPath: QWENCODE_SKILLS_DIR_PATH,
|
|
30646
|
+
importOnlySkillRoots: [AGENTSMD_SKILLS_DIR_PATH]
|
|
30647
|
+
};
|
|
30547
30648
|
}
|
|
30548
30649
|
getFrontmatter() {
|
|
30549
30650
|
return QwencodeSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
@@ -33133,7 +33234,7 @@ var SkillsProcessor = class extends DirFeatureProcessor {
|
|
|
33133
33234
|
for (const root of roots) {
|
|
33134
33235
|
const rootOutputRoot = typeof root === "string" ? this.outputRoot : root.outputRoot;
|
|
33135
33236
|
const relativeDirPath = typeof root === "string" ? root : root.relativeDirPath;
|
|
33136
|
-
const isLenientRoot = configuredRootPaths.has(relativeDirPath) || factory.meta.lenientImport === true ||
|
|
33237
|
+
const isLenientRoot = configuredRootPaths.has(relativeDirPath) || factory.meta.lenientImport === true || isLenientImportSkillRoot(relativeDirPath);
|
|
33137
33238
|
const skillsDirPath = join(rootOutputRoot, relativeDirPath);
|
|
33138
33239
|
if (!await directoryExists(skillsDirPath)) continue;
|
|
33139
33240
|
const ownedDirNames = [];
|
|
@@ -39564,6 +39665,16 @@ var VibePluginSubagent = class VibePluginSubagent extends ToolSubagent {
|
|
|
39564
39665
|
*/
|
|
39565
39666
|
const VIBE_PROMPTS_DIR_PATH = join(".vibe", "prompts");
|
|
39566
39667
|
/**
|
|
39668
|
+
* The agent name Vibe reserves for `post_agent` hook matching (v3.0.3+).
|
|
39669
|
+
* `AgentProfile.from_toml` refuses a file whose stem matches it case-insensitively, so an
|
|
39670
|
+
* agent written as `main.toml` would never load.
|
|
39671
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/vibe/core/agents/models.py
|
|
39672
|
+
*/
|
|
39673
|
+
const VIBE_RESERVED_AGENT_NAME = "main";
|
|
39674
|
+
function isReservedVibeAgentFileName(relativeFilePath) {
|
|
39675
|
+
return basename(relativeFilePath).replace(/\.(md|toml)$/, "").toLowerCase() === VIBE_RESERVED_AGENT_NAME;
|
|
39676
|
+
}
|
|
39677
|
+
/**
|
|
39567
39678
|
* Vibe resolves a `system_prompt_id` by file name, and
|
|
39568
39679
|
* `VibeConfigSchema._check_system_prompt` evaluates it during validation — an
|
|
39569
39680
|
* unresolvable id raises and the agent is dropped at discovery. The id must
|
|
@@ -39678,9 +39789,17 @@ var VibeSubagent = class VibeSubagent extends ToolSubagent {
|
|
|
39678
39789
|
* evaluates the property during validation, so an unresolvable
|
|
39679
39790
|
* `system_prompt_id` raises and `AgentRegistry._try_load` drops the agent
|
|
39680
39791
|
* with a warning.
|
|
39792
|
+
*
|
|
39793
|
+
* A subagent named `main` is skipped with a warning: Vibe reserves that name
|
|
39794
|
+
* for hook matching and does not load an agent file called `main.toml`.
|
|
39681
39795
|
*/
|
|
39682
|
-
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false }) {
|
|
39796
|
+
static fromRulesyncSubagents({ rulesyncSubagents, outputRoot = process.cwd(), global = false, logger }) {
|
|
39683
39797
|
return rulesyncSubagents.flatMap((rulesyncSubagent) => {
|
|
39798
|
+
const sourcePath = rulesyncSubagent.getRelativeFilePath();
|
|
39799
|
+
if (isReservedVibeAgentFileName(sourcePath)) {
|
|
39800
|
+
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.`);
|
|
39801
|
+
return [];
|
|
39802
|
+
}
|
|
39684
39803
|
const agentFile = VibeSubagent.fromRulesyncSubagent({
|
|
39685
39804
|
outputRoot,
|
|
39686
39805
|
relativeDirPath: VibeSubagent.getSettablePaths({ global }).relativeDirPath,
|
|
@@ -44967,17 +45086,49 @@ var ClaudecodeRule = class ClaudecodeRule extends ToolRule {
|
|
|
44967
45086
|
* - `alwaysApply: true`: force the rule to always load (equivalent to a file
|
|
44968
45087
|
* with no frontmatter at all).
|
|
44969
45088
|
* - `description`: label surfaced in the rule toggle UI.
|
|
45089
|
+
* - `disabled: true` (or `enabled: false`): the rule is switched off. Since
|
|
45090
|
+
* Cline v4.1.26 (cline/cline#14605) the rule toggle writes this flag into the
|
|
45091
|
+
* rule file itself instead of keeping it only in extension state.
|
|
44970
45092
|
*
|
|
44971
45093
|
* Rules without frontmatter are always active. Uses `z.looseObject()` so
|
|
44972
|
-
* forward-compatible fields added upstream are preserved on round-trip
|
|
45094
|
+
* forward-compatible fields added upstream are preserved on round-trip: every
|
|
45095
|
+
* key other than `paths` / `alwaysApply` / `description` is carried in the
|
|
45096
|
+
* rulesync `cline:` section.
|
|
44973
45097
|
*
|
|
44974
45098
|
* @see https://docs.cline.bot/customization/cline-rules
|
|
45099
|
+
* @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/extensions/config/user-instruction-config-loader.ts
|
|
44975
45100
|
*/
|
|
44976
45101
|
const ClineRuleFrontmatterSchema = z.looseObject({
|
|
44977
45102
|
paths: z.optional(z.union([z.array(z.string()), z.string()])),
|
|
44978
45103
|
alwaysApply: z.optional(z.boolean()),
|
|
44979
45104
|
description: z.optional(z.string())
|
|
44980
45105
|
});
|
|
45106
|
+
const CLINE_RULE_FIELD_TYPES = {
|
|
45107
|
+
disabled: "boolean",
|
|
45108
|
+
enabled: "boolean"
|
|
45109
|
+
};
|
|
45110
|
+
const CANONICAL_CLINE_RULE_KEYS = [
|
|
45111
|
+
"paths",
|
|
45112
|
+
"alwaysApply",
|
|
45113
|
+
"description"
|
|
45114
|
+
];
|
|
45115
|
+
/** Returns a copy of `record` without the canonical `paths` / `alwaysApply` / `description` keys. */
|
|
45116
|
+
function omitCanonicalClineRuleKeys(record) {
|
|
45117
|
+
return Object.fromEntries(Object.entries(record ?? {}).filter(([key]) => !CANONICAL_CLINE_RULE_KEYS.includes(key)));
|
|
45118
|
+
}
|
|
45119
|
+
/**
|
|
45120
|
+
* Splits a Cline rule frontmatter into the keys that have canonical rulesync
|
|
45121
|
+
* homes (`paths` / `alwaysApply` / `description`) and the rest, which are
|
|
45122
|
+
* lifted into the rulesync `cline:` section.
|
|
45123
|
+
*/
|
|
45124
|
+
function extractClineSection({ frontmatter, relativeFilePath }) {
|
|
45125
|
+
return Object.fromEntries(Object.entries(omitCanonicalClineRuleKeys(frontmatter)).filter(([key, value]) => {
|
|
45126
|
+
const expected = Object.hasOwn(CLINE_RULE_FIELD_TYPES, key) ? CLINE_RULE_FIELD_TYPES[key] : void 0;
|
|
45127
|
+
if (expected === void 0 || value === void 0 || typeof value === expected) return true;
|
|
45128
|
+
warnWithFallback(void 0, `Cline rules: dropping ${key} from ${relativeFilePath}: Cline rejects the whole rule unless it is a ${expected}.`);
|
|
45129
|
+
return false;
|
|
45130
|
+
}));
|
|
45131
|
+
}
|
|
44981
45132
|
function isNonRootParams$1(params) {
|
|
44982
45133
|
return "frontmatter" in params && "body" in params;
|
|
44983
45134
|
}
|
|
@@ -45052,6 +45203,10 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45052
45203
|
toRulesyncRule() {
|
|
45053
45204
|
if (this.isRoot()) return this.toRulesyncRuleDefault();
|
|
45054
45205
|
const globs = this.frontmatter?.alwaysApply === true ? ["**/*"] : normalizePaths$1(this.frontmatter?.paths);
|
|
45206
|
+
const cline = extractClineSection({
|
|
45207
|
+
frontmatter: this.frontmatter,
|
|
45208
|
+
relativeFilePath: this.getRelativeFilePath()
|
|
45209
|
+
});
|
|
45055
45210
|
return new RulesyncRule({
|
|
45056
45211
|
outputRoot: process.cwd(),
|
|
45057
45212
|
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
@@ -45060,7 +45215,8 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45060
45215
|
root: false,
|
|
45061
45216
|
targets: ["*"],
|
|
45062
45217
|
description: this.frontmatter?.description,
|
|
45063
|
-
globs
|
|
45218
|
+
globs,
|
|
45219
|
+
...Object.keys(cline).length > 0 && { cline }
|
|
45064
45220
|
},
|
|
45065
45221
|
body: this.body ?? this.getFileContent()
|
|
45066
45222
|
});
|
|
@@ -45105,16 +45261,22 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
45105
45261
|
* universal match-everything globs mean "always", so they map to
|
|
45106
45262
|
* `alwaysApply: true` instead of a `paths` list; specific globs become
|
|
45107
45263
|
* conditional `paths`; an empty globs list leaves the rule always-active with
|
|
45108
|
-
* no `paths`/`alwaysApply`.
|
|
45264
|
+
* no `paths`/`alwaysApply`. Every key of the rulesync `cline:` section
|
|
45265
|
+
* (e.g. `disabled: true`) is written into the frontmatter too; the canonical
|
|
45266
|
+
* `paths` / `alwaysApply` / `description` win over same-named keys there.
|
|
45109
45267
|
*/
|
|
45110
45268
|
static buildNonRootRule({ outputRoot, rulesyncRule, validate, relativeDirPath }) {
|
|
45111
45269
|
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
45112
45270
|
const globs = normalizePaths$1(rulesyncFrontmatter.globs);
|
|
45113
45271
|
const isUniversal = globs.length > 0 && globs.every((glob) => glob === "**/*" || glob === "*");
|
|
45272
|
+
const rawClineSection = rulesyncFrontmatter.cline ?? {};
|
|
45273
|
+
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.`);
|
|
45274
|
+
const clineSection = omitCanonicalClineRuleKeys(rawClineSection);
|
|
45114
45275
|
const frontmatter = {
|
|
45115
45276
|
paths: globs.length > 0 && !isUniversal ? globs : void 0,
|
|
45116
45277
|
alwaysApply: isUniversal ? true : void 0,
|
|
45117
|
-
description: rulesyncFrontmatter.description || void 0
|
|
45278
|
+
description: rulesyncFrontmatter.description || void 0,
|
|
45279
|
+
...clineSection
|
|
45118
45280
|
};
|
|
45119
45281
|
return new ClineRule({
|
|
45120
45282
|
outputRoot,
|
|
@@ -55801,6 +55963,14 @@ function applyCommandPrefix({ def, converterConfig }) {
|
|
|
55801
55963
|
if (isExecForm) return `${bracePlaceholder(converterConfig.projectDirVar)}/${stripSurroundingQuotes(relativeCommand)}`;
|
|
55802
55964
|
return anchorInline(`"${converterConfig.projectDirVar}"/${relativeCommand}`);
|
|
55803
55965
|
}
|
|
55966
|
+
/** Whether a field restricted to some events applies on this one. */
|
|
55967
|
+
function isEventApplicable({ events, eventName }) {
|
|
55968
|
+
return events === void 0 || events.has(eventName);
|
|
55969
|
+
}
|
|
55970
|
+
/** Quote and comma-join a set of names (events, hook types) for a warning. */
|
|
55971
|
+
function describeEvents(names) {
|
|
55972
|
+
return [...names].map((name) => `"${name}"`).join(", ");
|
|
55973
|
+
}
|
|
55804
55974
|
/**
|
|
55805
55975
|
* Whether a field registered for `command` hooks only applies to this hook.
|
|
55806
55976
|
* Applied on both export and import: a value imported into a canonical field
|
|
@@ -55822,7 +55992,7 @@ function isFieldApplicable({ commandOnly, hookType }) {
|
|
|
55822
55992
|
* silence — the mirror of the import side.
|
|
55823
55993
|
*/
|
|
55824
55994
|
function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn }) {
|
|
55825
|
-
for (const { canonical, tool, commandOnly, maxLength, collapseWhitespaceForLength } of fields) {
|
|
55995
|
+
for (const { canonical, tool, commandOnly, maxLength, collapseWhitespaceForLength, onlyWithFlag, events } of fields) {
|
|
55826
55996
|
const value = def[canonical];
|
|
55827
55997
|
if (value === void 0) continue;
|
|
55828
55998
|
if (!isFieldApplicable({
|
|
@@ -55832,6 +56002,13 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55832
56002
|
warn?.(`Dropping "${canonical}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on "command" hooks only, so it is not generated.`);
|
|
55833
56003
|
continue;
|
|
55834
56004
|
}
|
|
56005
|
+
if (events !== void 0 && !isEventApplicable({
|
|
56006
|
+
events,
|
|
56007
|
+
eventName
|
|
56008
|
+
})) {
|
|
56009
|
+
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.`);
|
|
56010
|
+
continue;
|
|
56011
|
+
}
|
|
55835
56012
|
if (!isValid({
|
|
55836
56013
|
value,
|
|
55837
56014
|
canonical
|
|
@@ -55839,14 +56016,18 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55839
56016
|
warn?.(`Dropping "${canonical}" from a "${hookType}" hook on "${eventName}": ${quoteValueForWarning(value)} is not a value this tool can express as "${tool}".`);
|
|
55840
56017
|
continue;
|
|
55841
56018
|
}
|
|
56019
|
+
if (onlyWithFlag !== void 0 && def[onlyWithFlag] !== true) warn?.(`"${canonical}" on a "${hookType}" hook on "${eventName}" has no effect without "${onlyWithFlag}": true; this tool reads "${tool}" only on "${onlyWithFlag}" hooks.`);
|
|
55842
56020
|
if (maxLength !== void 0 && typeof value === "string") {
|
|
55843
56021
|
const length = collapseWhitespaceForLength ? value.replace(/\s+/g, " ").trim().length : value.length;
|
|
55844
|
-
if (length > maxLength) warn?.(`"${canonical}" on a "${hookType}" hook on "${eventName}" is ${length} characters long; this tool caps "${tool}" at ${maxLength} characters and will shorten it.`);
|
|
56022
|
+
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.`);
|
|
55845
56023
|
}
|
|
55846
56024
|
}
|
|
55847
|
-
return Object.fromEntries(fields.filter(({ canonical, commandOnly }) => isFieldApplicable({
|
|
56025
|
+
return Object.fromEntries(fields.filter(({ canonical, commandOnly, events }) => isFieldApplicable({
|
|
55848
56026
|
commandOnly,
|
|
55849
56027
|
hookType
|
|
56028
|
+
}) && isEventApplicable({
|
|
56029
|
+
events,
|
|
56030
|
+
eventName
|
|
55850
56031
|
}) && isValid({
|
|
55851
56032
|
value: def[canonical],
|
|
55852
56033
|
canonical
|
|
@@ -55859,16 +56040,24 @@ function emitPassthroughFields({ def, hookType, eventName, fields, isValid, warn
|
|
|
55859
56040
|
* kind has a rule an authored file can plausibly violate — turns a rejected
|
|
55860
56041
|
* value into a warning instead of a silent drop.
|
|
55861
56042
|
*/
|
|
55862
|
-
function importPassthroughFields({ h, hookType, fields, isValid, describeInvalid, warn }) {
|
|
55863
|
-
const
|
|
56043
|
+
function importPassthroughFields({ h, hookType, eventName, fields, isValid, describeInvalid, warn }) {
|
|
56044
|
+
const typeApplicable = fields.filter(({ commandOnly }) => isFieldApplicable({
|
|
55864
56045
|
commandOnly,
|
|
55865
56046
|
hookType
|
|
55866
56047
|
}));
|
|
56048
|
+
const applicable = typeApplicable.filter(({ events }) => isEventApplicable({
|
|
56049
|
+
events,
|
|
56050
|
+
eventName
|
|
56051
|
+
}));
|
|
55867
56052
|
const skipped = fields.filter(({ commandOnly }) => !isFieldApplicable({
|
|
55868
56053
|
commandOnly,
|
|
55869
56054
|
hookType
|
|
55870
56055
|
}));
|
|
55871
56056
|
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.`);
|
|
56057
|
+
for (const { tool, events } of typeApplicable) if (events !== void 0 && !isEventApplicable({
|
|
56058
|
+
events,
|
|
56059
|
+
eventName
|
|
56060
|
+
}) && 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.`);
|
|
55872
56061
|
for (const { tool, canonical } of applicable) if (describeInvalid !== void 0 && h[tool] !== void 0 && !isValid({
|
|
55873
56062
|
value: h[tool],
|
|
55874
56063
|
canonical
|
|
@@ -56036,11 +56225,11 @@ function isKeywordFieldApplicable({ spec, hookType, eventName, flags, direction,
|
|
|
56036
56225
|
return false;
|
|
56037
56226
|
}
|
|
56038
56227
|
if (hookTypes !== void 0 && !hookTypes.has(hookType)) {
|
|
56039
|
-
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on ${
|
|
56228
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool documents "${tool}" on ${describeEvents(hookTypes)} hooks only, so it is not ${direction}.`);
|
|
56040
56229
|
return false;
|
|
56041
56230
|
}
|
|
56042
56231
|
if (events !== void 0 && !events.has(eventName)) {
|
|
56043
|
-
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${
|
|
56232
|
+
warn?.(`Dropping "${field}" from a "${hookType}" hook on "${eventName}": this tool honours "${tool}" on ${describeEvents(events)} only, so it is not ${direction}.`);
|
|
56044
56233
|
return false;
|
|
56045
56234
|
}
|
|
56046
56235
|
if (ignoredEvents?.has(eventName)) {
|
|
@@ -56486,6 +56675,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56486
56675
|
...importPassthroughFields({
|
|
56487
56676
|
h,
|
|
56488
56677
|
hookType,
|
|
56678
|
+
eventName,
|
|
56489
56679
|
fields: converterConfig.booleanPassthroughFields ?? [],
|
|
56490
56680
|
isValid: isBooleanValue,
|
|
56491
56681
|
warn
|
|
@@ -56500,6 +56690,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56500
56690
|
...importPassthroughFields({
|
|
56501
56691
|
h,
|
|
56502
56692
|
hookType,
|
|
56693
|
+
eventName,
|
|
56503
56694
|
fields: converterConfig.numberPassthroughFields ?? [],
|
|
56504
56695
|
isValid: isImportableNumber,
|
|
56505
56696
|
describeInvalid: describeInvalidScalar,
|
|
@@ -56508,6 +56699,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56508
56699
|
...importPassthroughFields({
|
|
56509
56700
|
h,
|
|
56510
56701
|
hookType,
|
|
56702
|
+
eventName,
|
|
56511
56703
|
fields: converterConfig.stringPassthroughFields ?? [],
|
|
56512
56704
|
isValid: isImportableString,
|
|
56513
56705
|
describeInvalid: describeInvalidScalar,
|
|
@@ -56516,6 +56708,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56516
56708
|
...importPassthroughFields({
|
|
56517
56709
|
h,
|
|
56518
56710
|
hookType,
|
|
56711
|
+
eventName,
|
|
56519
56712
|
fields: converterConfig.arrayPassthroughFields ?? [],
|
|
56520
56713
|
isValid: isImportableArray,
|
|
56521
56714
|
describeInvalid: describeInvalidArray,
|
|
@@ -56524,6 +56717,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56524
56717
|
...importPassthroughFields({
|
|
56525
56718
|
h,
|
|
56526
56719
|
hookType,
|
|
56720
|
+
eventName,
|
|
56527
56721
|
fields: converterConfig.recordPassthroughFields ?? [],
|
|
56528
56722
|
isValid: isEmittableRecord,
|
|
56529
56723
|
describeInvalid: describeInvalidRecord,
|
|
@@ -56532,6 +56726,7 @@ function importAllPassthroughFields({ h, hookType, eventName, converterConfig, w
|
|
|
56532
56726
|
...importPassthroughFields({
|
|
56533
56727
|
h,
|
|
56534
56728
|
hookType,
|
|
56729
|
+
eventName,
|
|
56535
56730
|
fields: converterConfig.objectPassthroughFields ?? [],
|
|
56536
56731
|
isValid: isImportableObject,
|
|
56537
56732
|
describeInvalid: describeInvalidObject,
|
|
@@ -61364,6 +61559,11 @@ const GROKCLI_CONVERTER_CONFIG = {
|
|
|
61364
61559
|
tool: "env",
|
|
61365
61560
|
commandOnly: true
|
|
61366
61561
|
}],
|
|
61562
|
+
booleanPassthroughFields: [{
|
|
61563
|
+
canonical: "failClosed",
|
|
61564
|
+
tool: "failClosed",
|
|
61565
|
+
events: /* @__PURE__ */ new Set(["preToolUse", "beforeSubmitPrompt"])
|
|
61566
|
+
}],
|
|
61367
61567
|
arrayPassthroughFields: [{
|
|
61368
61568
|
canonical: "args",
|
|
61369
61569
|
tool: "args",
|
|
@@ -63013,6 +63213,15 @@ const LETTACODE_CONVERTER_CONFIG = {
|
|
|
63013
63213
|
timeoutUnit: "milliseconds"
|
|
63014
63214
|
};
|
|
63015
63215
|
/**
|
|
63216
|
+
* Letta Code documents settings-file hooks as deprecated in favor of Mods
|
|
63217
|
+
* (https://docs.letta.com/reference/deprecated/hooks) but still ships the
|
|
63218
|
+
* loader, so rulesync keeps emitting them and tells the user once per run.
|
|
63219
|
+
* Mods are not modeled: they are JS/TS code modules loaded only from
|
|
63220
|
+
* `~/.letta/mods/` or an agent's memory directory, with no declarative or
|
|
63221
|
+
* project-local form a canonical hook could be translated into.
|
|
63222
|
+
*/
|
|
63223
|
+
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";
|
|
63224
|
+
/**
|
|
63016
63225
|
* Letta Code keeps a boolean `disabled` switch beside the event arrays inside
|
|
63017
63226
|
* `hooks`. rulesync owns the whole `hooks` key, so the switch is read back from
|
|
63018
63227
|
* the existing file and carried into the generated one instead of being lost.
|
|
@@ -63071,6 +63280,11 @@ function parseLettacodeSettings$1(fileContent, filePath) {
|
|
|
63071
63280
|
* and UI preferences, ...), so generation merges the `hooks` key into the
|
|
63072
63281
|
* existing file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it.
|
|
63073
63282
|
*
|
|
63283
|
+
* Letta Code marks this surface deprecated in favor of Mods (see
|
|
63284
|
+
* `LETTACODE_HOOKS_DEPRECATION_WARNING`); generation warns once per run when it
|
|
63285
|
+
* writes any hook.
|
|
63286
|
+
*
|
|
63287
|
+
* @see https://docs.letta.com/reference/deprecated/hooks
|
|
63074
63288
|
* @see https://github.com/letta-ai/letta-code/blob/main/src/hooks/types.ts
|
|
63075
63289
|
* @see https://github.com/letta-ai/letta-code/blob/main/src/hooks/loader.ts
|
|
63076
63290
|
*/
|
|
@@ -63115,6 +63329,7 @@ var LettacodeHooks = class LettacodeHooks extends ToolHooks {
|
|
|
63115
63329
|
converterConfig: LETTACODE_CONVERTER_CONFIG,
|
|
63116
63330
|
logger
|
|
63117
63331
|
});
|
|
63332
|
+
if (Object.keys(hooks).length > 0) warnOnceWithFallback(logger, LETTACODE_HOOKS_DEPRECATION_WARNING);
|
|
63118
63333
|
const fileContent = applySharedConfigPatch({
|
|
63119
63334
|
fileKey: sharedConfigFileKey(paths),
|
|
63120
63335
|
feature: "hooks",
|
|
@@ -64242,14 +64457,16 @@ const QODER_SPEC = {
|
|
|
64242
64457
|
{
|
|
64243
64458
|
canonical: "rewakeMessage",
|
|
64244
64459
|
tool: "rewakeMessage",
|
|
64245
|
-
commandOnly: true
|
|
64460
|
+
commandOnly: true,
|
|
64461
|
+
onlyWithFlag: "asyncRewake"
|
|
64246
64462
|
},
|
|
64247
64463
|
{
|
|
64248
64464
|
canonical: "rewakeSummary",
|
|
64249
64465
|
tool: "rewakeSummary",
|
|
64250
64466
|
commandOnly: true,
|
|
64251
64467
|
maxLength: 300,
|
|
64252
|
-
collapseWhitespaceForLength: true
|
|
64468
|
+
collapseWhitespaceForLength: true,
|
|
64469
|
+
onlyWithFlag: "asyncRewake"
|
|
64253
64470
|
}
|
|
64254
64471
|
],
|
|
64255
64472
|
booleanPassthroughFields: [
|
|
@@ -65110,11 +65327,38 @@ const VIBE_DIR = ".vibe";
|
|
|
65110
65327
|
const VIBE_HOOKS_FILE_NAME = "hooks.toml";
|
|
65111
65328
|
/**
|
|
65112
65329
|
* Vibe scopes the tool-name `match` glob and the `strict` flag to tool hooks
|
|
65113
|
-
* (`pre_tool` / `post_tool`)
|
|
65114
|
-
*
|
|
65115
|
-
* @
|
|
65330
|
+
* (`pre_tool` / `post_tool`). `post_agent` takes a `match` too since v3.0.3,
|
|
65331
|
+
* but on the name of the agent that finished its turn, not a tool name (see
|
|
65332
|
+
* {@link VIBE_MAIN_AGENT_MATCH}).
|
|
65333
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
65116
65334
|
*/
|
|
65117
65335
|
const VIBE_TOOL_EVENTS = /* @__PURE__ */ new Set(["pre_tool", "post_tool"]);
|
|
65336
|
+
/** The Vibe event both canonical `stop` and `subagentStop` become. */
|
|
65337
|
+
const VIBE_POST_AGENT_EVENT = "post_agent";
|
|
65338
|
+
/**
|
|
65339
|
+
* The reserved agent alias a `post_agent` hook's `match` uses to run only
|
|
65340
|
+
* after the main agent. Without a `match`, `post_agent` runs after every
|
|
65341
|
+
* agent, subagents included (v3.0.2+), whereas canonical `stop` is
|
|
65342
|
+
* main-agent-only — so `stop` is written with this match.
|
|
65343
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
65344
|
+
*/
|
|
65345
|
+
const VIBE_MAIN_AGENT_MATCH = "main";
|
|
65346
|
+
/**
|
|
65347
|
+
* The `post_agent` `match` for a canonical `subagentStop` hook, or null to
|
|
65348
|
+
* skip it. Vibe has no selector for "every subagent but not the main agent",
|
|
65349
|
+
* so a matcher naming the subagent is required.
|
|
65350
|
+
*/
|
|
65351
|
+
function toVibeSubagentMatch({ matcher, logger }) {
|
|
65352
|
+
if (typeof matcher !== "string" || matcher === "" || matcher === "*") {
|
|
65353
|
+
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.");
|
|
65354
|
+
return null;
|
|
65355
|
+
}
|
|
65356
|
+
if (matcher.toLowerCase() === VIBE_MAIN_AGENT_MATCH) {
|
|
65357
|
+
logger?.warn("Vibe hooks: skipping a \"subagentStop\" hook whose matcher is \"main\". Vibe reserves \"main\" for the main agent; use a \"stop\" hook instead.");
|
|
65358
|
+
return null;
|
|
65359
|
+
}
|
|
65360
|
+
return matcher;
|
|
65361
|
+
}
|
|
65118
65362
|
const SUPPORTED_VIBE_EVENTS = new Set(VIBE_HOOK_EVENTS);
|
|
65119
65363
|
/**
|
|
65120
65364
|
* The `match` a canonical `Bash` matcher becomes. Vibe tests `match` against a
|
|
@@ -65138,7 +65382,8 @@ function fromVibeHookMatch(match) {
|
|
|
65138
65382
|
/**
|
|
65139
65383
|
* Build the flat `[[hooks]]` array for `.vibe/hooks.toml` from a canonical
|
|
65140
65384
|
* hooks config. Vibe uses a flat array where each entry carries its own event
|
|
65141
|
-
* `type`,
|
|
65385
|
+
* `type`, `match` glob/regex (a tool name for tool hooks, an agent name
|
|
65386
|
+
* for `post_agent`), and `command`. Only `type: "command"`
|
|
65142
65387
|
* canonical hooks are emitted: a Vibe hook is a shell command line. (v2.25.1
|
|
65143
65388
|
* to v2.25.4 briefly exec'd it without a shell under the unified harness and
|
|
65144
65389
|
* skipped any command containing a backslash; v2.25.5 restored shell
|
|
@@ -65146,7 +65391,7 @@ function fromVibeHookMatch(match) {
|
|
|
65146
65391
|
* now run through the shell" — and dropped the backslash skip.)
|
|
65147
65392
|
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.2/README.md
|
|
65148
65393
|
*/
|
|
65149
|
-
function canonicalToVibeHooks({ config, toolOverride }) {
|
|
65394
|
+
function canonicalToVibeHooks({ config, toolOverride, logger }) {
|
|
65150
65395
|
const shared = {};
|
|
65151
65396
|
for (const [event, defs] of Object.entries(config.hooks)) if (SUPPORTED_VIBE_EVENTS.has(event)) shared[event] = defs;
|
|
65152
65397
|
const effective = {
|
|
@@ -65154,30 +65399,40 @@ function canonicalToVibeHooks({ config, toolOverride }) {
|
|
|
65154
65399
|
...toolOverride
|
|
65155
65400
|
};
|
|
65156
65401
|
const hooks = [];
|
|
65402
|
+
const nextIndexByVibeEvent = /* @__PURE__ */ new Map();
|
|
65157
65403
|
for (const [event, defs] of Object.entries(effective)) {
|
|
65158
65404
|
if (!SUPPORTED_VIBE_EVENTS.has(event)) continue;
|
|
65159
65405
|
const vibeEvent = lookupOwn({
|
|
65160
65406
|
record: CANONICAL_TO_VIBE_EVENT_NAMES,
|
|
65161
65407
|
key: event
|
|
65162
65408
|
}) ?? event;
|
|
65163
|
-
let index = 0;
|
|
65164
65409
|
for (const def of defs) {
|
|
65165
65410
|
if ((def.type ?? "command") !== "command") continue;
|
|
65166
65411
|
if (typeof def.command !== "string") continue;
|
|
65167
|
-
const name = typeof def.name === "string" ? def.name : `${vibeEvent}-${index}`;
|
|
65168
65412
|
const isToolEvent = VIBE_TOOL_EVENTS.has(vibeEvent);
|
|
65413
|
+
let match;
|
|
65414
|
+
if (isToolEvent) match = toVibeHookMatch(def.matcher);
|
|
65415
|
+
else if (event === "subagentStop") {
|
|
65416
|
+
const subagentMatch = toVibeSubagentMatch({
|
|
65417
|
+
matcher: def.matcher,
|
|
65418
|
+
logger
|
|
65419
|
+
});
|
|
65420
|
+
if (subagentMatch === null) continue;
|
|
65421
|
+
match = subagentMatch;
|
|
65422
|
+
} else match = VIBE_MAIN_AGENT_MATCH;
|
|
65423
|
+
const index = nextIndexByVibeEvent.get(vibeEvent) ?? 0;
|
|
65169
65424
|
const entry = {
|
|
65170
|
-
name
|
|
65425
|
+
name: typeof def.name === "string" ? def.name : `${vibeEvent}-${index}`,
|
|
65171
65426
|
type: vibeEvent,
|
|
65427
|
+
match,
|
|
65172
65428
|
command: def.command
|
|
65173
65429
|
};
|
|
65174
|
-
if (isToolEvent) entry.match = toVibeHookMatch(def.matcher);
|
|
65175
65430
|
if (typeof def.timeout === "number") entry.timeout = def.timeout;
|
|
65176
65431
|
const strict = def.strict;
|
|
65177
65432
|
if (isToolEvent && typeof strict === "boolean") entry.strict = strict;
|
|
65178
65433
|
if (typeof def.description === "string") entry.description = def.description;
|
|
65179
65434
|
hooks.push(entry);
|
|
65180
|
-
index
|
|
65435
|
+
nextIndexByVibeEvent.set(vibeEvent, index + 1);
|
|
65181
65436
|
}
|
|
65182
65437
|
}
|
|
65183
65438
|
return { hooks };
|
|
@@ -65189,14 +65444,22 @@ function vibeEntryToCanonicalDef(raw) {
|
|
|
65189
65444
|
const vibeEvent = typeof entry.type === "string" ? entry.type : void 0;
|
|
65190
65445
|
if (vibeEvent === void 0) return null;
|
|
65191
65446
|
if (isPrototypePollutionKey(vibeEvent)) return null;
|
|
65192
|
-
|
|
65447
|
+
let canonicalEvent = lookupOwn({
|
|
65193
65448
|
record: VIBE_TO_CANONICAL_EVENT_NAMES,
|
|
65194
65449
|
key: vibeEvent
|
|
65195
65450
|
}) ?? vibeEvent;
|
|
65196
65451
|
const def = { type: "command" };
|
|
65197
65452
|
if (typeof entry.command === "string") def.command = entry.command;
|
|
65198
|
-
|
|
65199
|
-
|
|
65453
|
+
if (vibeEvent === VIBE_POST_AGENT_EVENT) {
|
|
65454
|
+
const agentMatch = typeof entry.match === "string" ? entry.match : "";
|
|
65455
|
+
if (agentMatch !== "" && agentMatch !== "*" && agentMatch.toLowerCase() !== VIBE_MAIN_AGENT_MATCH) {
|
|
65456
|
+
canonicalEvent = "subagentStop";
|
|
65457
|
+
def.matcher = agentMatch;
|
|
65458
|
+
}
|
|
65459
|
+
} else {
|
|
65460
|
+
const matcher = fromVibeHookMatch(entry.match);
|
|
65461
|
+
if (matcher !== void 0) def.matcher = matcher;
|
|
65462
|
+
}
|
|
65200
65463
|
if (typeof entry.timeout === "number") def.timeout = entry.timeout;
|
|
65201
65464
|
if (typeof entry.name === "string") def.name = entry.name;
|
|
65202
65465
|
if (typeof entry.description === "string") def.description = entry.description;
|
|
@@ -65227,6 +65490,26 @@ function vibeHooksToCanonical(parsed) {
|
|
|
65227
65490
|
}
|
|
65228
65491
|
return canonical;
|
|
65229
65492
|
}
|
|
65493
|
+
/** Vibe reads a `match` with a `re:` prefix or glob metacharacters as a pattern. */
|
|
65494
|
+
const VIBE_PATTERN_MATCH = /^re:|[*?[]/;
|
|
65495
|
+
/**
|
|
65496
|
+
* Move the imported `subagentStop` hooks into the `vibe` override when any of
|
|
65497
|
+
* them matches agents by a regex or glob rather than by a literal name. Such a
|
|
65498
|
+
* pattern can also select the main agent (a main session answers to its mode
|
|
65499
|
+
* name, e.g. `accept-edits`), and other tools would read it as their own
|
|
65500
|
+
* matcher syntax, so it is kept Vibe-only. The override replaces the shared
|
|
65501
|
+
* list for its event, so the whole list moves to keep every entry on Vibe.
|
|
65502
|
+
*/
|
|
65503
|
+
function takeVibeOnlySubagentStop({ hooks, logger }) {
|
|
65504
|
+
const defs = lookupOwn({
|
|
65505
|
+
record: hooks,
|
|
65506
|
+
key: "subagentStop"
|
|
65507
|
+
});
|
|
65508
|
+
if (defs === void 0 || !defs.some((def) => typeof def.matcher === "string" && VIBE_PATTERN_MATCH.test(def.matcher))) return;
|
|
65509
|
+
delete hooks.subagentStop;
|
|
65510
|
+
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.");
|
|
65511
|
+
return defs;
|
|
65512
|
+
}
|
|
65230
65513
|
function parseVibeToml(fileContent) {
|
|
65231
65514
|
const parsed = smolToml.parse(fileContent || smolToml.stringify({}));
|
|
65232
65515
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
@@ -65266,12 +65549,13 @@ var VibeHooks = class extends ToolHooks {
|
|
|
65266
65549
|
validate
|
|
65267
65550
|
});
|
|
65268
65551
|
}
|
|
65269
|
-
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false }) {
|
|
65552
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
65270
65553
|
const paths = this.getSettablePaths({ global });
|
|
65271
65554
|
const config = rulesyncHooks.getJson();
|
|
65272
65555
|
const vibeHooks = canonicalToVibeHooks({
|
|
65273
65556
|
config,
|
|
65274
|
-
toolOverride: config.vibe?.hooks
|
|
65557
|
+
toolOverride: config.vibe?.hooks,
|
|
65558
|
+
logger
|
|
65275
65559
|
});
|
|
65276
65560
|
const fileContent = smolToml.stringify(vibeHooks);
|
|
65277
65561
|
return new this({
|
|
@@ -65282,15 +65566,30 @@ var VibeHooks = class extends ToolHooks {
|
|
|
65282
65566
|
validate
|
|
65283
65567
|
});
|
|
65284
65568
|
}
|
|
65285
|
-
toRulesyncHooks() {
|
|
65569
|
+
toRulesyncHooks(options = {}) {
|
|
65286
65570
|
const hooks = vibeHooksToCanonical(this.parseFileContentOrThrow({
|
|
65287
65571
|
displayName: "Vibe",
|
|
65288
65572
|
parse: (fileContent) => parseVibeToml(fileContent)
|
|
65289
65573
|
}));
|
|
65290
|
-
|
|
65574
|
+
const vibeOnlySubagentStop = takeVibeOnlySubagentStop({
|
|
65575
|
+
hooks,
|
|
65576
|
+
logger: options.logger
|
|
65577
|
+
});
|
|
65578
|
+
const config = buildImportedHooksConfig({
|
|
65291
65579
|
hooks,
|
|
65292
65580
|
overrideKey: "vibe"
|
|
65293
|
-
})
|
|
65581
|
+
});
|
|
65582
|
+
if (vibeOnlySubagentStop !== void 0) {
|
|
65583
|
+
const override = config.vibe ?? {};
|
|
65584
|
+
config.vibe = {
|
|
65585
|
+
...override,
|
|
65586
|
+
hooks: {
|
|
65587
|
+
...override.hooks,
|
|
65588
|
+
subagentStop: vibeOnlySubagentStop
|
|
65589
|
+
}
|
|
65590
|
+
};
|
|
65591
|
+
}
|
|
65592
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(config, null, 2) });
|
|
65294
65593
|
}
|
|
65295
65594
|
validate() {
|
|
65296
65595
|
try {
|
|
@@ -69629,7 +69928,7 @@ var CodebuddyMcp = class CodebuddyMcp extends ToolMcp {
|
|
|
69629
69928
|
}
|
|
69630
69929
|
};
|
|
69631
69930
|
// src/features/mcp/codebuff-mcp.ts
|
|
69632
|
-
const TOOL_NAME$
|
|
69931
|
+
const TOOL_NAME$2 = "codebuff";
|
|
69633
69932
|
const MAPPED_KEYS = /* @__PURE__ */ new Set([
|
|
69634
69933
|
"type",
|
|
69635
69934
|
"transport",
|
|
@@ -69664,14 +69963,14 @@ function toCodebuffEnv({ name, env, logger }) {
|
|
|
69664
69963
|
}
|
|
69665
69964
|
if (value.startsWith("$")) {
|
|
69666
69965
|
warnAndSkipMcpServer({
|
|
69667
|
-
toolName: TOOL_NAME$
|
|
69966
|
+
toolName: TOOL_NAME$2,
|
|
69668
69967
|
serverName: name,
|
|
69669
69968
|
reason: `env ${quoteValueForWarning(key)} starting with "$" that is not a whole \${VAR} reference, which Codebuff would resolve as a variable name and fail to load the whole mcp.json`,
|
|
69670
69969
|
logger
|
|
69671
69970
|
});
|
|
69672
69971
|
return null;
|
|
69673
69972
|
}
|
|
69674
|
-
if (CANONICAL_ENV_REF.test(value)) logger?.warn(`${TOOL_NAME$
|
|
69973
|
+
if (CANONICAL_ENV_REF.test(value)) logger?.warn(`${TOOL_NAME$2} MCP: env ${quoteValueForWarning(key)} of server ${quoteValueForWarning(name)} embeds a \${VAR} reference; Codebuff only resolves a whole-value \$VAR, so it is written as literal text.`);
|
|
69675
69974
|
converted.push([key, value]);
|
|
69676
69975
|
}
|
|
69677
69976
|
return Object.fromEntries(converted);
|
|
@@ -69688,31 +69987,31 @@ function toCodebuffEnv({ name, env, logger }) {
|
|
|
69688
69987
|
*/
|
|
69689
69988
|
function toCodebuffServer({ name, server, logger }) {
|
|
69690
69989
|
if (server.disabled === true) return warnAndSkipMcpServer({
|
|
69691
|
-
toolName: TOOL_NAME$
|
|
69990
|
+
toolName: TOOL_NAME$2,
|
|
69692
69991
|
serverName: name,
|
|
69693
69992
|
reason: "disabled: true, which Codebuff has no per-server switch for",
|
|
69694
69993
|
logger
|
|
69695
69994
|
});
|
|
69696
69995
|
const transport = server.type ?? server.transport;
|
|
69697
69996
|
if (transport === "ws") return warnAndSkipMcpServer({
|
|
69698
|
-
toolName: TOOL_NAME$
|
|
69997
|
+
toolName: TOOL_NAME$2,
|
|
69699
69998
|
serverName: name,
|
|
69700
69999
|
reason: "the WebSocket transport, which Codebuff does not support",
|
|
69701
70000
|
logger
|
|
69702
70001
|
});
|
|
69703
70002
|
const dropped = Object.keys(server).filter((key) => !MAPPED_KEYS.has(key));
|
|
69704
|
-
if (dropped.length > 0) logger?.warn(`${TOOL_NAME$
|
|
70003
|
+
if (dropped.length > 0) logger?.warn(`${TOOL_NAME$2} MCP: dropping ${dropped.map((key) => quoteValueForWarning(key)).join(", ")} from server ${quoteValueForWarning(name)}; Codebuff rejects keys outside its server schema.`);
|
|
69705
70004
|
if (isRemoteMcpServer(server)) {
|
|
69706
70005
|
const url = resolveRemoteMcpUrl(server);
|
|
69707
70006
|
if (url === void 0) return warnAndSkipMcpServer({
|
|
69708
|
-
toolName: TOOL_NAME$
|
|
70007
|
+
toolName: TOOL_NAME$2,
|
|
69709
70008
|
serverName: name,
|
|
69710
70009
|
reason: "a remote transport without a url",
|
|
69711
70010
|
logger
|
|
69712
70011
|
});
|
|
69713
70012
|
const params = server.params;
|
|
69714
70013
|
const validParams = isRecord(params) && Object.values(params).every((value) => typeof value === "string");
|
|
69715
|
-
if (params !== void 0 && !validParams) logger?.warn(`${TOOL_NAME$
|
|
70014
|
+
if (params !== void 0 && !validParams) logger?.warn(`${TOOL_NAME$2} MCP: dropping params from server ${quoteValueForWarning(name)}; Codebuff accepts only a map of string values.`);
|
|
69716
70015
|
return {
|
|
69717
70016
|
type: transport === "sse" ? "sse" : "http",
|
|
69718
70017
|
url,
|
|
@@ -69722,7 +70021,7 @@ function toCodebuffServer({ name, server, logger }) {
|
|
|
69722
70021
|
}
|
|
69723
70022
|
const [command, ...args] = resolveLocalMcpCommand(server);
|
|
69724
70023
|
if (command === void 0) return warnAndSkipMcpServer({
|
|
69725
|
-
toolName: TOOL_NAME$
|
|
70024
|
+
toolName: TOOL_NAME$2,
|
|
69726
70025
|
serverName: name,
|
|
69727
70026
|
reason: "no command to spawn",
|
|
69728
70027
|
logger
|
|
@@ -72392,7 +72691,7 @@ var CursorMcp = class CursorMcp extends ToolMcp {
|
|
|
72392
72691
|
}
|
|
72393
72692
|
};
|
|
72394
72693
|
// src/features/mcp/deepagents-mcp.ts
|
|
72395
|
-
const TOOL_NAME = "deepagents";
|
|
72694
|
+
const TOOL_NAME$1 = "deepagents";
|
|
72396
72695
|
/**
|
|
72397
72696
|
* Map a canonical transport onto the three dcode accepts.
|
|
72398
72697
|
*
|
|
@@ -72433,7 +72732,7 @@ function applyDeepagentsCommand({ name, server, converted, logger }) {
|
|
|
72433
72732
|
return true;
|
|
72434
72733
|
}
|
|
72435
72734
|
warnAndSkipMcpServer({
|
|
72436
|
-
toolName: TOOL_NAME,
|
|
72735
|
+
toolName: TOOL_NAME$1,
|
|
72437
72736
|
serverName: name,
|
|
72438
72737
|
reason: "an empty command list, which leaves deepagents nothing to spawn",
|
|
72439
72738
|
logger
|
|
@@ -72461,7 +72760,7 @@ function applyDeepagentsCommand({ name, server, converted, logger }) {
|
|
|
72461
72760
|
*/
|
|
72462
72761
|
function toDeepagentsOauth({ name, oauth, converted, logger }) {
|
|
72463
72762
|
if (!isSlackOauthServer(converted)) {
|
|
72464
|
-
logger?.warn(`${TOOL_NAME} MCP: dropping oauth on "${name}"; deepagents reads it only on a Slack (slack.com) server with auth: "oauth" and skips any other server that sets it.`);
|
|
72763
|
+
logger?.warn(`${TOOL_NAME$1} MCP: dropping oauth on "${name}"; deepagents reads it only on a Slack (slack.com) server with auth: "oauth" and skips any other server that sets it.`);
|
|
72465
72764
|
return;
|
|
72466
72765
|
}
|
|
72467
72766
|
const source = isRecord(oauth) ? oauth : {};
|
|
@@ -72470,18 +72769,18 @@ function toDeepagentsOauth({ name, oauth, converted, logger }) {
|
|
|
72470
72769
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
72471
72770
|
const nativeKey = key === "clientId" ? "client_id" : key;
|
|
72472
72771
|
if (nativeKey !== "client_id" && nativeKey !== "client_secret") {
|
|
72473
|
-
logger?.warn(`${TOOL_NAME} MCP: dropping oauth.${key} on "${name}"; deepagents accepts only client_id and client_secret.`);
|
|
72772
|
+
logger?.warn(`${TOOL_NAME$1} MCP: dropping oauth.${key} on "${name}"; deepagents accepts only client_id and client_secret.`);
|
|
72474
72773
|
continue;
|
|
72475
72774
|
}
|
|
72476
72775
|
if (nativeKey !== key && isDeepagentsOauthValue(source[nativeKey])) continue;
|
|
72477
72776
|
if (!isDeepagentsOauthValue(value)) {
|
|
72478
|
-
logger?.warn(`${TOOL_NAME} MCP: dropping oauth.${key} on "${name}"; deepagents requires a non-empty string without control characters.`);
|
|
72777
|
+
logger?.warn(`${TOOL_NAME$1} MCP: dropping oauth.${key} on "${name}"; deepagents requires a non-empty string without control characters.`);
|
|
72479
72778
|
continue;
|
|
72480
72779
|
}
|
|
72481
72780
|
result[nativeKey] = value;
|
|
72482
72781
|
}
|
|
72483
72782
|
if (result.client_id === void 0) {
|
|
72484
|
-
logger?.warn(`${TOOL_NAME} MCP: dropping oauth on "${name}"; deepagents requires oauth.client_id (canonical clientId) and skips a server without it.`);
|
|
72783
|
+
logger?.warn(`${TOOL_NAME$1} MCP: dropping oauth on "${name}"; deepagents requires oauth.client_id (canonical clientId) and skips a server without it.`);
|
|
72485
72784
|
return;
|
|
72486
72785
|
}
|
|
72487
72786
|
return result;
|
|
@@ -72510,14 +72809,14 @@ function isSlackOauthServer(server) {
|
|
|
72510
72809
|
*/
|
|
72511
72810
|
function toDeepagentsServer({ name, server, logger }) {
|
|
72512
72811
|
if (server.disabled === true) return warnAndSkipMcpServer({
|
|
72513
|
-
toolName: TOOL_NAME,
|
|
72812
|
+
toolName: TOOL_NAME$1,
|
|
72514
72813
|
serverName: name,
|
|
72515
72814
|
reason: "disabled: true, which deepagents does not read from .mcp.json (the server would still run)",
|
|
72516
72815
|
logger
|
|
72517
72816
|
});
|
|
72518
72817
|
const rawTransport = server.transport ?? server.type;
|
|
72519
72818
|
if (rawTransport === "ws") return warnAndSkipMcpServer({
|
|
72520
|
-
toolName: TOOL_NAME,
|
|
72819
|
+
toolName: TOOL_NAME$1,
|
|
72521
72820
|
serverName: name,
|
|
72522
72821
|
reason: "the WebSocket transport, which deepagents does not support",
|
|
72523
72822
|
logger
|
|
@@ -72547,21 +72846,21 @@ function toDeepagentsServer({ name, server, logger }) {
|
|
|
72547
72846
|
if (deepagentsOauth !== void 0) converted.oauth = deepagentsOauth;
|
|
72548
72847
|
}
|
|
72549
72848
|
if (enabledTools !== void 0 && disabledTools !== void 0) return warnAndSkipMcpServer({
|
|
72550
|
-
toolName: TOOL_NAME,
|
|
72849
|
+
toolName: TOOL_NAME$1,
|
|
72551
72850
|
serverName: name,
|
|
72552
72851
|
reason: "both enabledTools and disabledTools, which deepagents rejects — pick one",
|
|
72553
72852
|
logger
|
|
72554
72853
|
});
|
|
72555
72854
|
if (enabledTools !== void 0) {
|
|
72556
72855
|
if (enabledTools.length === 0) return warnAndSkipMcpServer({
|
|
72557
|
-
toolName: TOOL_NAME,
|
|
72856
|
+
toolName: TOOL_NAME$1,
|
|
72558
72857
|
serverName: name,
|
|
72559
72858
|
reason: "an empty enabledTools list, which allows no tools at all and which deepagents rejects",
|
|
72560
72859
|
logger
|
|
72561
72860
|
});
|
|
72562
72861
|
converted.allowedTools = enabledTools;
|
|
72563
72862
|
} else if (disabledTools !== void 0) {
|
|
72564
|
-
if (disabledTools.length === 0) logger?.warn(`${TOOL_NAME} MCP: dropping the empty disabledTools list on "${name}"; it denies nothing, and deepagents rejects the empty form.`);
|
|
72863
|
+
if (disabledTools.length === 0) logger?.warn(`${TOOL_NAME$1} MCP: dropping the empty disabledTools list on "${name}"; it denies nothing, and deepagents rejects the empty form.`);
|
|
72565
72864
|
else converted.disabledTools = disabledTools;
|
|
72566
72865
|
}
|
|
72567
72866
|
return converted;
|
|
@@ -73213,6 +73512,32 @@ var DshMcp = class DshMcp extends ToolMcp {
|
|
|
73213
73512
|
}
|
|
73214
73513
|
};
|
|
73215
73514
|
// src/features/mcp/factorydroid-mcp.ts
|
|
73515
|
+
const TOOL_NAME = "factorydroid";
|
|
73516
|
+
/**
|
|
73517
|
+
* Drop servers whose `enabledTools` allowlist is empty.
|
|
73518
|
+
*
|
|
73519
|
+
* Canonically an empty allowlist allows no tools, but Droid applies
|
|
73520
|
+
* `enabledTools` only "when the list is non-empty"
|
|
73521
|
+
* (https://docs.factory.com/harness/mcp), so writing `[]` would load every
|
|
73522
|
+
* tool the server reports. Skipping the server is the only form that does not
|
|
73523
|
+
* hand the model more tools than the canonical config allows.
|
|
73524
|
+
*/
|
|
73525
|
+
function skipEmptyAllowlistServers({ mcpServers, logger }) {
|
|
73526
|
+
const kept = {};
|
|
73527
|
+
for (const [name, server] of Object.entries(mcpServers)) {
|
|
73528
|
+
if (server.enabledTools?.length === 0) {
|
|
73529
|
+
warnAndSkipMcpServer({
|
|
73530
|
+
toolName: TOOL_NAME,
|
|
73531
|
+
serverName: name,
|
|
73532
|
+
reason: "an empty enabledTools list, which allows no tools at all but which Factory Droid treats as no filter",
|
|
73533
|
+
logger
|
|
73534
|
+
});
|
|
73535
|
+
continue;
|
|
73536
|
+
}
|
|
73537
|
+
kept[name] = server;
|
|
73538
|
+
}
|
|
73539
|
+
return kept;
|
|
73540
|
+
}
|
|
73216
73541
|
var FactorydroidMcp = class FactorydroidMcp extends ToolMcp {
|
|
73217
73542
|
json;
|
|
73218
73543
|
constructor(params) {
|
|
@@ -73238,8 +73563,11 @@ var FactorydroidMcp = class FactorydroidMcp extends ToolMcp {
|
|
|
73238
73563
|
validate
|
|
73239
73564
|
});
|
|
73240
73565
|
}
|
|
73241
|
-
static fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true }) {
|
|
73242
|
-
const factorydroidConfig = { mcpServers:
|
|
73566
|
+
static fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, logger }) {
|
|
73567
|
+
const factorydroidConfig = { mcpServers: skipEmptyAllowlistServers({
|
|
73568
|
+
mcpServers: rulesyncMcp.getMcpServers(),
|
|
73569
|
+
logger
|
|
73570
|
+
}) };
|
|
73243
73571
|
const fileContent = JSON.stringify(factorydroidConfig, null, 2);
|
|
73244
73572
|
return new FactorydroidMcp({
|
|
73245
73573
|
outputRoot,
|
|
@@ -76878,6 +77206,12 @@ const REASONIX_PLUGIN_FIELDS = [
|
|
|
76878
77206
|
"load",
|
|
76879
77207
|
"oauth_allow_missing_pkce_metadata"
|
|
76880
77208
|
];
|
|
77209
|
+
/**
|
|
77210
|
+
* The `load` mode that puts a server's tools in the provider schema. Reasonix
|
|
77211
|
+
* compares it case-insensitively after trimming (`normalizeMCPLoad`).
|
|
77212
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/studio-v2.53.0/internal/contract/config/mcp_load.go
|
|
77213
|
+
*/
|
|
77214
|
+
const REASONIX_MCP_LOAD_ALWAYS = "always";
|
|
76881
77215
|
/** The `[[plugins]]` key that canonical `disabledTools` is written to and read from. */
|
|
76882
77216
|
const REASONIX_DISABLED_TOOLS_KEY = "disabled_tools";
|
|
76883
77217
|
var ReasonixMcp = class ReasonixMcp extends ToolMcp {
|
|
@@ -77042,7 +77376,9 @@ function hasExpectedType(value, expected) {
|
|
|
77042
77376
|
* `concurrency` string decodes fine but is ignored in favor of the server-name
|
|
77043
77377
|
* default, so it is dropped too rather than written as a setting that does
|
|
77044
77378
|
* nothing. An unknown `load` string is read as `deferred`, the default, so it
|
|
77045
|
-
* passes through as authored.
|
|
77379
|
+
* passes through as authored. A project `load = "always"` also passes through,
|
|
77380
|
+
* but is warned about in `writePassthroughFields`: see the v2.53.0 note on
|
|
77381
|
+
* `REASONIX_PLUGIN_FIELDS`.
|
|
77046
77382
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/v2.31.0/internal/contract/config/plugin_entry.go
|
|
77047
77383
|
*/
|
|
77048
77384
|
function invalidPassthroughFieldReason(field, value) {
|
|
@@ -77082,6 +77418,7 @@ function writePassthroughFields({ name, server, plugin, global, logger }) {
|
|
|
77082
77418
|
continue;
|
|
77083
77419
|
}
|
|
77084
77420
|
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.`));
|
|
77421
|
+
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.`);
|
|
77085
77422
|
plugin[field] = value;
|
|
77086
77423
|
}
|
|
77087
77424
|
}
|
|
@@ -86020,6 +86357,41 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
86020
86357
|
// src/features/permissions/codebuddy-permissions.ts
|
|
86021
86358
|
/** Top-level key of `.codebuddy/settings.json` that rulesync owns. */
|
|
86022
86359
|
const CODEBUDDY_PERMISSIONS_KEY = "permissions";
|
|
86360
|
+
/**
|
|
86361
|
+
* The top-level settings groups the `codebuddy` override may author, each with
|
|
86362
|
+
* the only keys of that group rulesync writes and imports. A group's other
|
|
86363
|
+
* keys are kept as the file has them; an override key not listed here is
|
|
86364
|
+
* skipped with a warning.
|
|
86365
|
+
*
|
|
86366
|
+
* - `autoReview`: the `auto` permission mode's review policy (`policy`
|
|
86367
|
+
* replaces the built-in security policy, `extraPolicy` is appended to it).
|
|
86368
|
+
* - `autoMode`: the legacy natural-language `auto` rules, still read as
|
|
86369
|
+
* additional policy.
|
|
86370
|
+
* - `memory.parentDiscovery`: whether `CODEBUDDY.md` / `AGENTS.md` are also
|
|
86371
|
+
* discovered in parent directories of the launch directory.
|
|
86372
|
+
*
|
|
86373
|
+
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
86374
|
+
* @see https://www.codebuddy.ai/docs/cli/memory
|
|
86375
|
+
*/
|
|
86376
|
+
const CODEBUDDY_OVERRIDE_GROUP_KEYS = {
|
|
86377
|
+
autoReview: ["policy", "extraPolicy"],
|
|
86378
|
+
autoMode: [
|
|
86379
|
+
"environment",
|
|
86380
|
+
"allow",
|
|
86381
|
+
"soft_deny",
|
|
86382
|
+
"hard_deny"
|
|
86383
|
+
],
|
|
86384
|
+
memory: ["parentDiscovery"]
|
|
86385
|
+
};
|
|
86386
|
+
const CODEBUDDY_OVERRIDE_GROUPS = Object.keys(CODEBUDDY_OVERRIDE_GROUP_KEYS);
|
|
86387
|
+
/**
|
|
86388
|
+
* Groups CodeBuddy reads from the user and project-local settings only: the
|
|
86389
|
+
* shared project `.codebuddy/settings.json` is not read for them, so a checked-in
|
|
86390
|
+
* repository cannot relax the review of its own actions. They are written and
|
|
86391
|
+
* imported in global mode alone.
|
|
86392
|
+
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
86393
|
+
*/
|
|
86394
|
+
const CODEBUDDY_USER_ONLY_GROUPS = /* @__PURE__ */ new Set(["autoReview", "autoMode"]);
|
|
86023
86395
|
/** The rule lists under `permissions`, one per canonical action. */
|
|
86024
86396
|
const CODEBUDDY_RULE_LISTS = [
|
|
86025
86397
|
"allow",
|
|
@@ -86173,6 +86545,87 @@ function buildCodebuddyRuleLists({ config, existingPermissions, logger }) {
|
|
|
86173
86545
|
return lists;
|
|
86174
86546
|
}
|
|
86175
86547
|
/**
|
|
86548
|
+
* Whether a settings value has the shape the `codebuddy` override schema
|
|
86549
|
+
* accepts for that key, so an off-spec value is left in the file instead of
|
|
86550
|
+
* being written or making the imported permissions file fail validation.
|
|
86551
|
+
*/
|
|
86552
|
+
function isLiftableCodebuddyValue(key, value) {
|
|
86553
|
+
switch (key) {
|
|
86554
|
+
case "policy":
|
|
86555
|
+
case "extraPolicy": return typeof value === "string";
|
|
86556
|
+
case "parentDiscovery": return typeof value === "boolean";
|
|
86557
|
+
default: return isStringArray(value);
|
|
86558
|
+
}
|
|
86559
|
+
}
|
|
86560
|
+
/** Name every written override setting that can make the `auto` review more lenient. */
|
|
86561
|
+
function announceCodebuddyReviewSettings({ picked, group, where, logger }) {
|
|
86562
|
+
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.`);
|
|
86563
|
+
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.`);
|
|
86564
|
+
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.`);
|
|
86565
|
+
}
|
|
86566
|
+
/**
|
|
86567
|
+
* The top-level keys the `codebuddy` override contributes to the settings
|
|
86568
|
+
* patch, besides `permissions`. Each group is the existing group with the
|
|
86569
|
+
* authored keys laid over it; a group is only in the patch when authored, so
|
|
86570
|
+
* a hand-set value survives an unrelated regeneration.
|
|
86571
|
+
*/
|
|
86572
|
+
function buildCodebuddyOverridePatch({ override, existing, global, where, logger }) {
|
|
86573
|
+
const patch = {};
|
|
86574
|
+
if (override === void 0) return patch;
|
|
86575
|
+
for (const group of CODEBUDDY_OVERRIDE_GROUPS) {
|
|
86576
|
+
const authored = override[group];
|
|
86577
|
+
if (!isRecord(authored)) continue;
|
|
86578
|
+
if (CODEBUDDY_USER_ONLY_GROUPS.has(group) && !global) {
|
|
86579
|
+
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.`);
|
|
86580
|
+
continue;
|
|
86581
|
+
}
|
|
86582
|
+
const allowedKeys = CODEBUDDY_OVERRIDE_GROUP_KEYS[group];
|
|
86583
|
+
const picked = {};
|
|
86584
|
+
for (const [key, value] of Object.entries(authored)) {
|
|
86585
|
+
if (isPrototypePollutionKey(key) || value === void 0) continue;
|
|
86586
|
+
if (!allowedKeys.includes(key)) {
|
|
86587
|
+
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.`);
|
|
86588
|
+
continue;
|
|
86589
|
+
}
|
|
86590
|
+
picked[key] = value;
|
|
86591
|
+
}
|
|
86592
|
+
if (Object.keys(picked).length === 0) continue;
|
|
86593
|
+
const existingValue = existing[group];
|
|
86594
|
+
if (existingValue !== void 0 && !isRecord(existingValue)) {
|
|
86595
|
+
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}.`);
|
|
86596
|
+
continue;
|
|
86597
|
+
}
|
|
86598
|
+
patch[group] = {
|
|
86599
|
+
...existingValue,
|
|
86600
|
+
...picked
|
|
86601
|
+
};
|
|
86602
|
+
announceCodebuddyReviewSettings({
|
|
86603
|
+
picked,
|
|
86604
|
+
group,
|
|
86605
|
+
where,
|
|
86606
|
+
logger
|
|
86607
|
+
});
|
|
86608
|
+
}
|
|
86609
|
+
return patch;
|
|
86610
|
+
}
|
|
86611
|
+
/**
|
|
86612
|
+
* Lift the settings the `codebuddy` override authors back out of a settings
|
|
86613
|
+
* file. `autoReview` and `autoMode` are lifted only from the user file, the
|
|
86614
|
+
* one of the two rulesync manages that CodeBuddy reads them from.
|
|
86615
|
+
*/
|
|
86616
|
+
function liftCodebuddyOverride({ settings, global }) {
|
|
86617
|
+
const lifted = {};
|
|
86618
|
+
for (const group of CODEBUDDY_OVERRIDE_GROUPS) {
|
|
86619
|
+
if (CODEBUDDY_USER_ONLY_GROUPS.has(group) && !global) continue;
|
|
86620
|
+
const existingGroup = settings[group];
|
|
86621
|
+
if (!isRecord(existingGroup)) continue;
|
|
86622
|
+
const picked = {};
|
|
86623
|
+
for (const key of CODEBUDDY_OVERRIDE_GROUP_KEYS[group]) if (Object.hasOwn(existingGroup, key) && isLiftableCodebuddyValue(key, existingGroup[key])) picked[key] = existingGroup[key];
|
|
86624
|
+
if (Object.keys(picked).length > 0) lifted[group] = picked;
|
|
86625
|
+
}
|
|
86626
|
+
return lifted;
|
|
86627
|
+
}
|
|
86628
|
+
/**
|
|
86176
86629
|
* CodeBuddy Code permissions.
|
|
86177
86630
|
*
|
|
86178
86631
|
* Rules live under the `permissions` key of `.codebuddy/settings.json`
|
|
@@ -86183,7 +86636,10 @@ function buildCodebuddyRuleLists({ config, existingPermissions, logger }) {
|
|
|
86183
86636
|
* writes go through the shared-config gateway, the sibling keys of the rule
|
|
86184
86637
|
* lists (`defaultMode`, `additionalDirectories`, `trustedDirectories`, ...)
|
|
86185
86638
|
* are kept, and the file is never deleted. The gitignored
|
|
86186
|
-
* `.codebuddy/settings.local.json` layer is left to CodeBuddy.
|
|
86639
|
+
* `.codebuddy/settings.local.json` layer is left to CodeBuddy. The
|
|
86640
|
+
* `codebuddy` override's other settings (`autoReview`, `autoMode`,
|
|
86641
|
+
* `memory.parentDiscovery`) go through `buildCodebuddyOverridePatch` /
|
|
86642
|
+
* `liftCodebuddyOverride`.
|
|
86187
86643
|
*
|
|
86188
86644
|
* @see https://www.codebuddy.ai/docs/cli/permissions
|
|
86189
86645
|
* @see https://www.codebuddy.ai/docs/cli/settings
|
|
@@ -86222,8 +86678,9 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86222
86678
|
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
86223
86679
|
});
|
|
86224
86680
|
const existingPermissions = isRecord(existing[CODEBUDDY_PERMISSIONS_KEY]) ? existing[CODEBUDDY_PERMISSIONS_KEY] : {};
|
|
86681
|
+
const config = rulesyncPermissions.getJson();
|
|
86225
86682
|
const lists = buildCodebuddyRuleLists({
|
|
86226
|
-
config
|
|
86683
|
+
config,
|
|
86227
86684
|
existingPermissions,
|
|
86228
86685
|
logger
|
|
86229
86686
|
});
|
|
@@ -86238,7 +86695,16 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86238
86695
|
fileKey: sharedConfigFileKey(paths),
|
|
86239
86696
|
feature: "permissions",
|
|
86240
86697
|
existingContent,
|
|
86241
|
-
patch: {
|
|
86698
|
+
patch: {
|
|
86699
|
+
[CODEBUDDY_PERMISSIONS_KEY]: Object.keys(permissions).length > 0 ? permissions : void 0,
|
|
86700
|
+
...buildCodebuddyOverridePatch({
|
|
86701
|
+
override: config.codebuddy,
|
|
86702
|
+
existing,
|
|
86703
|
+
global,
|
|
86704
|
+
where: `${join(paths.relativeDirPath, paths.relativeFilePath)}${global ? " in the home directory" : ""}`,
|
|
86705
|
+
logger
|
|
86706
|
+
})
|
|
86707
|
+
},
|
|
86242
86708
|
filePath
|
|
86243
86709
|
}),
|
|
86244
86710
|
validate: true,
|
|
@@ -86259,7 +86725,14 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
86259
86725
|
const existing = rules[parsed.pattern];
|
|
86260
86726
|
if (existing === void 0 || PERMISSION_ACTION_PRIORITY[action] > PERMISSION_ACTION_PRIORITY[existing]) rules[parsed.pattern] = action;
|
|
86261
86727
|
}
|
|
86262
|
-
|
|
86728
|
+
const codebuddy = liftCodebuddyOverride({
|
|
86729
|
+
settings,
|
|
86730
|
+
global: this.global
|
|
86731
|
+
});
|
|
86732
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
86733
|
+
permission,
|
|
86734
|
+
...Object.keys(codebuddy).length > 0 && { codebuddy }
|
|
86735
|
+
}, null, 2) });
|
|
86263
86736
|
}
|
|
86264
86737
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
86265
86738
|
return new CodebuddyPermissions({
|
|
@@ -89225,7 +89698,7 @@ const AUTO_APPROVE_KEYS = {
|
|
|
89225
89698
|
*
|
|
89226
89699
|
* @see https://code.visualstudio.com/docs/agents/reference/ai-settings
|
|
89227
89700
|
*/
|
|
89228
|
-
const DEFAULT_PERMISSION_MODE_KEY = "chat.permissions.default";
|
|
89701
|
+
const DEFAULT_PERMISSION_MODE_KEY$1 = "chat.permissions.default";
|
|
89229
89702
|
/**
|
|
89230
89703
|
* The prefix every Agent Host sandbox setting shares. A `copilot.sandbox` leaf
|
|
89231
89704
|
* at path `a.b` is written to `chat.agent.sandbox.a.b`.
|
|
@@ -89513,7 +89986,7 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
89513
89986
|
});
|
|
89514
89987
|
}
|
|
89515
89988
|
const defaultPermissionMode = config.copilot?.defaultPermissionMode;
|
|
89516
|
-
if (defaultPermissionMode !== void 0) patch[DEFAULT_PERMISSION_MODE_KEY] = defaultPermissionMode;
|
|
89989
|
+
if (defaultPermissionMode !== void 0) patch[DEFAULT_PERMISSION_MODE_KEY$1] = defaultPermissionMode;
|
|
89517
89990
|
const sandbox = config.copilot?.sandbox;
|
|
89518
89991
|
if (sandbox !== void 0) {
|
|
89519
89992
|
Object.assign(patch, buildSandboxPatch({
|
|
@@ -89569,7 +90042,7 @@ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
|
|
|
89569
90042
|
for (const [pattern, flag] of Object.entries(autoApprove)) rules[pattern] = flag ? "allow" : "deny";
|
|
89570
90043
|
if (Object.keys(rules).length > 0) permission[category] = rules;
|
|
89571
90044
|
}
|
|
89572
|
-
const defaultPermissionMode = settings[DEFAULT_PERMISSION_MODE_KEY];
|
|
90045
|
+
const defaultPermissionMode = settings[DEFAULT_PERMISSION_MODE_KEY$1];
|
|
89573
90046
|
const sandbox = readSandboxFromSettings(settings);
|
|
89574
90047
|
const copilot = {
|
|
89575
90048
|
...isCopilotDefaultPermissionMode(defaultPermissionMode) && { defaultPermissionMode },
|
|
@@ -89602,6 +90075,16 @@ const WEBFETCH_CATEGORY$1 = "webfetch";
|
|
|
89602
90075
|
/** `~/.copilot/settings.json` and `.github/copilot/settings.json` keys. */
|
|
89603
90076
|
const ALLOWED_URLS_KEY = "allowedUrls";
|
|
89604
90077
|
const DENIED_URLS_KEY = "deniedUrls";
|
|
90078
|
+
/**
|
|
90079
|
+
* `~/.copilot/settings.json` only: the startup permission mode for new
|
|
90080
|
+
* interactive sessions, authored through the `copilotcli.defaultPermissionMode`
|
|
90081
|
+
* override. It is a user setting — the repository settings file does not list
|
|
90082
|
+
* it among its repo-overridable keys.
|
|
90083
|
+
*/
|
|
90084
|
+
const DEFAULT_PERMISSION_MODE_KEY = "defaultPermissionMode";
|
|
90085
|
+
function isCopilotcliDefaultPermissionMode(value) {
|
|
90086
|
+
return COPILOTCLI_DEFAULT_PERMISSION_MODES.some((mode) => mode === value);
|
|
90087
|
+
}
|
|
89605
90088
|
function toUrlList(value) {
|
|
89606
90089
|
if (!Array.isArray(value)) return [];
|
|
89607
90090
|
return value.filter((entry) => typeof entry === "string");
|
|
@@ -89643,6 +90126,13 @@ function splitUrlRules(rules) {
|
|
|
89643
90126
|
* top-level keys in the user `settings.json`, so only documented keys are ever
|
|
89644
90127
|
* emitted there either.)
|
|
89645
90128
|
*
|
|
90129
|
+
* The `copilotcli.defaultPermissionMode` override (`manual` | `assisted` |
|
|
90130
|
+
* `allow-all`) writes the user-scope `defaultPermissionMode` key in global mode
|
|
90131
|
+
* only; in project mode it is skipped with a warning, since the repository
|
|
90132
|
+
* settings file does not accept it. It is patched only when authored, so a mode
|
|
90133
|
+
* the user picked through `/settings` otherwise survives. Writing `allow-all`
|
|
90134
|
+
* warns, because it makes new sessions skip every approval prompt.
|
|
90135
|
+
*
|
|
89646
90136
|
* @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
|
|
89647
90137
|
* @see https://github.com/github/copilot-cli/blob/main/changelog.md
|
|
89648
90138
|
*/
|
|
@@ -89685,7 +90175,8 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89685
90175
|
const paths = CopilotcliPermissions.getSettablePaths({ global });
|
|
89686
90176
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
89687
90177
|
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
89688
|
-
const
|
|
90178
|
+
const config = rulesyncPermissions.getJson();
|
|
90179
|
+
const rules = config.permission[WEBFETCH_CATEGORY$1];
|
|
89689
90180
|
const patch = {};
|
|
89690
90181
|
if (rules !== void 0) {
|
|
89691
90182
|
const { allowedUrls, deniedUrls } = splitUrlRules(rules);
|
|
@@ -89693,6 +90184,13 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89693
90184
|
if (global) patch[ALLOWED_URLS_KEY] = allowedUrls.length > 0 ? allowedUrls : void 0;
|
|
89694
90185
|
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.`);
|
|
89695
90186
|
}
|
|
90187
|
+
const defaultPermissionMode = config.copilotcli?.defaultPermissionMode;
|
|
90188
|
+
if (defaultPermissionMode !== void 0) {
|
|
90189
|
+
if (global) {
|
|
90190
|
+
patch[DEFAULT_PERMISSION_MODE_KEY] = defaultPermissionMode;
|
|
90191
|
+
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.`);
|
|
90192
|
+
} 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.`);
|
|
90193
|
+
}
|
|
89696
90194
|
return new CopilotcliPermissions({
|
|
89697
90195
|
outputRoot,
|
|
89698
90196
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -89714,7 +90212,11 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
89714
90212
|
for (const pattern of toUrlList(settings[DENIED_URLS_KEY])) rules[pattern] = "deny";
|
|
89715
90213
|
if (this.global) for (const pattern of toUrlList(settings[ALLOWED_URLS_KEY])) rules[pattern] ??= "allow";
|
|
89716
90214
|
const permission = Object.keys(rules).length > 0 ? { [WEBFETCH_CATEGORY$1]: rules } : {};
|
|
89717
|
-
|
|
90215
|
+
const defaultPermissionMode = this.global ? settings[DEFAULT_PERMISSION_MODE_KEY] : void 0;
|
|
90216
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
90217
|
+
permission,
|
|
90218
|
+
...isCopilotcliDefaultPermissionMode(defaultPermissionMode) && { copilotcli: { defaultPermissionMode } }
|
|
90219
|
+
}, null, 2) });
|
|
89718
90220
|
}
|
|
89719
90221
|
parseSettings() {
|
|
89720
90222
|
const relativePath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
@@ -95219,6 +95721,7 @@ var MimocodePermissions = class extends OpencodePermissions {
|
|
|
95219
95721
|
// src/features/permissions/omp-permissions.ts
|
|
95220
95722
|
const CATCH_ALL_PATTERN$5 = "*";
|
|
95221
95723
|
const APPROVAL_MODE_KEY = "approvalMode";
|
|
95724
|
+
const RESTRICTED_FOLDERS_KEY = "restrictedFolders";
|
|
95222
95725
|
const ACTION_TO_OMP_APPROVAL = {
|
|
95223
95726
|
allow: "allow",
|
|
95224
95727
|
ask: "prompt",
|
|
@@ -95257,9 +95760,9 @@ const MANAGED_OMP_TOOLS = /* @__PURE__ */ new Set([
|
|
|
95257
95760
|
*
|
|
95258
95761
|
* - `tools.approval.<tool>: allow | prompt | deny` is a per-tool policy honored
|
|
95259
95762
|
* in every approval mode. A category's catch-all `*` rule maps here
|
|
95260
|
-
* (`ask` → `prompt`).
|
|
95261
|
-
* with pattern-specific rules collapses to one action (deny > ask >
|
|
95262
|
-
* an implicit `ask` when there is no catch-all) and a warning.
|
|
95763
|
+
* (`ask` → `prompt`). `tools.approval` has no per-path matcher, so a non-bash
|
|
95764
|
+
* category with pattern-specific rules collapses to one action (deny > ask >
|
|
95765
|
+
* allow, with an implicit `ask` when there is no catch-all) and a warning.
|
|
95263
95766
|
* - `bash.patterns: [{ match, approval }]` is an ordered, first-match-wins list
|
|
95264
95767
|
* of command globs (only `*` is a wildcard). Every `bash` rule except a
|
|
95265
95768
|
* catch-all `allow` is written there, ordered deny → prompt → allow so that
|
|
@@ -95276,12 +95779,15 @@ const MANAGED_OMP_TOOLS = /* @__PURE__ */ new Set([
|
|
|
95276
95779
|
*
|
|
95277
95780
|
* The `omp.approvalMode` override writes `tools.approvalMode` (`always-ask`,
|
|
95278
95781
|
* `always-ask-cached`, `write` or `yolo`); without it the key is left as it is in the file, and on
|
|
95279
|
-
* import a recognized value is read back into the override.
|
|
95782
|
+
* import a recognized value is read back into the override. Likewise the
|
|
95783
|
+
* `omp.restrictedFolders` override writes `tools.restrictedFolders` (folders
|
|
95784
|
+
* where every action prompts in every approval mode, `yolo` included), is left
|
|
95785
|
+
* alone when absent, and is read back on import.
|
|
95280
95786
|
*
|
|
95281
95787
|
* `config.yml` carries every other oh-my-pi setting, so only `tools.approval`
|
|
95282
95788
|
* (the keys rulesync manages), `bash.patterns` and an authored
|
|
95283
|
-
* `tools.approvalMode` are rewritten; sibling
|
|
95284
|
-
* kept, and the file is never deleted.
|
|
95789
|
+
* `tools.approvalMode` or `tools.restrictedFolders` are rewritten; sibling
|
|
95790
|
+
* keys such as `bash.enabled` are kept, and the file is never deleted.
|
|
95285
95791
|
*
|
|
95286
95792
|
* @see https://github.com/can1357/oh-my-pi/blob/main/docs/settings.md
|
|
95287
95793
|
* @see https://github.com/can1357/oh-my-pi/blob/main/docs/approval-mode.md
|
|
@@ -95330,6 +95836,7 @@ var OmpPermissions = class OmpPermissions extends ToolPermissions {
|
|
|
95330
95836
|
logger
|
|
95331
95837
|
});
|
|
95332
95838
|
const approvalMode = config.omp?.approvalMode;
|
|
95839
|
+
const restrictedFolders = config.omp?.restrictedFolders;
|
|
95333
95840
|
const existingTools = isRecord(existing.tools) ? existing.tools : {};
|
|
95334
95841
|
const nextApproval = {
|
|
95335
95842
|
...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)])) : {},
|
|
@@ -95337,6 +95844,7 @@ var OmpPermissions = class OmpPermissions extends ToolPermissions {
|
|
|
95337
95844
|
};
|
|
95338
95845
|
const toolsSiblings = withoutKey(existingTools, "approval");
|
|
95339
95846
|
if (approvalMode !== void 0) toolsSiblings[APPROVAL_MODE_KEY] = approvalMode;
|
|
95847
|
+
if (restrictedFolders !== void 0) toolsSiblings[RESTRICTED_FOLDERS_KEY] = restrictedFolders;
|
|
95340
95848
|
const nextTools = Object.keys(nextApproval).length > 0 ? {
|
|
95341
95849
|
...toolsSiblings,
|
|
95342
95850
|
approval: nextApproval
|
|
@@ -95587,11 +96095,26 @@ function convertOmpToRulesync(config) {
|
|
|
95587
96095
|
}
|
|
95588
96096
|
if (Object.keys(bashRules).length > 0) permission.bash = bashRules;
|
|
95589
96097
|
const approvalMode = tools[APPROVAL_MODE_KEY];
|
|
96098
|
+
const restrictedFolders = importRestrictedFolders(tools[RESTRICTED_FOLDERS_KEY]);
|
|
96099
|
+
const omp = {
|
|
96100
|
+
...isOmpApprovalMode(approvalMode) && { approvalMode },
|
|
96101
|
+
...restrictedFolders !== void 0 && { restrictedFolders }
|
|
96102
|
+
};
|
|
95590
96103
|
return {
|
|
95591
96104
|
permission,
|
|
95592
|
-
...
|
|
96105
|
+
...Object.keys(omp).length > 0 && { omp }
|
|
95593
96106
|
};
|
|
95594
96107
|
}
|
|
96108
|
+
/**
|
|
96109
|
+
* `tools.restrictedFolders` as the override list: its string entries, or
|
|
96110
|
+
* `undefined` when it is unset or no entry survives (so a list of non-strings
|
|
96111
|
+
* is not imported as `[]` and then rewritten as an empty list).
|
|
96112
|
+
*/
|
|
96113
|
+
function importRestrictedFolders(value) {
|
|
96114
|
+
if (!Array.isArray(value)) return;
|
|
96115
|
+
const folders = value.filter((entry) => typeof entry === "string");
|
|
96116
|
+
return folders.length === 0 && value.length > 0 ? void 0 : folders;
|
|
96117
|
+
}
|
|
95595
96118
|
function isOmpApprovalMode(value) {
|
|
95596
96119
|
return OMP_APPROVAL_MODES.some((mode) => mode === value);
|
|
95597
96120
|
}
|
|
@@ -100688,19 +101211,31 @@ const CANONICAL_TO_VIBE_TOOL_NAMES = {
|
|
|
100688
101211
|
};
|
|
100689
101212
|
/**
|
|
100690
101213
|
* The tables earlier rulesync versions wrote for canonical categories that
|
|
100691
|
-
* Vibe 3.x
|
|
100692
|
-
* effect")
|
|
100693
|
-
* `agents
|
|
101214
|
+
* Vibe 3.x does not read: `[tools.grep]` (Vibe warns that it "has no
|
|
101215
|
+
* effect"), `[tools.task]` for the subagent tool v3.0.0 renamed to
|
|
101216
|
+
* `agents.*`, and `[tools.external_directory]` — Vibe has no such tool, and
|
|
101217
|
+
* its unused-settings check skips unknown tool names as custom/MCP tools, so
|
|
101218
|
+
* that deny was silently inert. A regenerate that configures the category removes them — and
|
|
100694
101219
|
* the matching `disabled_tools` entry — instead of leaving an inert table.
|
|
100695
101220
|
* Import still reads them (see {@link VIBE_TO_CANONICAL_TOOL_NAMES}), so a
|
|
100696
101221
|
* Vibe 2.x config is lifted into the canonical categories rather than lost.
|
|
100697
101222
|
*/
|
|
100698
101223
|
const VIBE_RETIRED_TOOL_TABLES = {
|
|
100699
101224
|
grep: "grep",
|
|
100700
|
-
agent: "task"
|
|
101225
|
+
agent: "task",
|
|
101226
|
+
external_directory: "external_directory"
|
|
100701
101227
|
};
|
|
100702
101228
|
/** The canonical category Vibe's `enable_subagents` switch expresses. */
|
|
100703
101229
|
const VIBE_SUBAGENT_CATEGORY = "agent";
|
|
101230
|
+
/** The canonical category Vibe's top-level `outside_workdir` setting expresses. */
|
|
101231
|
+
const VIBE_OUTSIDE_WORKDIR_CATEGORY = "external_directory";
|
|
101232
|
+
/**
|
|
101233
|
+
* The values of Vibe's `outside_workdir` (v3.0.3+). There is no allow value:
|
|
101234
|
+
* access outside the working directory is granted per path through a tool's
|
|
101235
|
+
* `allowlist` (`vibe-path:...`) or `--add-dir`.
|
|
101236
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
101237
|
+
*/
|
|
101238
|
+
const VIBE_OUTSIDE_WORKDIR_VALUES = /* @__PURE__ */ new Set(["ask", "deny"]);
|
|
100704
101239
|
const VIBE_TO_CANONICAL_TOOL_NAMES = {
|
|
100705
101240
|
bash: "bash",
|
|
100706
101241
|
read_file: "read",
|
|
@@ -100776,7 +101311,8 @@ const CANONICAL_PERMISSION_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
100776
101311
|
"grep",
|
|
100777
101312
|
"glob",
|
|
100778
101313
|
"notebookedit",
|
|
100779
|
-
"agent"
|
|
101314
|
+
"agent",
|
|
101315
|
+
"external_directory"
|
|
100780
101316
|
]);
|
|
100781
101317
|
var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
100782
101318
|
toml;
|
|
@@ -100857,7 +101393,11 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
100857
101393
|
rules: permission[VIBE_SUBAGENT_CATEGORY],
|
|
100858
101394
|
logger
|
|
100859
101395
|
});
|
|
100860
|
-
const
|
|
101396
|
+
const outsideWorkdirPatch = resolveOutsideWorkdirPatch({
|
|
101397
|
+
rules: permission[VIBE_OUTSIDE_WORKDIR_CATEGORY],
|
|
101398
|
+
logger
|
|
101399
|
+
});
|
|
101400
|
+
const tableCategories = Object.entries(permission).filter(([category]) => category !== VIBE_SUBAGENT_CATEGORY && category !== VIBE_OUTSIDE_WORKDIR_CATEGORY);
|
|
100861
101401
|
for (const [category, rules] of tableCategories) {
|
|
100862
101402
|
const vibeToolNames = resolveNames(category);
|
|
100863
101403
|
if (vibeToolNames === void 0) {
|
|
@@ -100931,7 +101471,8 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
100931
101471
|
enabledTools
|
|
100932
101472
|
}),
|
|
100933
101473
|
disabled_tools: disabledTools.size > 0 ? [...disabledTools].toSorted() : void 0,
|
|
100934
|
-
...enableSubagentsPatch
|
|
101474
|
+
...enableSubagentsPatch,
|
|
101475
|
+
...outsideWorkdirPatch
|
|
100935
101476
|
},
|
|
100936
101477
|
filePath
|
|
100937
101478
|
}),
|
|
@@ -100975,6 +101516,8 @@ var VibePermissions = class VibePermissions extends ToolPermissions {
|
|
|
100975
101516
|
}
|
|
100976
101517
|
for (const category of silencedCategories) ensurePermission(permission, category)["*"] = "deny";
|
|
100977
101518
|
if (this.toml.enable_subagents === false) ensurePermission(permission, VIBE_SUBAGENT_CATEGORY)["*"] = "deny";
|
|
101519
|
+
const outsideWorkdir = this.toml.outside_workdir;
|
|
101520
|
+
if (typeof outsideWorkdir === "string" && VIBE_OUTSIDE_WORKDIR_VALUES.has(outsideWorkdir)) ensurePermission(permission, VIBE_OUTSIDE_WORKDIR_CATEGORY)["*"] = outsideWorkdir === "deny" ? "deny" : "ask";
|
|
100978
101521
|
warnOnUnresolvableDisabledTools({
|
|
100979
101522
|
entries: disabledToolEntries,
|
|
100980
101523
|
tools: this.toml.tools
|
|
@@ -101051,7 +101594,7 @@ function retireLegacyToolTables({ permission, tools, disabledTools, logger }) {
|
|
|
101051
101594
|
}
|
|
101052
101595
|
if (disabledTools.delete(legacyToolName)) removed.push(`disabled_tools entry '${legacyToolName}'`);
|
|
101053
101596
|
}
|
|
101054
|
-
if (removed.length > 0) logger?.warn(`Removed ${removed.join(", ")} from the Vibe config: Vibe 3.x
|
|
101597
|
+
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.`);
|
|
101055
101598
|
}
|
|
101056
101599
|
/**
|
|
101057
101600
|
* Report the rules of a category that has no Vibe tool table. `grep` gets its
|
|
@@ -101087,6 +101630,30 @@ function resolveEnableSubagentsPatch({ rules, logger }) {
|
|
|
101087
101630
|
return { enable_subagents: action === "deny" ? false : void 0 };
|
|
101088
101631
|
}
|
|
101089
101632
|
/**
|
|
101633
|
+
* Translate the canonical `external_directory` category into Vibe's top-level
|
|
101634
|
+
* `outside_workdir` setting (v3.0.3+), which decides whether a file or shell
|
|
101635
|
+
* tool call reaching outside the working directory prompts (`"ask"`, the
|
|
101636
|
+
* default) or is refused with a tool error (`"deny"`). A `*` deny or ask writes
|
|
101637
|
+
* that value; a `*` allow has no Vibe value, so it is warned about and the key
|
|
101638
|
+
* is removed, leaving Vibe's default prompt. Without a `*` rule the key is left
|
|
101639
|
+
* as the file has it. Pattern rules are skipped with a warning: path grants go
|
|
101640
|
+
* in a tool's `allowlist` (`vibe-path:...`) instead.
|
|
101641
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/README.md
|
|
101642
|
+
* @see https://github.com/mistralai/mistral-vibe/blob/v3.0.3/vibe/app_server/_unified_permissions.py
|
|
101643
|
+
*/
|
|
101644
|
+
function resolveOutsideWorkdirPatch({ rules, logger }) {
|
|
101645
|
+
if (rules === void 0) return {};
|
|
101646
|
+
const patternCount = Object.keys(rules).filter((pattern) => pattern !== "*").length;
|
|
101647
|
+
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.`);
|
|
101648
|
+
const action = rules["*"];
|
|
101649
|
+
if (action === void 0) return {};
|
|
101650
|
+
if (action === "allow") {
|
|
101651
|
+
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.`);
|
|
101652
|
+
return { outside_workdir: void 0 };
|
|
101653
|
+
}
|
|
101654
|
+
return { outside_workdir: action };
|
|
101655
|
+
}
|
|
101656
|
+
/**
|
|
101090
101657
|
* Apply the Vibe-scoped override's per-tool `sensitive_patterns` (patterns that
|
|
101091
101658
|
* escalate to ASK even when the base permission is ALWAYS). rulesync owns this
|
|
101092
101659
|
* list for any category the override names: a present list is set, an empty
|
|
@@ -106436,4 +107003,4 @@ async function importChecksCore(params) {
|
|
|
106436
107003
|
}
|
|
106437
107004
|
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 };
|
|
106438
107005
|
|
|
106439
|
-
//# sourceMappingURL=import-
|
|
107006
|
+
//# sourceMappingURL=import-DPXjeK8q.js.map
|