rulesync 16.25.0 → 16.26.1

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.
@@ -4705,9 +4705,14 @@ const KIMI_CODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CAN
4705
4705
  * ZCode's configuration-file hooks expose exactly seven PascalCase events, all
4706
4706
  * of which have a clean canonical equivalent: `SessionStart`, `PreToolUse`,
4707
4707
  * `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `Stop`, and
4708
- * `UserPromptSubmit` ← `beforeSubmitPrompt`. An optional matcher (a
4709
- * case-sensitive regular expression) is honored on all of them except
4710
- * `UserPromptSubmit` and `Stop`, which expose no value to match against.
4708
+ * `UserPromptSubmit` ← `beforeSubmitPrompt`. An optional matcher is honored on
4709
+ * all of them except `UserPromptSubmit` and `Stop`, which expose no value to
4710
+ * match against. ZCode reads the matcher in one of two ways: a value made up
4711
+ * solely of letters, digits, underscores and `|` is an exact name list
4712
+ * (`Write|Edit` matches those two tool names, not the regex alternation), and
4713
+ * any other character makes the whole value a case-sensitive JavaScript regex.
4714
+ * Rulesync passes matchers through verbatim, so an authored value keeps
4715
+ * whichever reading ZCode gives it — worth knowing before escaping one.
4711
4716
  * Configuration hooks are read only from the user config
4712
4717
  * `~/.zcode/cli/config.json` (workspace config hooks are never executed) and
4713
4718
  * additionally require `hooks.enabled: true` to run.
@@ -6685,7 +6690,7 @@ const CursorPermissionsOverrideSchema = z.looseObject({
6685
6690
  * Tool-scoped override block for Qwen Code. Qwen's `settings.json` exposes
6686
6691
  * autonomy/sandbox controls with no canonical permission category — under
6687
6692
  * `tools` (`approvalMode` = plan/default/auto-edit/auto/yolo, `autoAccept`,
6688
- * `sandbox`, `sandboxImage`, `disabled`, `visible`, `listDirectory`,
6693
+ * `sandbox`, `sandboxImage`, `disabled`, `visible`, `eager`, `listDirectory`,
6689
6694
  * `workflowsEnabled`) and `security` (`folderTrust`, `allowedHttpHookUrls`,
6690
6695
  * `allowPrivateNetworkHooks`, `allowedInsecureVoiceBaseUrls`). Qwen Code strips
6691
6696
  * `tools.workflowsEnabled`, `security.allowPrivateNetworkHooks` and
@@ -6699,7 +6704,9 @@ const CursorPermissionsOverrideSchema = z.looseObject({
6699
6704
  * changes what the file said is reported in either scope, naming what that key
6700
6705
  * decides there — the autonomy and containment
6701
6706
  * controls (`approvalMode`, `autoAccept`, `sandbox`, `sandboxImage`), the
6702
- * registry controls (`disabled`, `visible`, `listDirectory`), the Auto Mode
6707
+ * registry controls (`disabled`, `visible`, `listDirectory`, and `eager`, which
6708
+ * demotes an omitted tool to deferred rather than removing it, replacing rather
6709
+ * than merging with the list a higher scope set), the Auto Mode
6703
6710
  * classifier config, and, because these groups are loose objects, any key
6704
6711
  * rulesync does not model. Import flags a scope-dependent key only when it read
6705
6712
  * the project file, since that is the value a `--global` regenerate would
@@ -6879,12 +6886,37 @@ const DeepagentsStartupOverrideSchema = z.looseObject({
6879
6886
  read_project_dotenv: z.optional(z.boolean())
6880
6887
  });
6881
6888
  /**
6889
+ * deepagents-cli's Python extension gate under `[extensions]` in
6890
+ * `~/.deepagents/config.toml`. `discover_extensions` auto-loads `*.py` from the
6891
+ * user's `~/.deepagents/extensions/` and from the checked-out project's
6892
+ * `<root>/.deepagents/extensions/`, so these two keys decide whether
6893
+ * repository-authored Python is imported into the agent process at all —
6894
+ * the same class of knob as `[startup].mode`, and one that ships defaulting to
6895
+ * a prompt a user can accept. Loose so a key added upstream passes through
6896
+ * verbatim.
6897
+ *
6898
+ * `extensions.extra_paths` is deliberately absent: it names machine-local files
6899
+ * and directories, which do not belong in a committed
6900
+ * `.rulesync/permissions.jsonc`, and it only ever widens what loads.
6901
+ *
6902
+ * @see https://docs.langchain.com/oss/deepagents/code/configuration
6903
+ */
6904
+ const DeepagentsExtensionsOverrideSchema = z.looseObject({
6905
+ enabled: z.optional(z.boolean()),
6906
+ trust: z.optional(z.enum([
6907
+ "ask",
6908
+ "always",
6909
+ "never"
6910
+ ]))
6911
+ });
6912
+ /**
6882
6913
  * The `[shell].allow_list` array itself is rulesync-owned, driven by the
6883
6914
  * shared `permission.bash` block, so it has no key here.
6884
6915
  */
6885
6916
  const DeepagentsPermissionsOverrideSchema = z.looseObject({
6886
6917
  permission: z.optional(ToolScopedPermissionSchema),
6887
- startup: z.optional(DeepagentsStartupOverrideSchema)
6918
+ startup: z.optional(DeepagentsStartupOverrideSchema),
6919
+ extensions: z.optional(DeepagentsExtensionsOverrideSchema)
6888
6920
  });
6889
6921
  /**
6890
6922
  * The actions Junie's allowlist accepts. Verified against the shipped Junie
@@ -7895,8 +7927,9 @@ const RulesyncRuleFrontmatterSchema = z.object({
7895
7927
  agentsmd: z.optional(z.looseObject({ subprojectPath: z.optional(z.string()) })),
7896
7928
  claudecode: z.optional(z.looseObject({ paths: z.optional(z.array(z.string())) })),
7897
7929
  codebuddy: z.optional(z.looseObject({
7898
- paths: z.optional(z.array(z.string())),
7930
+ paths: z.optional(z.union([z.string(), z.array(z.string())])),
7899
7931
  alwaysApply: z.optional(z.boolean()),
7932
+ enabled: z.optional(z.boolean()),
7900
7933
  description: z.optional(z.string())
7901
7934
  })),
7902
7935
  cursor: z.optional(z.looseObject({
@@ -9189,6 +9222,11 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
9189
9222
  license: z.optional(z.string()),
9190
9223
  compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
9191
9224
  metadata: z.optional(z.looseObject({}))
9225
+ })),
9226
+ zcode: z.optional(z.looseObject({
9227
+ when_to_use: z.optional(z.string()),
9228
+ license: z.optional(z.string()),
9229
+ metadata: z.optional(z.looseObject({}))
9192
9230
  }))
9193
9231
  });
9194
9232
  /**
@@ -17771,6 +17809,20 @@ const ROO_MCP_FILE_NAME = "mcp.json";
17771
17809
  */
17772
17810
  const ROO_MODE_SLUG_PATTERN = /^[a-zA-Z0-9-]+$/;
17773
17811
  /**
17812
+ * `~/.roo/rules/AGENTS.md` — where the root rule goes in global scope.
17813
+ *
17814
+ * Roo/Zoo Code discover the agent-rules files (`AGENTS.md`, `AGENT.md`,
17815
+ * `AGENTS.local.md`) in the **workspace only**: `loadAllAgentRulesFiles` reads
17816
+ * `cwd`, plus subdirectories holding a `.roo` folder when `enableSubfolderRules`
17817
+ * is on. There is no home-directory branch, so a `~/AGENTS.md` is never read.
17818
+ * The global rules directory `~/.roo/rules/` *is* loaded, so the root rule is
17819
+ * written there alongside the non-root files rather than to an unread path.
17820
+ * The basename is kept so the file still reads as the root overview.
17821
+ * @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/core/prompts/sections/custom-instructions.ts
17822
+ * @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/services/roo-config/index.ts
17823
+ */
17824
+ const ROO_GLOBAL_ROOT_RULE_FILE_NAME = "AGENTS.md";
17825
+ /**
17774
17826
  * `.roo/rules-{mode}/` — the mode-specific rule directory Roo/Zoo Code load
17775
17827
  * INSTEAD of `.roo/rules/` while that mode is active. The relative path is the
17776
17828
  * same in global scope, where it resolves under `~/.roo/`.
@@ -22830,7 +22882,7 @@ var DevinHooks = class DevinHooks extends ToolHooks {
22830
22882
  * adapter so the settings reader in `src/utils/` can name the same list without
22831
22883
  * importing a feature.
22832
22884
  *
22833
- * @see https://docs.factory.ai/cli/configuration/settings
22885
+ * @see https://docs.factory.ai/droid-cli/settings
22834
22886
  */
22835
22887
  const FACTORYDROID_OVERRIDE_KEYS = [
22836
22888
  "commandBlocklist",
@@ -22838,6 +22890,8 @@ const FACTORYDROID_OVERRIDE_KEYS = [
22838
22890
  "sandbox",
22839
22891
  "mcpPolicy",
22840
22892
  "mcpAutonomyOverrides",
22893
+ "mcpAutonomyUrlOverrides",
22894
+ "builtInToolAutonomyOverrides",
22841
22895
  "enableDroidShield",
22842
22896
  "sessionDefaultSettings",
22843
22897
  "maxAutonomyLevel",
@@ -22845,7 +22899,10 @@ const FACTORYDROID_OVERRIDE_KEYS = [
22845
22899
  "interactionMode",
22846
22900
  "extraKnownMarketplaces",
22847
22901
  "enabledPlugins",
22902
+ "strictEnabledPlugins",
22903
+ "strictKnownMarketplaces",
22848
22904
  "hooksDisabled",
22905
+ "allowManagedHooksOnly",
22849
22906
  "disabledSkills",
22850
22907
  "modelPolicy",
22851
22908
  "missionPolicy"
@@ -26886,6 +26943,7 @@ var ClineIgnore = class ClineIgnore extends ToolIgnore {
26886
26943
  //#region src/constants/crush-paths.ts
26887
26944
  const CRUSH_RULE_FILE_NAME = "CRUSH.md";
26888
26945
  const CRUSH_GLOBAL_DIR = join(".config", "crush");
26946
+ const CRUSH_LOCAL_RULE_FILE_NAME = "CRUSH.local.md";
26889
26947
  const CRUSH_IGNORE_FILE_NAME = ".crushignore";
26890
26948
  const CRUSH_SKILLS_PROJECT_DIR = join(".crush", "skills");
26891
26949
  const CRUSH_SKILLS_GLOBAL_DIR = join(CRUSH_GLOBAL_DIR, "skills");
@@ -32799,7 +32857,9 @@ const REASONIX_PLUGIN_FIELDS = [
32799
32857
  "headers",
32800
32858
  "startup_timeout_seconds",
32801
32859
  "call_timeout_seconds",
32802
- "tool_timeout_seconds"
32860
+ "tool_timeout_seconds",
32861
+ "concurrency",
32862
+ "auto_start"
32803
32863
  ];
32804
32864
  var ReasonixMcp = class ReasonixMcp extends ToolMcp {
32805
32865
  toml;
@@ -33106,9 +33166,16 @@ function lookupTransport(map, key) {
33106
33166
  }
33107
33167
  /**
33108
33168
  * Read the "put this server's own instructions into the agent's prompt" flag
33109
- * off an unfiltered canonical entry, under either spelling. Anything other
33110
- * than a literal `true` reads as not enabled, matching Rovo Dev, where the key
33111
- * is absent by default.
33169
+ * off an unfiltered canonical entry, under either spelling. `undefined` means
33170
+ * the entry says nothing, which is Rovo Dev's own default.
33171
+ *
33172
+ * The flag is a **tri-state**, not a switch that only exists when on. Atlassian
33173
+ * inverted the default on 2026-09-02: a server's instructions are now surfaced
33174
+ * when the key is absent or `true`, and `false` is what suppresses them — the
33175
+ * opposite of the opt-in wording this adapter was first written against, where
33176
+ * absent and `false` meant the same thing. So `false` has to travel end to end;
33177
+ * collapsing it away is what makes a suppression impossible to author, and
33178
+ * erases one on import.
33112
33179
  *
33113
33180
  * The canonical key decides whenever it is present, the way codex resolves the
33114
33181
  * same two-spelling conflict for `experimental_environment`. OR-ing the two
@@ -33116,28 +33183,25 @@ function lookupTransport(map, key) {
33116
33183
  * `enable_instructions: true` copied out of Atlassian's docs — fail-open, on
33117
33184
  * the one key whose whole purpose is a trust decision.
33118
33185
  *
33186
+ * A non-boolean under either spelling reads as `undefined` rather than as a
33187
+ * suppression: Rovo Dev surfaces the instructions for anything that is not
33188
+ * `false`, so writing `false` there would invent a restriction the tool is not
33189
+ * applying. (The canonical key is a strict boolean in the schema, so only the
33190
+ * raw spelling can carry one.)
33191
+ *
33119
33192
  * `isPlainObject` rather than `isRecord`: this walks a user-supplied key set,
33120
33193
  * so a `constructor` entry must not resolve up the prototype chain.
33121
33194
  */
33122
33195
  function readEnableInstructions(rawServer) {
33123
- if (!isPlainObject$1(rawServer)) return false;
33124
- if (rawServer.rovodevEnableInstructions !== void 0) return rawServer.rovodevEnableInstructions === true;
33125
- return rawServer.enable_instructions === true;
33126
- }
33127
- /**
33128
- * Names that were emitted with `enable_instructions: true` during one
33129
- * `fromRulesyncMcp` call, so the run can say so once rather than per server.
33130
- * Atlassian gates this key on trust, and it is the only thing generate writes
33131
- * that widens what steers the model — the quietest possible write is the wrong
33132
- * one for it.
33133
- */
33134
- function warnEnabledInstructions(names, logger) {
33135
- if (names.length === 0) return;
33136
- logger?.warn(`Rovo Dev MCP: writing enable_instructions: true for ${names.join(", ")}. Rovo Dev pastes ${names.length === 1 ? "that server's" : "those servers'"} own instructions into the agent's system prompt, so enable it only for servers you trust.`);
33196
+ if (!isPlainObject$1(rawServer)) return;
33197
+ const canonical = rawServer.rovodevEnableInstructions;
33198
+ if (canonical !== void 0) return typeof canonical === "boolean" ? canonical : void 0;
33199
+ const raw = rawServer.enable_instructions;
33200
+ return typeof raw === "boolean" ? raw : void 0;
33137
33201
  }
33138
33202
  function toRovodevServer(name, server, logger) {
33139
33203
  const { type, transport, disabled: _disabled, rovodevEnableInstructions, ...rest } = server;
33140
- if (rovodevEnableInstructions === true) rest.enable_instructions = true;
33204
+ if (typeof rovodevEnableInstructions === "boolean") rest.enable_instructions = rovodevEnableInstructions;
33141
33205
  const declared = typeof transport === "string" ? transport : typeof type === "string" ? type : void 0;
33142
33206
  if (declared === void 0) return rest;
33143
33207
  const mapped = lookupTransport(CANONICAL_TO_ROVODEV_TRANSPORT, declared);
@@ -33152,7 +33216,7 @@ function toRovodevServer(name, server, logger) {
33152
33216
  }
33153
33217
  function fromRovodevServer(server) {
33154
33218
  const { transport, enable_instructions: enableInstructions, ...rest } = server;
33155
- if (enableInstructions === true) rest.rovodevEnableInstructions = true;
33219
+ if (typeof enableInstructions === "boolean") rest.rovodevEnableInstructions = enableInstructions;
33156
33220
  if (typeof transport !== "string") return rest;
33157
33221
  const mapped = lookupTransport(ROVODEV_TO_CANONICAL_TRANSPORT, transport);
33158
33222
  return mapped === void 0 ? rest : {
@@ -33593,10 +33657,10 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
33593
33657
  canWriteDisableToggle = false;
33594
33658
  }
33595
33659
  const mcpServers = Object.fromEntries(Object.entries(rulesyncMcp.getMcpServers()).map(([name, server]) => {
33596
- const rawServer = rulesyncMcp.getRawMcpServer(name);
33660
+ const enableInstructions = readEnableInstructions(rulesyncMcp.getRawMcpServer(name));
33597
33661
  const record = {
33598
33662
  ...server,
33599
- ...readEnableInstructions(rawServer) && { rovodevEnableInstructions: true }
33663
+ ...enableInstructions !== void 0 && { rovodevEnableInstructions: enableInstructions }
33600
33664
  };
33601
33665
  if (record.disabled === true && !canWriteDisableToggle) {
33602
33666
  logger?.warn(`Rovo Dev MCP: skipping disabled server "${name}" because config.yml cannot be parsed, so mcp.disabledMcpServers cannot be written to switch it off.`);
@@ -33605,7 +33669,6 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
33605
33669
  const converted = toRovodevServer(name, record, logger);
33606
33670
  return converted === null ? null : [name, converted];
33607
33671
  }).filter((entry) => entry !== null));
33608
- warnEnabledInstructions(Object.entries(mcpServers).filter(([, server]) => server.enable_instructions === true).map(([name]) => name), logger);
33609
33672
  const rovodevConfig = {
33610
33673
  ...json,
33611
33674
  mcpServers
@@ -39749,6 +39812,34 @@ const DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS = {
39749
39812
  yolo_switcher: true,
39750
39813
  read_project_dotenv: true
39751
39814
  };
39815
+ /**
39816
+ * `[extensions]` gates dcode's Python extension system. `discover_extensions`
39817
+ * auto-loads `*.py` from the user's `~/.deepagents/extensions/` and from the
39818
+ * checked-out project's `<root>/.deepagents/extensions/`, so this table decides
39819
+ * whether a cloned repository's Python is imported into the agent process.
39820
+ */
39821
+ const EXTENSIONS_TABLE_KEY = "extensions";
39822
+ /**
39823
+ * Keys lifted back into the override on import. `extensions.extra_paths` is
39824
+ * deliberately absent: it names machine-local files and directories, which do
39825
+ * not belong in a committed `.rulesync/permissions.jsonc`, and it only ever
39826
+ * widens what loads.
39827
+ */
39828
+ const DEEPAGENTS_EXTENSIONS_KEYS = ["enabled", "trust"];
39829
+ /** `TrustPolicy` in `extensions/settings.py`; dcode reads nothing else. */
39830
+ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
39831
+ "ask",
39832
+ "always",
39833
+ "never"
39834
+ ];
39835
+ /**
39836
+ * `[extensions].enabled` defaults to `true` upstream, so writing `true` on a
39837
+ * machine that has not set it changes nothing — the same reasoning as
39838
+ * `DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS`.
39839
+ *
39840
+ * @see https://github.com/langchain-ai/deepagents `config_manifest.py`
39841
+ */
39842
+ const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
39752
39843
  const ALLOW_ALL_SENTINEL = "all";
39753
39844
  const RECOMMENDED_SENTINEL = "recommended";
39754
39845
  const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
@@ -39818,7 +39909,10 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
39818
39909
  * `DeepagentsPermissionsOverrideSchema`): `[startup].mode`
39819
39910
  * (`manual` / `auto` / `yolo`), `[startup].yolo_switcher` and
39820
39911
  * `[startup].read_project_dotenv` are merged into the file on generate and
39821
- * lifted back into the override on import.
39912
+ * lifted back into the override on import. The same override carries
39913
+ * `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
39914
+ * which decide whether the Python in a checked-out project's
39915
+ * `.deepagents/extensions/` is imported into the agent process.
39822
39916
  *
39823
39917
  * On **import** the allowlist comes back as `bash` `allow` rules named by the
39824
39918
  * executable — skipping any entry dcode could not match in the first place, so
@@ -39920,6 +40014,13 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
39920
40014
  filePath,
39921
40015
  logger
39922
40016
  });
40017
+ const extensionsOverride = config.deepagents?.extensions;
40018
+ if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
40019
+ settings,
40020
+ extensionsOverride,
40021
+ filePath,
40022
+ logger
40023
+ });
39923
40024
  return new DeepagentsPermissions({
39924
40025
  outputRoot,
39925
40026
  relativeDirPath: paths.relativeDirPath,
@@ -39944,8 +40045,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
39944
40045
  startup: isPlainObject$1(settings[STARTUP_TABLE_KEY]) ? settings[STARTUP_TABLE_KEY] : {},
39945
40046
  selfPath
39946
40047
  });
40048
+ const extensionsOverride = liftExtensionsOverride({
40049
+ extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
40050
+ selfPath
40051
+ });
39947
40052
  const result = { ...config };
39948
- if (Object.keys(startupOverride).length > 0) result.deepagents = { startup: startupOverride };
40053
+ const deepagents = {};
40054
+ if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
40055
+ if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
40056
+ if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
39949
40057
  return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
39950
40058
  }
39951
40059
  validate() {
@@ -40032,57 +40140,192 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
40032
40140
  if (willWrite && allowAll) warnWithFallback(logger, "The bash '*' allow rule became allow_list = [\"all\"] for deepagents-cli, which auto-approves every command and skips its dangerous-pattern check (command substitution, redirects, process substitution). List the executables you want instead if that is more than you meant.");
40033
40141
  }
40034
40142
  /**
40035
- * Lift the `[startup]` keys rulesync models back into the `deepagents`
40036
- * override, keeping only values dcode itself accepts.
40143
+ * Lift the keys rulesync models from one `config.toml` table back into the
40144
+ * `deepagents` override, keeping only values dcode itself accepts.
40145
+ *
40146
+ * A value outside what the option's own parser reads is `Invalid` upstream —
40147
+ * dcode ignores it and falls back to its default. Importing it anyway would
40148
+ * record a setting the tool is not applying and, because the canonical schema
40149
+ * is stricter than TOML, would write a `.rulesync/permissions.jsonc` the next
40150
+ * `rulesync generate` cannot even read.
40037
40151
  *
40038
- * A `mode` outside the three approval modes, or a non-boolean where a switch
40039
- * belongs, is `Invalid` upstream dcode ignores it and falls back to its
40040
- * default. Importing it anyway would record a setting the tool is not applying
40041
- * and, because the canonical schema is stricter than TOML, would write a
40042
- * `.rulesync/permissions.jsonc` the next `rulesync generate` cannot even read.
40152
+ * `normalize` returns the value to keep, or `null` for one dcode would not
40153
+ * read a wrapper rather than the bare value, so a legitimately falsy setting
40154
+ * is not mistaken for a rejection.
40043
40155
  */
40044
- function liftStartupOverride({ startup, selfPath }) {
40045
- const startupOverride = {};
40156
+ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
40157
+ const override = {};
40046
40158
  const rejected = [];
40047
- for (const key of DEEPAGENTS_STARTUP_KEYS) {
40048
- const value = startup[key];
40159
+ for (const key of keys) {
40160
+ const value = table[key];
40049
40161
  if (value === void 0) continue;
40050
- if (key === "mode" ? DEEPAGENTS_STARTUP_MODES.includes(value) : typeof value === "boolean") startupOverride[key] = value;
40162
+ const normalized = normalize({
40163
+ key,
40164
+ value
40165
+ });
40166
+ if (normalized) override[key] = normalized.value;
40051
40167
  else rejected.push(`${key} = ${JSON.stringify(value)}`);
40052
40168
  }
40053
- if (rejected.length > 0) warnWithFallback(void 0, `deepagents-cli falls back to its own default for a '[${STARTUP_TABLE_KEY}]' value it cannot read, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
40054
- return startupOverride;
40169
+ if (rejected.length > 0) warnWithFallback(void 0, `deepagents-cli falls back to its own default for a '[${tableKey}]' value it cannot read, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
40170
+ return override;
40055
40171
  }
40056
40172
  /**
40057
- * Merge the `deepagents.startup` override into `[startup]`, preserving every
40058
- * other key of that table. A `startup` that is not a table at all is something
40059
- * rulesync did not write and cannot merge into, so it is left exactly as the
40060
- * user has it rather than replaced.
40173
+ * Lift the `[startup]` keys rulesync models back into the `deepagents`
40174
+ * override. A `mode` outside the three approval modes, or a non-boolean where
40175
+ * a switch belongs, is one dcode ignores.
40061
40176
  */
40062
- function mergeStartupOverride({ settings, startupOverride, filePath, logger }) {
40063
- const existingStartup = settings[STARTUP_TABLE_KEY];
40064
- if (existingStartup !== void 0 && !isPlainObject$1(existingStartup)) {
40065
- warnWithFallback(logger, `deepagents-cli: '${STARTUP_TABLE_KEY}' in ${filePath} is not a table, so the deepagents startup override was skipped rather than overwriting it.`);
40177
+ function liftStartupOverride({ startup, selfPath }) {
40178
+ return liftOverrideTable({
40179
+ table: startup,
40180
+ tableKey: STARTUP_TABLE_KEY,
40181
+ keys: DEEPAGENTS_STARTUP_KEYS,
40182
+ normalize: ({ key, value }) => {
40183
+ if (key === "mode") return DEEPAGENTS_STARTUP_MODES.includes(value) ? { value } : null;
40184
+ return typeof value === "boolean" ? { value } : null;
40185
+ },
40186
+ selfPath
40187
+ });
40188
+ }
40189
+ /**
40190
+ * Lift the `[extensions]` keys rulesync models back into the `deepagents`
40191
+ * override.
40192
+ *
40193
+ * `trust` is read the way `parse_trust_policy` reads it — trimmed and
40194
+ * lowercased — so `"Always"` comes back as the policy dcode is actually
40195
+ * applying rather than being dropped, the same courtesy the allowlist
40196
+ * sentinels already get. The canonical enum holds only the lowercase
40197
+ * spellings, so keeping the value verbatim would write a permissions file the
40198
+ * next generate could not parse.
40199
+ */
40200
+ function liftExtensionsOverride({ extensions, selfPath }) {
40201
+ return liftOverrideTable({
40202
+ table: extensions,
40203
+ tableKey: EXTENSIONS_TABLE_KEY,
40204
+ keys: DEEPAGENTS_EXTENSIONS_KEYS,
40205
+ normalize: ({ key, value }) => {
40206
+ if (key === "trust") {
40207
+ if (typeof value !== "string") return null;
40208
+ const policy = value.trim().toLowerCase();
40209
+ return DEEPAGENTS_EXTENSION_TRUST_POLICIES.includes(policy) ? { value: policy } : null;
40210
+ }
40211
+ return typeof value === "boolean" ? { value } : null;
40212
+ },
40213
+ selfPath
40214
+ });
40215
+ }
40216
+ /**
40217
+ * Merge one block of the `deepagents` override into its `config.toml` table,
40218
+ * preserving every other key of that table. A table that is not a table at all
40219
+ * is something rulesync did not write and cannot merge into, so it is left
40220
+ * exactly as the user has it rather than replaced.
40221
+ */
40222
+ function mergeOverrideTable({ settings, tableKey, override, knownKeys, isDroppedKey, warnAboutRelaxations, filePath, logger }) {
40223
+ const existing = settings[tableKey];
40224
+ if (existing !== void 0 && !isPlainObject$1(existing)) {
40225
+ warnWithFallback(logger, `deepagents-cli: '${tableKey}' in ${filePath} is not a table, so the deepagents ${tableKey} override was skipped rather than overwriting it.`);
40066
40226
  return;
40067
40227
  }
40068
- const startup = isPlainObject$1(existingStartup) ? { ...existingStartup } : {};
40069
- const previousStartup = { ...startup };
40228
+ const table = isPlainObject$1(existing) ? { ...existing } : {};
40229
+ const previous = { ...table };
40070
40230
  const writtenKeys = [];
40071
- for (const [key, value] of Object.entries(startupOverride)) {
40231
+ for (const [key, value] of Object.entries(override)) {
40072
40232
  if (isPrototypePollutionKey(key)) continue;
40073
- if (key === STARTUP_RECENT_KEY) continue;
40233
+ if (isDroppedKey?.(key)) continue;
40074
40234
  if (value === null || value === void 0) continue;
40075
- startup[key] = value;
40235
+ table[key] = value;
40076
40236
  writtenKeys.push(key);
40077
40237
  }
40078
- warnAboutStartupRelaxations({
40079
- startupOverride,
40080
- previousStartup,
40238
+ warnAboutRelaxations({
40239
+ override,
40240
+ previous,
40241
+ filePath,
40242
+ logger
40243
+ });
40244
+ warnAboutUncheckedKeys({
40245
+ tableKey,
40246
+ knownKeys,
40081
40247
  writtenKeys,
40082
40248
  filePath,
40083
40249
  logger
40084
40250
  });
40085
- if (Object.keys(startup).length > 0) settings[STARTUP_TABLE_KEY] = startup;
40251
+ if (Object.keys(table).length > 0) settings[tableKey] = table;
40252
+ }
40253
+ /**
40254
+ * Name the keys the merge wrote that rulesync does not model. The override is a
40255
+ * loose object so a key added upstream still reaches the config, but this one
40256
+ * writes into the machine's global file: what rulesync cannot judge, it at
40257
+ * least names.
40258
+ */
40259
+ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, logger }) {
40260
+ const known = new Set(knownKeys);
40261
+ const unknownKeys = writtenKeys.filter((key) => !known.has(key));
40262
+ if (unknownKeys.length === 0) return;
40263
+ warnWithFallback(logger, `The deepagents ${tableKey} override wrote ${unknownKeys.join(", ")} into ${filePath} unchecked — rulesync does not know what those keys grant, and that file is your global deepagents-cli config.`);
40264
+ }
40265
+ /**
40266
+ * Merge the `deepagents.startup` override into `[startup]`.
40267
+ */
40268
+ function mergeStartupOverride({ settings, startupOverride, filePath, logger }) {
40269
+ mergeOverrideTable({
40270
+ settings,
40271
+ tableKey: STARTUP_TABLE_KEY,
40272
+ override: startupOverride,
40273
+ knownKeys: DEEPAGENTS_STARTUP_KEYS,
40274
+ isDroppedKey: (key) => key === STARTUP_RECENT_KEY,
40275
+ warnAboutRelaxations: warnAboutStartupRelaxations,
40276
+ filePath,
40277
+ logger
40278
+ });
40279
+ }
40280
+ /**
40281
+ * Merge the `deepagents.extensions` override into `[extensions]`. Nothing is
40282
+ * dropped here — `extra_paths` is not modeled, but it is a key the user may
40283
+ * legitimately want carried, so it passes through as any other unknown key
40284
+ * does, named by the unchecked-key warning.
40285
+ */
40286
+ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logger }) {
40287
+ mergeOverrideTable({
40288
+ settings,
40289
+ tableKey: EXTENSIONS_TABLE_KEY,
40290
+ override: extensionsOverride,
40291
+ knownKeys: DEEPAGENTS_EXTENSIONS_KEYS,
40292
+ warnAboutRelaxations: warnAboutExtensionsRelaxations,
40293
+ filePath,
40294
+ logger
40295
+ });
40296
+ }
40297
+ /**
40298
+ * Name the value an override key replaces, so a setting the user had turned
40299
+ * down is visible as such rather than reported as a bare new value.
40300
+ */
40301
+ function describeOverriddenValue({ key, value, previous }) {
40302
+ const previousValue = previous[key];
40303
+ return previousValue === void 0 || previousValue === value ? `${key} = ${JSON.stringify(value)}` : `${key} = ${JSON.stringify(value)} (was ${JSON.stringify(previousValue)})`;
40304
+ }
40305
+ /**
40306
+ * Warn when the `deepagents` extensions override widens what dcode loads on its
40307
+ * own, for the same reason the startup one does: this block is written into the
40308
+ * user's **global** config from a `.rulesync/permissions.jsonc` a repository
40309
+ * can carry.
40310
+ *
40311
+ * `trust` decides what happens to the Python in a checked-out project's
40312
+ * `.deepagents/extensions/` — `always` imports it into the agent process with
40313
+ * no prompt at all — and `enabled` is the switch above it. `never` and `false`
40314
+ * restrict, so neither is reported.
40315
+ */
40316
+ function warnAboutExtensionsRelaxations({ override, previous, filePath, logger }) {
40317
+ const relaxations = [];
40318
+ if (override.trust === "always") relaxations.push(describeOverriddenValue({
40319
+ key: "trust",
40320
+ value: "always",
40321
+ previous
40322
+ }));
40323
+ if (override.enabled === true && (previous.enabled ?? DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT) !== true) relaxations.push(describeOverriddenValue({
40324
+ key: "enabled",
40325
+ value: true,
40326
+ previous
40327
+ }));
40328
+ if (relaxations.length > 0) warnWithFallback(logger, `The deepagents extensions override wrote ${relaxations.join(", ")} into ${filePath}, which is your global deepagents-cli config: it decides whether the Python in a checked-out project's .deepagents/extensions/ is imported into dcode, for every project on this machine, not just this one.`);
40086
40329
  }
40087
40330
  /**
40088
40331
  * Warn when the `deepagents` startup override relaxes what dcode may do on its
@@ -40097,12 +40340,13 @@ function mergeStartupOverride({ settings, startupOverride, filePath, logger }) {
40097
40340
  * process environment. The value being replaced is named alongside, so a
40098
40341
  * setting the user had turned down is visible as such.
40099
40342
  */
40100
- function warnAboutStartupRelaxations({ startupOverride, previousStartup, writtenKeys, filePath, logger }) {
40343
+ function warnAboutStartupRelaxations({ override: startupOverride, previous: previousStartup, filePath, logger }) {
40101
40344
  const relaxations = [];
40102
- const describe = (key, value) => {
40103
- const previous = previousStartup[key];
40104
- return previous === void 0 || previous === value ? `${key} = ${JSON.stringify(value)}` : `${key} = ${JSON.stringify(value)} (was ${JSON.stringify(previous)})`;
40105
- };
40345
+ const describe = (key, value) => describeOverriddenValue({
40346
+ key,
40347
+ value,
40348
+ previous: previousStartup
40349
+ });
40106
40350
  const mode = startupOverride.mode;
40107
40351
  if (mode === "auto" || mode === "yolo") relaxations.push(describe("mode", mode));
40108
40352
  for (const [key, upstreamDefault] of Object.entries(DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS)) {
@@ -40112,9 +40356,6 @@ function warnAboutStartupRelaxations({ startupOverride, previousStartup, written
40112
40356
  }
40113
40357
  if (relaxations.length > 0) warnWithFallback(logger, `The deepagents startup override wrote ${relaxations.join(", ")} into ${filePath}, which is your global deepagents-cli config: it relaxes how much dcode does without asking, for every project on this machine, not just this one.`);
40114
40358
  if (startupOverride[STARTUP_RECENT_KEY] !== void 0) warnWithFallback(logger, `The deepagents startup override's '${STARTUP_RECENT_KEY}' was not written to ${filePath}: dcode manages that key itself, and with no explicit 'mode' beside it, it is what restores auto-approval at launch. Set 'mode' if switching approval modes is the intent.`);
40115
- const known = new Set(DEEPAGENTS_STARTUP_KEYS);
40116
- const unknownKeys = writtenKeys.filter((key) => !known.has(key));
40117
- if (unknownKeys.length > 0) warnWithFallback(logger, `The deepagents startup override wrote ${unknownKeys.join(", ")} into ${filePath} unchecked — rulesync does not know what those keys grant, and that file is your global deepagents-cli config.`);
40118
40359
  }
40119
40360
  /**
40120
40361
  * Read `[shell].allow_list` the way dcode does: a TOML array is taken element
@@ -43224,6 +43465,7 @@ const QWEN_OVERRIDE_TOOLS_KEYS = [
43224
43465
  "sandboxImage",
43225
43466
  "disabled",
43226
43467
  "visible",
43468
+ "eager",
43227
43469
  "listDirectory",
43228
43470
  "workflowsEnabled"
43229
43471
  ];
@@ -43321,6 +43563,11 @@ const QWEN_OVERRIDE_GROUPS = [{
43321
43563
  projectNote: ({ qualifiedKey, quotedValue, filePath }) => `${qualifiedKey} = ${quotedValue} was written to the project-scoped ${filePath}, so it decides which deferred tools are visible at startup in this repository.`,
43322
43564
  globalNote: "Qwen Code honors this key wherever it is written, so in the global scope this decides which deferred tools are visible at startup for every project on this machine."
43323
43565
  },
43566
+ eager: {
43567
+ rule: "global-machine-wide",
43568
+ projectNote: ({ qualifiedKey, quotedValue, filePath }) => `${qualifiedKey} = ${quotedValue} was written to the project-scoped ${filePath}, replacing the list in that file rather than adding to it, so in this repository every non-exempt eager-by-default tool it does not name is demoted to deferred — still callable, but only after \`tool_search\` loads it, and out of reach for the session if \`tool_search\` is not registered. A name added back is a tool whose schema is in the initial request again.`,
43569
+ globalNote: "Qwen Code honors this key wherever it is written, and the override replaces the list in this file rather than adding to it, so in the global scope it decides which eager-by-default tools keep their schemas in the initial request for every project on this machine — the ones it does not name are demoted to deferred, reachable only through `tool_search`, and out of reach entirely for a session where `tool_search` is not registered, while a name it adds back is a tool declared up front again."
43570
+ },
43324
43571
  listDirectory: {
43325
43572
  rule: "global-machine-wide",
43326
43573
  projectNote: ({ qualifiedKey, quotedValue, filePath }) => `${qualifiedKey} = ${quotedValue} was written to the project-scoped ${filePath}, so it decides whether the built-in \`list_directory\` tool is registered in this repository.`,
@@ -54735,7 +54982,10 @@ var WarpSkill = class WarpSkill extends ToolSkill {
54735
54982
  //#region src/features/skills/zcode-skill.ts
54736
54983
  const ZcodeSkillFrontmatterSchema = z.looseObject({
54737
54984
  name: z.string(),
54738
- description: z.string()
54985
+ description: z.string(),
54986
+ when_to_use: z.optional(z.string()),
54987
+ license: z.optional(z.string()),
54988
+ metadata: z.optional(z.looseObject({}))
54739
54989
  });
54740
54990
  /**
54741
54991
  * Represents a ZCode skill directory.
@@ -54798,10 +55048,16 @@ var ZcodeSkill = class ZcodeSkill extends ToolSkill {
54798
55048
  }
54799
55049
  toRulesyncSkill() {
54800
55050
  const frontmatter = this.getFrontmatter();
55051
+ const zcodeSection = {
55052
+ ...frontmatter.when_to_use !== void 0 && { when_to_use: frontmatter.when_to_use },
55053
+ ...frontmatter.license !== void 0 && { license: frontmatter.license },
55054
+ ...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata }
55055
+ };
54801
55056
  const rulesyncFrontmatter = {
54802
55057
  name: frontmatter.name,
54803
55058
  description: frontmatter.description,
54804
- targets: ["*"]
55059
+ targets: ["*"],
55060
+ ...Object.keys(zcodeSection).length > 0 && { zcode: zcodeSection }
54805
55061
  };
54806
55062
  return new RulesyncSkill({
54807
55063
  outputRoot: this.outputRoot,
@@ -54816,9 +55072,21 @@ var ZcodeSkill = class ZcodeSkill extends ToolSkill {
54816
55072
  }
54817
55073
  static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
54818
55074
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
55075
+ const zcodeSection = rulesyncFrontmatter.zcode;
55076
+ const license = resolveLicense({
55077
+ rootFrontmatter: rulesyncFrontmatter,
55078
+ section: zcodeSection
55079
+ });
55080
+ const metadata = resolveMetadata({
55081
+ rootFrontmatter: rulesyncFrontmatter,
55082
+ section: zcodeSection
55083
+ });
54819
55084
  const zcodeFrontmatter = {
54820
55085
  name: rulesyncFrontmatter.name,
54821
- description: rulesyncFrontmatter.description
55086
+ description: rulesyncFrontmatter.description,
55087
+ ...zcodeSection?.when_to_use !== void 0 && { when_to_use: zcodeSection.when_to_use },
55088
+ ...license !== void 0 && { license },
55089
+ ...metadata !== void 0 && { metadata }
54822
55090
  };
54823
55091
  const settablePaths = ZcodeSkill.getSettablePaths({ global });
54824
55092
  return new ZcodeSkill({
@@ -62919,10 +63187,21 @@ var ClineRule = class ClineRule extends ToolRule {
62919
63187
  */
62920
63188
  const CodebuddyRuleFrontmatterSchema = z.object({
62921
63189
  description: z.optional(z.string()),
62922
- paths: z.optional(z.array(z.string())),
62923
- alwaysApply: z.optional(z.boolean())
63190
+ paths: z.optional(z.union([z.string(), z.array(z.string())])),
63191
+ alwaysApply: z.optional(z.boolean()),
63192
+ enabled: z.optional(z.boolean())
62924
63193
  });
62925
63194
  /**
63195
+ * Normalizes the documented `string` / `string[]` shapes of `paths` to the
63196
+ * list form the rest of the adapter works with. An empty list and an empty
63197
+ * string both mean "no paths".
63198
+ */
63199
+ function normalizeCodebuddyPaths(paths) {
63200
+ if (paths === void 0) return;
63201
+ const list = (typeof paths === "string" ? [paths] : paths).filter((path) => path.trim() !== "");
63202
+ return list.length > 0 ? list : void 0;
63203
+ }
63204
+ /**
62926
63205
  * A universal glob (matching everything) is redundant on an Always Apply
62927
63206
  * rule and, paired with `alwaysApply: true`, is the same semantic conflict
62928
63207
  * `CursorRule.resolveCursorGlobs` avoids for Cursor: `alwaysApply` already
@@ -62940,7 +63219,7 @@ const UNIVERSAL_PATHS = /* @__PURE__ */ new Set(["**/*", "*"]);
62940
63219
  * Rules format:
62941
63220
  * - {project}/CODEBUDDY.md (root: true), also read from {project}/.codebuddy/CODEBUDDY.md
62942
63221
  * - {project}/.codebuddy/rules/*.md (root: false, with optional
62943
- * `description` / `paths` / `alwaysApply` frontmatter)
63222
+ * `description` / `paths` / `alwaysApply` / `enabled` frontmatter)
62944
63223
  * - Global: ~/.codebuddy/CODEBUDDY.md and ~/.codebuddy/rules/*.md
62945
63224
  *
62946
63225
  * @see https://www.codebuddy.ai/docs/cli/memory
@@ -62982,9 +63261,10 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
62982
63261
  this.body = body;
62983
63262
  }
62984
63263
  static generateFileContent(body, frontmatter) {
62985
- if (frontmatter.description === void 0 && frontmatter.paths === void 0 && frontmatter.alwaysApply === void 0) return body;
63264
+ if (frontmatter.description === void 0 && frontmatter.paths === void 0 && frontmatter.alwaysApply === void 0 && frontmatter.enabled === void 0) return body;
62986
63265
  return stringifyFrontmatter(body, {
62987
63266
  description: frontmatter.description,
63267
+ enabled: frontmatter.enabled,
62988
63268
  alwaysApply: frontmatter.alwaysApply,
62989
63269
  paths: frontmatter.paths
62990
63270
  });
@@ -63032,10 +63312,36 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63032
63312
  root: isRoot
63033
63313
  });
63034
63314
  }
63035
- static resolveCodebuddyPaths({ paths, alwaysApply }) {
63036
- if (!paths || paths.length === 0) return;
63037
- if (alwaysApply && paths.every((path) => UNIVERSAL_PATHS.has(path.trim()))) return;
63038
- return paths;
63315
+ /**
63316
+ * Resolves the `paths` / `alwaysApply` pair against CodeBuddy's Rule Type
63317
+ * Determination table, which reads:
63318
+ *
63319
+ * | `alwaysApply` | `paths` | Rule type |
63320
+ * | ---------------- | --------- | ---------------------------------- |
63321
+ * | `true` (default) | any | ALWAYS — always injected |
63322
+ * | `false` | has value | MANUAL — triggered on matching file |
63323
+ * | `false` | none | not supported; the rule is dropped |
63324
+ *
63325
+ * The default being `true` is the opposite of Cursor, which the adapter was
63326
+ * modeled on: `paths` alone scopes nothing, so a rule meant to be path
63327
+ * triggered has to carry an explicit `alwaysApply: false` alongside it.
63328
+ *
63329
+ * @see https://www.codebuddy.ai/docs/cli/memory
63330
+ */
63331
+ static resolveCodebuddyRuleType({ paths, alwaysApply }) {
63332
+ const scopedPaths = paths && paths.length > 0 && !paths.every((path) => UNIVERSAL_PATHS.has(path.trim())) ? paths : void 0;
63333
+ if (alwaysApply === true) return {
63334
+ paths: scopedPaths,
63335
+ alwaysApply: true
63336
+ };
63337
+ if (scopedPaths === void 0) return {
63338
+ paths: void 0,
63339
+ alwaysApply: void 0
63340
+ };
63341
+ return {
63342
+ paths: scopedPaths,
63343
+ alwaysApply: false
63344
+ };
63039
63345
  }
63040
63346
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
63041
63347
  const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
@@ -63052,17 +63358,17 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63052
63358
  root
63053
63359
  });
63054
63360
  if (!paths.nonRoot) throw new Error(`nonRoot path is not set for ${rulesyncRule.getRelativeFilePath()}`);
63055
- const codebuddyPaths = rulesyncFrontmatter.codebuddy?.paths;
63361
+ const codebuddyPaths = normalizeCodebuddyPaths(rulesyncFrontmatter.codebuddy?.paths);
63056
63362
  const globs = rulesyncFrontmatter.globs;
63057
- const alwaysApply = rulesyncFrontmatter.codebuddy?.alwaysApply;
63058
- const pathsValue = CodebuddyRule.resolveCodebuddyPaths({
63363
+ const ruleType = CodebuddyRule.resolveCodebuddyRuleType({
63059
63364
  paths: codebuddyPaths ?? (globs?.length ? globs : void 0),
63060
- alwaysApply: alwaysApply === true
63365
+ alwaysApply: rulesyncFrontmatter.codebuddy?.alwaysApply
63061
63366
  });
63062
63367
  const codebuddyFrontmatter = {
63063
63368
  description: rulesyncFrontmatter.codebuddy?.description ?? rulesyncFrontmatter.description,
63064
- paths: pathsValue,
63065
- alwaysApply
63369
+ paths: ruleType.paths,
63370
+ alwaysApply: ruleType.alwaysApply,
63371
+ enabled: rulesyncFrontmatter.codebuddy?.enabled
63066
63372
  };
63067
63373
  return new CodebuddyRule({
63068
63374
  outputRoot,
@@ -63092,17 +63398,19 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63092
63398
  validate: true
63093
63399
  });
63094
63400
  }
63095
- const isAlways = this.frontmatter.alwaysApply === true;
63096
- const sourcePaths = this.frontmatter.paths ?? [];
63401
+ const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
63402
+ const isAlways = this.frontmatter.alwaysApply !== false;
63097
63403
  const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
63404
+ const alwaysApply = this.frontmatter.alwaysApply ?? (sourcePaths.length > 0 ? true : void 0);
63098
63405
  const rulesyncFrontmatter = {
63099
63406
  targets,
63100
63407
  root: false,
63101
63408
  description: this.frontmatter.description,
63102
63409
  globs,
63103
- ...(this.frontmatter.paths !== void 0 || this.frontmatter.alwaysApply !== void 0 || this.frontmatter.description !== void 0) && { codebuddy: {
63104
- paths: this.frontmatter.paths,
63105
- alwaysApply: this.frontmatter.alwaysApply,
63410
+ ...(this.frontmatter.paths !== void 0 || this.frontmatter.alwaysApply !== void 0 || this.frontmatter.enabled !== void 0 || this.frontmatter.description !== void 0) && { codebuddy: {
63411
+ paths: sourcePaths.length > 0 ? sourcePaths : void 0,
63412
+ alwaysApply,
63413
+ enabled: this.frontmatter.enabled,
63106
63414
  description: this.frontmatter.description
63107
63415
  } }
63108
63416
  };
@@ -65885,33 +66193,49 @@ var ReplitRule = class ReplitRule extends ToolRule {
65885
66193
  * Supports plain Markdown without frontmatter, mode-specific rules,
65886
66194
  * and both directory-based and single-file configurations.
65887
66195
  *
65888
- * - Project scope writes the non-root directory `.roo/rules/`.
66196
+ * - Project scope writes the non-root directory `.roo/rules/`, and the root
66197
+ * rule to the workspace-root `AGENTS.md` the extension reads from `cwd`.
65889
66198
  * - Global scope writes the same non-root directory resolved under the home
65890
- * directory (`~/.roo/rules/`), which Roo loads before workspace rules.
66199
+ * directory (`~/.roo/rules/`), which Roo loads before workspace rules. The
66200
+ * root rule joins it as `~/.roo/rules/AGENTS.md`: agent-rules discovery is
66201
+ * workspace-only, so the default project-scope root would resolve to a
66202
+ * `~/AGENTS.md` nothing ever reads.
65891
66203
  * @see https://roocodeinc.github.io/Roo-Code/features/custom-instructions
65892
66204
  */
65893
66205
  var RooRule = class RooRule extends ToolRule {
65894
- static getSettablePaths(_options = {}) {
65895
- return { nonRoot: { relativeDirPath: buildToolPath(ROO_DIR, "rules", _options.excludeToolDir) } };
66206
+ static getSettablePaths({ global, excludeToolDir } = {}) {
66207
+ const rulesDirPath = buildToolPath(ROO_DIR, "rules", excludeToolDir);
66208
+ if (global) return {
66209
+ root: {
66210
+ relativeDirPath: rulesDirPath,
66211
+ relativeFilePath: ROO_GLOBAL_ROOT_RULE_FILE_NAME
66212
+ },
66213
+ nonRoot: { relativeDirPath: rulesDirPath }
66214
+ };
66215
+ return { nonRoot: { relativeDirPath: rulesDirPath } };
65896
66216
  }
65897
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true }) {
65898
- const relativeDirPath = overrideDirPath !== void 0 && RooRule.extractModeFromDirPath(overrideDirPath) !== void 0 ? overrideDirPath : this.getSettablePaths().nonRoot.relativeDirPath;
66217
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
66218
+ const paths = this.getSettablePaths({ global });
66219
+ const relativeDirPath = overrideDirPath !== void 0 && RooRule.extractModeFromDirPath(overrideDirPath) !== void 0 ? overrideDirPath : paths.nonRoot?.relativeDirPath ?? buildToolPath(".roo", "rules");
65899
66220
  const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
66221
+ const isRoot = "root" in paths && relativeDirPath === paths.root.relativeDirPath && relativeFilePath === paths.root.relativeFilePath;
65900
66222
  return new RooRule({
65901
66223
  outputRoot,
65902
66224
  relativeDirPath,
65903
66225
  relativeFilePath,
65904
66226
  fileContent,
65905
66227
  validate,
65906
- root: false
66228
+ root: isRoot
65907
66229
  });
65908
66230
  }
65909
- static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true }) {
66231
+ static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
66232
+ const paths = this.getSettablePaths({ global });
65910
66233
  const params = this.buildToolRuleParamsDefault({
65911
66234
  outputRoot,
65912
66235
  rulesyncRule,
65913
66236
  validate,
65914
- nonRootPath: this.getSettablePaths().nonRoot
66237
+ ..."root" in paths && { rootPath: paths.root },
66238
+ nonRootPath: paths.nonRoot
65915
66239
  });
65916
66240
  const mode = rulesyncRule.getFrontmatter().roo?.mode;
65917
66241
  if (!params.root && mode !== void 0 && mode !== "") {
@@ -66834,7 +67158,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
66834
67158
  extension: "md",
66835
67159
  supportsGlobal: true,
66836
67160
  ruleDiscoveryMode: "auto",
66837
- collisionPolicy: "fold"
67161
+ collisionPolicy: "fold",
67162
+ localRootMode: "separate-local-file",
67163
+ localRootFileName: CRUSH_LOCAL_RULE_FILE_NAME
66838
67164
  }
66839
67165
  }],
66840
67166
  ["cursor", {
@@ -67554,6 +67880,34 @@ var RulesProcessor = class extends FeatureProcessor {
67554
67880
  root: true,
67555
67881
  localRoot
67556
67882
  });
67883
+ if (isClassOrSubclassOf({
67884
+ candidate: factory.class,
67885
+ base: CodebuddyRule
67886
+ })) {
67887
+ const paths = CodebuddyRule.getSettablePaths({ global: this.global });
67888
+ return new CodebuddyRule({
67889
+ outputRoot: this.outputRoot,
67890
+ relativeDirPath: relativeDirPath ?? paths.root.relativeDirPath,
67891
+ relativeFilePath: fileName,
67892
+ frontmatter: {},
67893
+ body,
67894
+ validate: true,
67895
+ root: true,
67896
+ localRoot
67897
+ });
67898
+ }
67899
+ if (isClassOrSubclassOf({
67900
+ candidate: factory.class,
67901
+ base: CrushRule
67902
+ })) return new CrushRule({
67903
+ outputRoot: this.outputRoot,
67904
+ relativeDirPath: relativeDirPath ?? ".",
67905
+ relativeFilePath: fileName,
67906
+ fileContent: body,
67907
+ validate: true,
67908
+ root: true,
67909
+ localRoot
67910
+ });
67557
67911
  if (isClassOrSubclassOf({
67558
67912
  candidate: factory.class,
67559
67913
  base: QwencodeRule
@@ -70152,6 +70506,6 @@ async function importChecksCore(params) {
70152
70506
  return writtenCount;
70153
70507
  }
70154
70508
  //#endregion
70155
- export { RulesyncCheckFrontmatterSchema as $, ALL_TOOL_TARGETS_WITH_WILDCARD as $t, FACTORYDROID_DIR as A, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as An, fileExists as At, RulesyncSkillFrontmatterSchema as B, stripControlCharacters as Bn, readFileContent as Bt, CODEXCLI_BASH_RULES_FILE_NAME as C, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as Cn, assertDirectoryIfExists as Ct, CLAUDECODE_MEMORIES_DIR_NAME as D, RULESYNC_RULES_RELATIVE_DIR_PATH as Dn, createTempDirectory as Dt, CLAUDECODE_LOCAL_RULE_FILE_NAME as E, RULESYNC_RELATIVE_DIR_PATH as En, checkPathTraversal as Et, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as F, formatError as Fn, isSymlink as Ft, RulesyncIgnore as G, removeFileStrict as Gt, RulesyncRuleFrontmatterSchema as H, stripHiddenCharacters as Hn, removeDirectory as Ht, getLocalSkillDirNames as I, truncateText as In, listDirectoryEntryNames as It, resolveRulesyncSourceWritePath as J, runWithDirectoryRollback as Jt, RulesyncHooks as K, removeTempDirectory as Kt, RulesyncSubagent as L, hasDeceptiveHiddenCharacters as Ln, listFilePathsRecursively as Lt, caseFoldIdentity as M, ALL_FEATURES as Mn, getHomeDirectory as Mt, groupSpellingsByCaseFoldedIdentity as N, ALL_FEATURES_WITH_WILDCARD as Nn, isFileNotFoundError as Nt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as O, RULESYNC_SKILLS_RELATIVE_DIR_PATH as On, directoryExists as Ot, AUGMENTCODE_DIR as P, DEPRECATED_FEATURE_REPLACEMENTS as Pn, isFileSystemError as Pt, RulesyncCheck as Q, ALL_TOOL_TARGETS as Qt, RulesyncSubagentFrontmatterSchema as R, hasEnclosingMarkOutsideKeycap as Rn, listSubdirectoryNames as Rt, ChecksProcessor as S, RULESYNC_PERMISSIONS_FILE_NAME as Sn, applyFileMode as St, CLAUDECODE_DIR as T, RULESYNC_PERMISSIONS_SCHEMA_URL as Tn, assertWritablePathInsideRoot as Tt, RulesyncPermissions as U, removeDirectoryStrict as Ut, RulesyncRule as V, stripControlCharactersKeepingLineFeeds as Vn, readFileContentOrNull as Vt, RulesyncMcp as W, removeFile as Wt, RulesyncCommand as X, writeFileBuffer as Xt, parseJsonc as Y, toPosixPath as Yt, RulesyncCommandFrontmatterSchema as Z, writeFileContent as Zt, IgnoreProcessor as _, RULESYNC_MCP_FILE_NAME as _n, withFallbackLoggerTarget as _t, getProcessorRegistryEntry as a, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as an, mergeInputRootConfigs as at, QWENCODE_DIR as b, RULESYNC_MCP_SCHEMA_URL as bn, CLIError as bt, RulesProcessor as c, RULESYNC_CONFIG_RELATIVE_FILE_PATH as cn, ConfigFileSchema as ct, CODEBUDDY_DIR as d, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as dn, findControlCharacter as dt, PACKAGING_TOOL_TARGETS as en, stringifyFrontmatter as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_HOOKS_FILE_NAME as fn, ConsoleLogger as ft, McpProcessor as g, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as gn, warnOnConflictingFlags as gt, shortenToWidth as h, RULESYNC_IGNORE_RELATIVE_FILE_PATH as hn, fallbackLogger as ht, inspectInputRoots as i, RULESYNC_AIIGNORE_FILE_NAME as in, ConfigResolver as it, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as j, parseCommaSeparatedList as jn, getFileSize as jt, CLAUDECODE_SKILLS_DIR_PATH as k, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as kn, ensureDir as kt, SubagentsProcessor as l, RULESYNC_CONFIG_SCHEMA_URL as ln, GITIGNORE_DESTINATION_KEY as lt, displayWidthOf as m, RULESYNC_HOOKS_RELATIVE_FILE_PATH as mn, WarningCollectingLogger as mt, formatSourceLoadFailure as n, CURATED_RULES_FEATURE_SUBDIR as nn, SHARED_USER_MANAGED_CONFIG_PATHS as nt, convertFromTool as o, RULESYNC_CHECKS_RELATIVE_DIR_PATH as on, resolveEffectiveInputRoots as ot, ELLIPSIS_WIDTH as p, RULESYNC_HOOKS_LEGACY_FILE_NAME as pn, JsonLogger as pt, getRulesyncSourceCandidates as q, resolvePath as qt, generate as r, MAX_FILE_SIZE as rn, SKILL_FILE_NAME as rt, isPackagingToolTarget as s, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as sn, CONFLICTING_TARGET_PAIRS as st, importFromTool as t, ToolTargetSchema as tn, loadYaml as tt, SkillsProcessor as u, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as un, SourceEntrySchema as ut, HooksProcessor as v, RULESYNC_MCP_LEGACY_FILE_NAME as vn, resetRunWarningState as vt, CODEXCLI_DIR as w, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as wn, assertTreeContainsNoSymlinks as wt, QWENCODE_LOCAL_RULE_FILE_NAME as x, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as xn, ErrorCodes as xt, CommandsProcessor as y, RULESYNC_MCP_RELATIVE_FILE_PATH as yn, withWarnOnceScope as yt, RulesyncSkill as z, quoteForLog as zn, pathEscapesRoot as zt };
70509
+ export { RulesyncCheck as $, ALL_TOOL_TARGETS as $t, CLAUDECODE_SKILLS_DIR_PATH as A, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as An, ensureDir as At, RulesyncSkill as B, quoteForLog as Bn, pathEscapesRoot as Bt, ChecksProcessor as C, RULESYNC_PERMISSIONS_FILE_NAME as Cn, applyFileMode as Ct, CLAUDECODE_LOCAL_RULE_FILE_NAME as D, RULESYNC_RELATIVE_DIR_PATH as Dn, checkPathTraversal as Dt, CLAUDECODE_DIR as E, RULESYNC_PERMISSIONS_SCHEMA_URL as En, assertWritablePathInsideRoot as Et, AUGMENTCODE_DIR as F, DEPRECATED_FEATURE_REPLACEMENTS as Fn, isFileSystemError as Ft, RulesyncMcp as G, removeFile as Gt, RulesyncRule as H, stripControlCharactersKeepingLineFeeds as Hn, readFileContentOrNull as Ht, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as I, formatError as In, isSymlink as It, getRulesyncSourceCandidates as J, resolvePath as Jt, RulesyncIgnore as K, removeFileStrict as Kt, getLocalSkillDirNames as L, truncateText as Ln, listDirectoryEntryNames as Lt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as M, parseCommaSeparatedList as Mn, getFileSize as Mt, caseFoldIdentity as N, ALL_FEATURES as Nn, getHomeDirectory as Nt, CLAUDECODE_MEMORIES_DIR_NAME as O, RULESYNC_RULES_RELATIVE_DIR_PATH as On, createTempDirectory as Ot, groupSpellingsByCaseFoldedIdentity as P, ALL_FEATURES_WITH_WILDCARD as Pn, isFileNotFoundError as Pt, RulesyncCommandFrontmatterSchema as Q, writeFileContent as Qt, RulesyncSubagent as R, hasDeceptiveHiddenCharacters as Rn, listFilePathsRecursively as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Sn, ErrorCodes as St, CODEXCLI_DIR as T, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Tn, assertTreeContainsNoSymlinks as Tt, RulesyncRuleFrontmatterSchema as U, stripHiddenCharacters as Un, removeDirectory as Ut, RulesyncSkillFrontmatterSchema as V, stripControlCharacters as Vn, readFileContent as Vt, RulesyncPermissions as W, removeDirectoryStrict as Wt, parseJsonc as X, toPosixPath as Xt, resolveRulesyncSourceWritePath as Y, runWithDirectoryRollback as Yt, RulesyncCommand as Z, writeFileBuffer as Zt, IgnoreProcessor as _, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as _n, warnOnConflictingFlags as _t, getProcessorRegistryEntry as a, RULESYNC_AIIGNORE_FILE_NAME as an, ConfigResolver as at, CommandsProcessor as b, RULESYNC_MCP_RELATIVE_FILE_PATH as bn, withWarnOnceScope as bt, RulesProcessor as c, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as cn, CONFLICTING_TARGET_PAIRS as ct, CODEBUDDY_DIR as d, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as dn, SourceEntrySchema as dt, ALL_TOOL_TARGETS_WITH_WILDCARD as en, RulesyncCheckFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as fn, findControlCharacter as ft, McpProcessor as g, RULESYNC_IGNORE_RELATIVE_FILE_PATH as gn, fallbackLogger as gt, shortenToWidth as h, RULESYNC_HOOKS_RELATIVE_FILE_PATH as hn, WarningCollectingLogger as ht, inspectInputRoots as i, MAX_FILE_SIZE as in, SKILL_FILE_NAME as it, FACTORYDROID_DIR as j, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as jn, fileExists as jt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as k, RULESYNC_SKILLS_RELATIVE_DIR_PATH as kn, directoryExists as kt, SubagentsProcessor as l, RULESYNC_CONFIG_RELATIVE_FILE_PATH as ln, ConfigFileSchema as lt, displayWidthOf as m, RULESYNC_HOOKS_LEGACY_FILE_NAME as mn, JsonLogger as mt, formatSourceLoadFailure as n, ToolTargetSchema as nn, loadYaml as nt, convertFromTool as o, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as on, mergeInputRootConfigs as ot, ELLIPSIS_WIDTH as p, RULESYNC_HOOKS_FILE_NAME as pn, ConsoleLogger as pt, RulesyncHooks as q, removeTempDirectory as qt, generate as r, CURATED_RULES_FEATURE_SUBDIR as rn, SHARED_USER_MANAGED_CONFIG_PATHS as rt, isPackagingToolTarget as s, RULESYNC_CHECKS_RELATIVE_DIR_PATH as sn, resolveEffectiveInputRoots as st, importFromTool as t, PACKAGING_TOOL_TARGETS as tn, stringifyFrontmatter as tt, SkillsProcessor as u, RULESYNC_CONFIG_SCHEMA_URL as un, GITIGNORE_DESTINATION_KEY as ut, CRUSH_LOCAL_RULE_FILE_NAME as v, RULESYNC_MCP_FILE_NAME as vn, withFallbackLoggerTarget as vt, CODEXCLI_BASH_RULES_FILE_NAME as w, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as wn, assertDirectoryIfExists as wt, QWENCODE_DIR as x, RULESYNC_MCP_SCHEMA_URL as xn, CLIError as xt, HooksProcessor as y, RULESYNC_MCP_LEGACY_FILE_NAME as yn, resetRunWarningState as yt, RulesyncSubagentFrontmatterSchema as z, hasEnclosingMarkOutsideKeycap as zn, listSubdirectoryNames as zt };
70156
70510
 
70157
- //# sourceMappingURL=import-B6p6c3L0.js.map
70511
+ //# sourceMappingURL=import-BNh_gCUr.js.map