rulesync 16.25.0 → 16.26.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.
@@ -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.
@@ -6879,12 +6884,37 @@ const DeepagentsStartupOverrideSchema = z.looseObject({
6879
6884
  read_project_dotenv: z.optional(z.boolean())
6880
6885
  });
6881
6886
  /**
6887
+ * deepagents-cli's Python extension gate under `[extensions]` in
6888
+ * `~/.deepagents/config.toml`. `discover_extensions` auto-loads `*.py` from the
6889
+ * user's `~/.deepagents/extensions/` and from the checked-out project's
6890
+ * `<root>/.deepagents/extensions/`, so these two keys decide whether
6891
+ * repository-authored Python is imported into the agent process at all —
6892
+ * the same class of knob as `[startup].mode`, and one that ships defaulting to
6893
+ * a prompt a user can accept. Loose so a key added upstream passes through
6894
+ * verbatim.
6895
+ *
6896
+ * `extensions.extra_paths` is deliberately absent: it names machine-local files
6897
+ * and directories, which do not belong in a committed
6898
+ * `.rulesync/permissions.jsonc`, and it only ever widens what loads.
6899
+ *
6900
+ * @see https://docs.langchain.com/oss/deepagents/code/configuration
6901
+ */
6902
+ const DeepagentsExtensionsOverrideSchema = z.looseObject({
6903
+ enabled: z.optional(z.boolean()),
6904
+ trust: z.optional(z.enum([
6905
+ "ask",
6906
+ "always",
6907
+ "never"
6908
+ ]))
6909
+ });
6910
+ /**
6882
6911
  * The `[shell].allow_list` array itself is rulesync-owned, driven by the
6883
6912
  * shared `permission.bash` block, so it has no key here.
6884
6913
  */
6885
6914
  const DeepagentsPermissionsOverrideSchema = z.looseObject({
6886
6915
  permission: z.optional(ToolScopedPermissionSchema),
6887
- startup: z.optional(DeepagentsStartupOverrideSchema)
6916
+ startup: z.optional(DeepagentsStartupOverrideSchema),
6917
+ extensions: z.optional(DeepagentsExtensionsOverrideSchema)
6888
6918
  });
6889
6919
  /**
6890
6920
  * The actions Junie's allowlist accepts. Verified against the shipped Junie
@@ -7895,8 +7925,9 @@ const RulesyncRuleFrontmatterSchema = z.object({
7895
7925
  agentsmd: z.optional(z.looseObject({ subprojectPath: z.optional(z.string()) })),
7896
7926
  claudecode: z.optional(z.looseObject({ paths: z.optional(z.array(z.string())) })),
7897
7927
  codebuddy: z.optional(z.looseObject({
7898
- paths: z.optional(z.array(z.string())),
7928
+ paths: z.optional(z.union([z.string(), z.array(z.string())])),
7899
7929
  alwaysApply: z.optional(z.boolean()),
7930
+ enabled: z.optional(z.boolean()),
7900
7931
  description: z.optional(z.string())
7901
7932
  })),
7902
7933
  cursor: z.optional(z.looseObject({
@@ -9189,6 +9220,11 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
9189
9220
  license: z.optional(z.string()),
9190
9221
  compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
9191
9222
  metadata: z.optional(z.looseObject({}))
9223
+ })),
9224
+ zcode: z.optional(z.looseObject({
9225
+ when_to_use: z.optional(z.string()),
9226
+ license: z.optional(z.string()),
9227
+ metadata: z.optional(z.looseObject({}))
9192
9228
  }))
9193
9229
  });
9194
9230
  /**
@@ -17771,6 +17807,20 @@ const ROO_MCP_FILE_NAME = "mcp.json";
17771
17807
  */
17772
17808
  const ROO_MODE_SLUG_PATTERN = /^[a-zA-Z0-9-]+$/;
17773
17809
  /**
17810
+ * `~/.roo/rules/AGENTS.md` — where the root rule goes in global scope.
17811
+ *
17812
+ * Roo/Zoo Code discover the agent-rules files (`AGENTS.md`, `AGENT.md`,
17813
+ * `AGENTS.local.md`) in the **workspace only**: `loadAllAgentRulesFiles` reads
17814
+ * `cwd`, plus subdirectories holding a `.roo` folder when `enableSubfolderRules`
17815
+ * is on. There is no home-directory branch, so a `~/AGENTS.md` is never read.
17816
+ * The global rules directory `~/.roo/rules/` *is* loaded, so the root rule is
17817
+ * written there alongside the non-root files rather than to an unread path.
17818
+ * The basename is kept so the file still reads as the root overview.
17819
+ * @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/core/prompts/sections/custom-instructions.ts
17820
+ * @see https://github.com/Zoo-Code-Org/Zoo-Code/blob/main/src/services/roo-config/index.ts
17821
+ */
17822
+ const ROO_GLOBAL_ROOT_RULE_FILE_NAME = "AGENTS.md";
17823
+ /**
17774
17824
  * `.roo/rules-{mode}/` — the mode-specific rule directory Roo/Zoo Code load
17775
17825
  * INSTEAD of `.roo/rules/` while that mode is active. The relative path is the
17776
17826
  * same in global scope, where it resolves under `~/.roo/`.
@@ -22830,7 +22880,7 @@ var DevinHooks = class DevinHooks extends ToolHooks {
22830
22880
  * adapter so the settings reader in `src/utils/` can name the same list without
22831
22881
  * importing a feature.
22832
22882
  *
22833
- * @see https://docs.factory.ai/cli/configuration/settings
22883
+ * @see https://docs.factory.ai/droid-cli/settings
22834
22884
  */
22835
22885
  const FACTORYDROID_OVERRIDE_KEYS = [
22836
22886
  "commandBlocklist",
@@ -22838,6 +22888,8 @@ const FACTORYDROID_OVERRIDE_KEYS = [
22838
22888
  "sandbox",
22839
22889
  "mcpPolicy",
22840
22890
  "mcpAutonomyOverrides",
22891
+ "mcpAutonomyUrlOverrides",
22892
+ "builtInToolAutonomyOverrides",
22841
22893
  "enableDroidShield",
22842
22894
  "sessionDefaultSettings",
22843
22895
  "maxAutonomyLevel",
@@ -22845,7 +22897,10 @@ const FACTORYDROID_OVERRIDE_KEYS = [
22845
22897
  "interactionMode",
22846
22898
  "extraKnownMarketplaces",
22847
22899
  "enabledPlugins",
22900
+ "strictEnabledPlugins",
22901
+ "strictKnownMarketplaces",
22848
22902
  "hooksDisabled",
22903
+ "allowManagedHooksOnly",
22849
22904
  "disabledSkills",
22850
22905
  "modelPolicy",
22851
22906
  "missionPolicy"
@@ -26886,6 +26941,7 @@ var ClineIgnore = class ClineIgnore extends ToolIgnore {
26886
26941
  //#region src/constants/crush-paths.ts
26887
26942
  const CRUSH_RULE_FILE_NAME = "CRUSH.md";
26888
26943
  const CRUSH_GLOBAL_DIR = join(".config", "crush");
26944
+ const CRUSH_LOCAL_RULE_FILE_NAME = "CRUSH.local.md";
26889
26945
  const CRUSH_IGNORE_FILE_NAME = ".crushignore";
26890
26946
  const CRUSH_SKILLS_PROJECT_DIR = join(".crush", "skills");
26891
26947
  const CRUSH_SKILLS_GLOBAL_DIR = join(CRUSH_GLOBAL_DIR, "skills");
@@ -33106,9 +33162,16 @@ function lookupTransport(map, key) {
33106
33162
  }
33107
33163
  /**
33108
33164
  * 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.
33165
+ * off an unfiltered canonical entry, under either spelling. `undefined` means
33166
+ * the entry says nothing, which is Rovo Dev's own default.
33167
+ *
33168
+ * The flag is a **tri-state**, not a switch that only exists when on. Atlassian
33169
+ * inverted the default on 2026-09-02: a server's instructions are now surfaced
33170
+ * when the key is absent or `true`, and `false` is what suppresses them — the
33171
+ * opposite of the opt-in wording this adapter was first written against, where
33172
+ * absent and `false` meant the same thing. So `false` has to travel end to end;
33173
+ * collapsing it away is what makes a suppression impossible to author, and
33174
+ * erases one on import.
33112
33175
  *
33113
33176
  * The canonical key decides whenever it is present, the way codex resolves the
33114
33177
  * same two-spelling conflict for `experimental_environment`. OR-ing the two
@@ -33116,28 +33179,25 @@ function lookupTransport(map, key) {
33116
33179
  * `enable_instructions: true` copied out of Atlassian's docs — fail-open, on
33117
33180
  * the one key whose whole purpose is a trust decision.
33118
33181
  *
33182
+ * A non-boolean under either spelling reads as `undefined` rather than as a
33183
+ * suppression: Rovo Dev surfaces the instructions for anything that is not
33184
+ * `false`, so writing `false` there would invent a restriction the tool is not
33185
+ * applying. (The canonical key is a strict boolean in the schema, so only the
33186
+ * raw spelling can carry one.)
33187
+ *
33119
33188
  * `isPlainObject` rather than `isRecord`: this walks a user-supplied key set,
33120
33189
  * so a `constructor` entry must not resolve up the prototype chain.
33121
33190
  */
33122
33191
  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.`);
33192
+ if (!isPlainObject$1(rawServer)) return;
33193
+ const canonical = rawServer.rovodevEnableInstructions;
33194
+ if (canonical !== void 0) return typeof canonical === "boolean" ? canonical : void 0;
33195
+ const raw = rawServer.enable_instructions;
33196
+ return typeof raw === "boolean" ? raw : void 0;
33137
33197
  }
33138
33198
  function toRovodevServer(name, server, logger) {
33139
33199
  const { type, transport, disabled: _disabled, rovodevEnableInstructions, ...rest } = server;
33140
- if (rovodevEnableInstructions === true) rest.enable_instructions = true;
33200
+ if (typeof rovodevEnableInstructions === "boolean") rest.enable_instructions = rovodevEnableInstructions;
33141
33201
  const declared = typeof transport === "string" ? transport : typeof type === "string" ? type : void 0;
33142
33202
  if (declared === void 0) return rest;
33143
33203
  const mapped = lookupTransport(CANONICAL_TO_ROVODEV_TRANSPORT, declared);
@@ -33152,7 +33212,7 @@ function toRovodevServer(name, server, logger) {
33152
33212
  }
33153
33213
  function fromRovodevServer(server) {
33154
33214
  const { transport, enable_instructions: enableInstructions, ...rest } = server;
33155
- if (enableInstructions === true) rest.rovodevEnableInstructions = true;
33215
+ if (typeof enableInstructions === "boolean") rest.rovodevEnableInstructions = enableInstructions;
33156
33216
  if (typeof transport !== "string") return rest;
33157
33217
  const mapped = lookupTransport(ROVODEV_TO_CANONICAL_TRANSPORT, transport);
33158
33218
  return mapped === void 0 ? rest : {
@@ -33593,10 +33653,10 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
33593
33653
  canWriteDisableToggle = false;
33594
33654
  }
33595
33655
  const mcpServers = Object.fromEntries(Object.entries(rulesyncMcp.getMcpServers()).map(([name, server]) => {
33596
- const rawServer = rulesyncMcp.getRawMcpServer(name);
33656
+ const enableInstructions = readEnableInstructions(rulesyncMcp.getRawMcpServer(name));
33597
33657
  const record = {
33598
33658
  ...server,
33599
- ...readEnableInstructions(rawServer) && { rovodevEnableInstructions: true }
33659
+ ...enableInstructions !== void 0 && { rovodevEnableInstructions: enableInstructions }
33600
33660
  };
33601
33661
  if (record.disabled === true && !canWriteDisableToggle) {
33602
33662
  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 +33665,6 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
33605
33665
  const converted = toRovodevServer(name, record, logger);
33606
33666
  return converted === null ? null : [name, converted];
33607
33667
  }).filter((entry) => entry !== null));
33608
- warnEnabledInstructions(Object.entries(mcpServers).filter(([, server]) => server.enable_instructions === true).map(([name]) => name), logger);
33609
33668
  const rovodevConfig = {
33610
33669
  ...json,
33611
33670
  mcpServers
@@ -39749,6 +39808,34 @@ const DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS = {
39749
39808
  yolo_switcher: true,
39750
39809
  read_project_dotenv: true
39751
39810
  };
39811
+ /**
39812
+ * `[extensions]` gates dcode's Python extension system. `discover_extensions`
39813
+ * auto-loads `*.py` from the user's `~/.deepagents/extensions/` and from the
39814
+ * checked-out project's `<root>/.deepagents/extensions/`, so this table decides
39815
+ * whether a cloned repository's Python is imported into the agent process.
39816
+ */
39817
+ const EXTENSIONS_TABLE_KEY = "extensions";
39818
+ /**
39819
+ * Keys lifted back into the override on import. `extensions.extra_paths` is
39820
+ * deliberately absent: it names machine-local files and directories, which do
39821
+ * not belong in a committed `.rulesync/permissions.jsonc`, and it only ever
39822
+ * widens what loads.
39823
+ */
39824
+ const DEEPAGENTS_EXTENSIONS_KEYS = ["enabled", "trust"];
39825
+ /** `TrustPolicy` in `extensions/settings.py`; dcode reads nothing else. */
39826
+ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
39827
+ "ask",
39828
+ "always",
39829
+ "never"
39830
+ ];
39831
+ /**
39832
+ * `[extensions].enabled` defaults to `true` upstream, so writing `true` on a
39833
+ * machine that has not set it changes nothing — the same reasoning as
39834
+ * `DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS`.
39835
+ *
39836
+ * @see https://github.com/langchain-ai/deepagents `config_manifest.py`
39837
+ */
39838
+ const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
39752
39839
  const ALLOW_ALL_SENTINEL = "all";
39753
39840
  const RECOMMENDED_SENTINEL = "recommended";
39754
39841
  const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
@@ -39818,7 +39905,10 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
39818
39905
  * `DeepagentsPermissionsOverrideSchema`): `[startup].mode`
39819
39906
  * (`manual` / `auto` / `yolo`), `[startup].yolo_switcher` and
39820
39907
  * `[startup].read_project_dotenv` are merged into the file on generate and
39821
- * lifted back into the override on import.
39908
+ * lifted back into the override on import. The same override carries
39909
+ * `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
39910
+ * which decide whether the Python in a checked-out project's
39911
+ * `.deepagents/extensions/` is imported into the agent process.
39822
39912
  *
39823
39913
  * On **import** the allowlist comes back as `bash` `allow` rules named by the
39824
39914
  * executable — skipping any entry dcode could not match in the first place, so
@@ -39920,6 +40010,13 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
39920
40010
  filePath,
39921
40011
  logger
39922
40012
  });
40013
+ const extensionsOverride = config.deepagents?.extensions;
40014
+ if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
40015
+ settings,
40016
+ extensionsOverride,
40017
+ filePath,
40018
+ logger
40019
+ });
39923
40020
  return new DeepagentsPermissions({
39924
40021
  outputRoot,
39925
40022
  relativeDirPath: paths.relativeDirPath,
@@ -39944,8 +40041,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
39944
40041
  startup: isPlainObject$1(settings[STARTUP_TABLE_KEY]) ? settings[STARTUP_TABLE_KEY] : {},
39945
40042
  selfPath
39946
40043
  });
40044
+ const extensionsOverride = liftExtensionsOverride({
40045
+ extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
40046
+ selfPath
40047
+ });
39947
40048
  const result = { ...config };
39948
- if (Object.keys(startupOverride).length > 0) result.deepagents = { startup: startupOverride };
40049
+ const deepagents = {};
40050
+ if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
40051
+ if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
40052
+ if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
39949
40053
  return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
39950
40054
  }
39951
40055
  validate() {
@@ -40032,57 +40136,192 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
40032
40136
  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
40137
  }
40034
40138
  /**
40035
- * Lift the `[startup]` keys rulesync models back into the `deepagents`
40036
- * override, keeping only values dcode itself accepts.
40139
+ * Lift the keys rulesync models from one `config.toml` table back into the
40140
+ * `deepagents` override, keeping only values dcode itself accepts.
40037
40141
  *
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.
40142
+ * A value outside what the option's own parser reads is `Invalid` upstream
40143
+ * dcode ignores it and falls back to its default. Importing it anyway would
40144
+ * record a setting the tool is not applying and, because the canonical schema
40145
+ * is stricter than TOML, would write a `.rulesync/permissions.jsonc` the next
40146
+ * `rulesync generate` cannot even read.
40147
+ *
40148
+ * `normalize` returns the value to keep, or `null` for one dcode would not
40149
+ * read — a wrapper rather than the bare value, so a legitimately falsy setting
40150
+ * is not mistaken for a rejection.
40043
40151
  */
40044
- function liftStartupOverride({ startup, selfPath }) {
40045
- const startupOverride = {};
40152
+ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
40153
+ const override = {};
40046
40154
  const rejected = [];
40047
- for (const key of DEEPAGENTS_STARTUP_KEYS) {
40048
- const value = startup[key];
40155
+ for (const key of keys) {
40156
+ const value = table[key];
40049
40157
  if (value === void 0) continue;
40050
- if (key === "mode" ? DEEPAGENTS_STARTUP_MODES.includes(value) : typeof value === "boolean") startupOverride[key] = value;
40158
+ const normalized = normalize({
40159
+ key,
40160
+ value
40161
+ });
40162
+ if (normalized) override[key] = normalized.value;
40051
40163
  else rejected.push(`${key} = ${JSON.stringify(value)}`);
40052
40164
  }
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;
40165
+ 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.`);
40166
+ return override;
40055
40167
  }
40056
40168
  /**
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.
40169
+ * Lift the `[startup]` keys rulesync models back into the `deepagents`
40170
+ * override. A `mode` outside the three approval modes, or a non-boolean where
40171
+ * a switch belongs, is one dcode ignores.
40061
40172
  */
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.`);
40173
+ function liftStartupOverride({ startup, selfPath }) {
40174
+ return liftOverrideTable({
40175
+ table: startup,
40176
+ tableKey: STARTUP_TABLE_KEY,
40177
+ keys: DEEPAGENTS_STARTUP_KEYS,
40178
+ normalize: ({ key, value }) => {
40179
+ if (key === "mode") return DEEPAGENTS_STARTUP_MODES.includes(value) ? { value } : null;
40180
+ return typeof value === "boolean" ? { value } : null;
40181
+ },
40182
+ selfPath
40183
+ });
40184
+ }
40185
+ /**
40186
+ * Lift the `[extensions]` keys rulesync models back into the `deepagents`
40187
+ * override.
40188
+ *
40189
+ * `trust` is read the way `parse_trust_policy` reads it — trimmed and
40190
+ * lowercased — so `"Always"` comes back as the policy dcode is actually
40191
+ * applying rather than being dropped, the same courtesy the allowlist
40192
+ * sentinels already get. The canonical enum holds only the lowercase
40193
+ * spellings, so keeping the value verbatim would write a permissions file the
40194
+ * next generate could not parse.
40195
+ */
40196
+ function liftExtensionsOverride({ extensions, selfPath }) {
40197
+ return liftOverrideTable({
40198
+ table: extensions,
40199
+ tableKey: EXTENSIONS_TABLE_KEY,
40200
+ keys: DEEPAGENTS_EXTENSIONS_KEYS,
40201
+ normalize: ({ key, value }) => {
40202
+ if (key === "trust") {
40203
+ if (typeof value !== "string") return null;
40204
+ const policy = value.trim().toLowerCase();
40205
+ return DEEPAGENTS_EXTENSION_TRUST_POLICIES.includes(policy) ? { value: policy } : null;
40206
+ }
40207
+ return typeof value === "boolean" ? { value } : null;
40208
+ },
40209
+ selfPath
40210
+ });
40211
+ }
40212
+ /**
40213
+ * Merge one block of the `deepagents` override into its `config.toml` table,
40214
+ * preserving every other key of that table. A table that is not a table at all
40215
+ * is something rulesync did not write and cannot merge into, so it is left
40216
+ * exactly as the user has it rather than replaced.
40217
+ */
40218
+ function mergeOverrideTable({ settings, tableKey, override, knownKeys, isDroppedKey, warnAboutRelaxations, filePath, logger }) {
40219
+ const existing = settings[tableKey];
40220
+ if (existing !== void 0 && !isPlainObject$1(existing)) {
40221
+ warnWithFallback(logger, `deepagents-cli: '${tableKey}' in ${filePath} is not a table, so the deepagents ${tableKey} override was skipped rather than overwriting it.`);
40066
40222
  return;
40067
40223
  }
40068
- const startup = isPlainObject$1(existingStartup) ? { ...existingStartup } : {};
40069
- const previousStartup = { ...startup };
40224
+ const table = isPlainObject$1(existing) ? { ...existing } : {};
40225
+ const previous = { ...table };
40070
40226
  const writtenKeys = [];
40071
- for (const [key, value] of Object.entries(startupOverride)) {
40227
+ for (const [key, value] of Object.entries(override)) {
40072
40228
  if (isPrototypePollutionKey(key)) continue;
40073
- if (key === STARTUP_RECENT_KEY) continue;
40229
+ if (isDroppedKey?.(key)) continue;
40074
40230
  if (value === null || value === void 0) continue;
40075
- startup[key] = value;
40231
+ table[key] = value;
40076
40232
  writtenKeys.push(key);
40077
40233
  }
40078
- warnAboutStartupRelaxations({
40079
- startupOverride,
40080
- previousStartup,
40234
+ warnAboutRelaxations({
40235
+ override,
40236
+ previous,
40237
+ filePath,
40238
+ logger
40239
+ });
40240
+ warnAboutUncheckedKeys({
40241
+ tableKey,
40242
+ knownKeys,
40081
40243
  writtenKeys,
40082
40244
  filePath,
40083
40245
  logger
40084
40246
  });
40085
- if (Object.keys(startup).length > 0) settings[STARTUP_TABLE_KEY] = startup;
40247
+ if (Object.keys(table).length > 0) settings[tableKey] = table;
40248
+ }
40249
+ /**
40250
+ * Name the keys the merge wrote that rulesync does not model. The override is a
40251
+ * loose object so a key added upstream still reaches the config, but this one
40252
+ * writes into the machine's global file: what rulesync cannot judge, it at
40253
+ * least names.
40254
+ */
40255
+ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, logger }) {
40256
+ const known = new Set(knownKeys);
40257
+ const unknownKeys = writtenKeys.filter((key) => !known.has(key));
40258
+ if (unknownKeys.length === 0) return;
40259
+ 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.`);
40260
+ }
40261
+ /**
40262
+ * Merge the `deepagents.startup` override into `[startup]`.
40263
+ */
40264
+ function mergeStartupOverride({ settings, startupOverride, filePath, logger }) {
40265
+ mergeOverrideTable({
40266
+ settings,
40267
+ tableKey: STARTUP_TABLE_KEY,
40268
+ override: startupOverride,
40269
+ knownKeys: DEEPAGENTS_STARTUP_KEYS,
40270
+ isDroppedKey: (key) => key === STARTUP_RECENT_KEY,
40271
+ warnAboutRelaxations: warnAboutStartupRelaxations,
40272
+ filePath,
40273
+ logger
40274
+ });
40275
+ }
40276
+ /**
40277
+ * Merge the `deepagents.extensions` override into `[extensions]`. Nothing is
40278
+ * dropped here — `extra_paths` is not modeled, but it is a key the user may
40279
+ * legitimately want carried, so it passes through as any other unknown key
40280
+ * does, named by the unchecked-key warning.
40281
+ */
40282
+ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logger }) {
40283
+ mergeOverrideTable({
40284
+ settings,
40285
+ tableKey: EXTENSIONS_TABLE_KEY,
40286
+ override: extensionsOverride,
40287
+ knownKeys: DEEPAGENTS_EXTENSIONS_KEYS,
40288
+ warnAboutRelaxations: warnAboutExtensionsRelaxations,
40289
+ filePath,
40290
+ logger
40291
+ });
40292
+ }
40293
+ /**
40294
+ * Name the value an override key replaces, so a setting the user had turned
40295
+ * down is visible as such rather than reported as a bare new value.
40296
+ */
40297
+ function describeOverriddenValue({ key, value, previous }) {
40298
+ const previousValue = previous[key];
40299
+ return previousValue === void 0 || previousValue === value ? `${key} = ${JSON.stringify(value)}` : `${key} = ${JSON.stringify(value)} (was ${JSON.stringify(previousValue)})`;
40300
+ }
40301
+ /**
40302
+ * Warn when the `deepagents` extensions override widens what dcode loads on its
40303
+ * own, for the same reason the startup one does: this block is written into the
40304
+ * user's **global** config from a `.rulesync/permissions.jsonc` a repository
40305
+ * can carry.
40306
+ *
40307
+ * `trust` decides what happens to the Python in a checked-out project's
40308
+ * `.deepagents/extensions/` — `always` imports it into the agent process with
40309
+ * no prompt at all — and `enabled` is the switch above it. `never` and `false`
40310
+ * restrict, so neither is reported.
40311
+ */
40312
+ function warnAboutExtensionsRelaxations({ override, previous, filePath, logger }) {
40313
+ const relaxations = [];
40314
+ if (override.trust === "always") relaxations.push(describeOverriddenValue({
40315
+ key: "trust",
40316
+ value: "always",
40317
+ previous
40318
+ }));
40319
+ if (override.enabled === true && (previous.enabled ?? DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT) !== true) relaxations.push(describeOverriddenValue({
40320
+ key: "enabled",
40321
+ value: true,
40322
+ previous
40323
+ }));
40324
+ 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
40325
  }
40087
40326
  /**
40088
40327
  * Warn when the `deepagents` startup override relaxes what dcode may do on its
@@ -40097,12 +40336,13 @@ function mergeStartupOverride({ settings, startupOverride, filePath, logger }) {
40097
40336
  * process environment. The value being replaced is named alongside, so a
40098
40337
  * setting the user had turned down is visible as such.
40099
40338
  */
40100
- function warnAboutStartupRelaxations({ startupOverride, previousStartup, writtenKeys, filePath, logger }) {
40339
+ function warnAboutStartupRelaxations({ override: startupOverride, previous: previousStartup, filePath, logger }) {
40101
40340
  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
- };
40341
+ const describe = (key, value) => describeOverriddenValue({
40342
+ key,
40343
+ value,
40344
+ previous: previousStartup
40345
+ });
40106
40346
  const mode = startupOverride.mode;
40107
40347
  if (mode === "auto" || mode === "yolo") relaxations.push(describe("mode", mode));
40108
40348
  for (const [key, upstreamDefault] of Object.entries(DEEPAGENTS_STARTUP_BOOLEAN_DEFAULTS)) {
@@ -40112,9 +40352,6 @@ function warnAboutStartupRelaxations({ startupOverride, previousStartup, written
40112
40352
  }
40113
40353
  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
40354
  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
40355
  }
40119
40356
  /**
40120
40357
  * Read `[shell].allow_list` the way dcode does: a TOML array is taken element
@@ -54735,7 +54972,10 @@ var WarpSkill = class WarpSkill extends ToolSkill {
54735
54972
  //#region src/features/skills/zcode-skill.ts
54736
54973
  const ZcodeSkillFrontmatterSchema = z.looseObject({
54737
54974
  name: z.string(),
54738
- description: z.string()
54975
+ description: z.string(),
54976
+ when_to_use: z.optional(z.string()),
54977
+ license: z.optional(z.string()),
54978
+ metadata: z.optional(z.looseObject({}))
54739
54979
  });
54740
54980
  /**
54741
54981
  * Represents a ZCode skill directory.
@@ -54798,10 +55038,16 @@ var ZcodeSkill = class ZcodeSkill extends ToolSkill {
54798
55038
  }
54799
55039
  toRulesyncSkill() {
54800
55040
  const frontmatter = this.getFrontmatter();
55041
+ const zcodeSection = {
55042
+ ...frontmatter.when_to_use !== void 0 && { when_to_use: frontmatter.when_to_use },
55043
+ ...frontmatter.license !== void 0 && { license: frontmatter.license },
55044
+ ...frontmatter.metadata !== void 0 && { metadata: frontmatter.metadata }
55045
+ };
54801
55046
  const rulesyncFrontmatter = {
54802
55047
  name: frontmatter.name,
54803
55048
  description: frontmatter.description,
54804
- targets: ["*"]
55049
+ targets: ["*"],
55050
+ ...Object.keys(zcodeSection).length > 0 && { zcode: zcodeSection }
54805
55051
  };
54806
55052
  return new RulesyncSkill({
54807
55053
  outputRoot: this.outputRoot,
@@ -54816,9 +55062,21 @@ var ZcodeSkill = class ZcodeSkill extends ToolSkill {
54816
55062
  }
54817
55063
  static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
54818
55064
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
55065
+ const zcodeSection = rulesyncFrontmatter.zcode;
55066
+ const license = resolveLicense({
55067
+ rootFrontmatter: rulesyncFrontmatter,
55068
+ section: zcodeSection
55069
+ });
55070
+ const metadata = resolveMetadata({
55071
+ rootFrontmatter: rulesyncFrontmatter,
55072
+ section: zcodeSection
55073
+ });
54819
55074
  const zcodeFrontmatter = {
54820
55075
  name: rulesyncFrontmatter.name,
54821
- description: rulesyncFrontmatter.description
55076
+ description: rulesyncFrontmatter.description,
55077
+ ...zcodeSection?.when_to_use !== void 0 && { when_to_use: zcodeSection.when_to_use },
55078
+ ...license !== void 0 && { license },
55079
+ ...metadata !== void 0 && { metadata }
54822
55080
  };
54823
55081
  const settablePaths = ZcodeSkill.getSettablePaths({ global });
54824
55082
  return new ZcodeSkill({
@@ -62919,10 +63177,21 @@ var ClineRule = class ClineRule extends ToolRule {
62919
63177
  */
62920
63178
  const CodebuddyRuleFrontmatterSchema = z.object({
62921
63179
  description: z.optional(z.string()),
62922
- paths: z.optional(z.array(z.string())),
62923
- alwaysApply: z.optional(z.boolean())
63180
+ paths: z.optional(z.union([z.string(), z.array(z.string())])),
63181
+ alwaysApply: z.optional(z.boolean()),
63182
+ enabled: z.optional(z.boolean())
62924
63183
  });
62925
63184
  /**
63185
+ * Normalizes the documented `string` / `string[]` shapes of `paths` to the
63186
+ * list form the rest of the adapter works with. An empty list and an empty
63187
+ * string both mean "no paths".
63188
+ */
63189
+ function normalizeCodebuddyPaths(paths) {
63190
+ if (paths === void 0) return;
63191
+ const list = (typeof paths === "string" ? [paths] : paths).filter((path) => path.trim() !== "");
63192
+ return list.length > 0 ? list : void 0;
63193
+ }
63194
+ /**
62926
63195
  * A universal glob (matching everything) is redundant on an Always Apply
62927
63196
  * rule and, paired with `alwaysApply: true`, is the same semantic conflict
62928
63197
  * `CursorRule.resolveCursorGlobs` avoids for Cursor: `alwaysApply` already
@@ -62940,7 +63209,7 @@ const UNIVERSAL_PATHS = /* @__PURE__ */ new Set(["**/*", "*"]);
62940
63209
  * Rules format:
62941
63210
  * - {project}/CODEBUDDY.md (root: true), also read from {project}/.codebuddy/CODEBUDDY.md
62942
63211
  * - {project}/.codebuddy/rules/*.md (root: false, with optional
62943
- * `description` / `paths` / `alwaysApply` frontmatter)
63212
+ * `description` / `paths` / `alwaysApply` / `enabled` frontmatter)
62944
63213
  * - Global: ~/.codebuddy/CODEBUDDY.md and ~/.codebuddy/rules/*.md
62945
63214
  *
62946
63215
  * @see https://www.codebuddy.ai/docs/cli/memory
@@ -62982,9 +63251,10 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
62982
63251
  this.body = body;
62983
63252
  }
62984
63253
  static generateFileContent(body, frontmatter) {
62985
- if (frontmatter.description === void 0 && frontmatter.paths === void 0 && frontmatter.alwaysApply === void 0) return body;
63254
+ if (frontmatter.description === void 0 && frontmatter.paths === void 0 && frontmatter.alwaysApply === void 0 && frontmatter.enabled === void 0) return body;
62986
63255
  return stringifyFrontmatter(body, {
62987
63256
  description: frontmatter.description,
63257
+ enabled: frontmatter.enabled,
62988
63258
  alwaysApply: frontmatter.alwaysApply,
62989
63259
  paths: frontmatter.paths
62990
63260
  });
@@ -63032,10 +63302,36 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63032
63302
  root: isRoot
63033
63303
  });
63034
63304
  }
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;
63305
+ /**
63306
+ * Resolves the `paths` / `alwaysApply` pair against CodeBuddy's Rule Type
63307
+ * Determination table, which reads:
63308
+ *
63309
+ * | `alwaysApply` | `paths` | Rule type |
63310
+ * | ---------------- | --------- | ---------------------------------- |
63311
+ * | `true` (default) | any | ALWAYS — always injected |
63312
+ * | `false` | has value | MANUAL — triggered on matching file |
63313
+ * | `false` | none | not supported; the rule is dropped |
63314
+ *
63315
+ * The default being `true` is the opposite of Cursor, which the adapter was
63316
+ * modeled on: `paths` alone scopes nothing, so a rule meant to be path
63317
+ * triggered has to carry an explicit `alwaysApply: false` alongside it.
63318
+ *
63319
+ * @see https://www.codebuddy.ai/docs/cli/memory
63320
+ */
63321
+ static resolveCodebuddyRuleType({ paths, alwaysApply }) {
63322
+ const scopedPaths = paths && paths.length > 0 && !paths.every((path) => UNIVERSAL_PATHS.has(path.trim())) ? paths : void 0;
63323
+ if (alwaysApply === true) return {
63324
+ paths: scopedPaths,
63325
+ alwaysApply: true
63326
+ };
63327
+ if (scopedPaths === void 0) return {
63328
+ paths: void 0,
63329
+ alwaysApply: void 0
63330
+ };
63331
+ return {
63332
+ paths: scopedPaths,
63333
+ alwaysApply: false
63334
+ };
63039
63335
  }
63040
63336
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
63041
63337
  const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
@@ -63052,17 +63348,17 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63052
63348
  root
63053
63349
  });
63054
63350
  if (!paths.nonRoot) throw new Error(`nonRoot path is not set for ${rulesyncRule.getRelativeFilePath()}`);
63055
- const codebuddyPaths = rulesyncFrontmatter.codebuddy?.paths;
63351
+ const codebuddyPaths = normalizeCodebuddyPaths(rulesyncFrontmatter.codebuddy?.paths);
63056
63352
  const globs = rulesyncFrontmatter.globs;
63057
- const alwaysApply = rulesyncFrontmatter.codebuddy?.alwaysApply;
63058
- const pathsValue = CodebuddyRule.resolveCodebuddyPaths({
63353
+ const ruleType = CodebuddyRule.resolveCodebuddyRuleType({
63059
63354
  paths: codebuddyPaths ?? (globs?.length ? globs : void 0),
63060
- alwaysApply: alwaysApply === true
63355
+ alwaysApply: rulesyncFrontmatter.codebuddy?.alwaysApply
63061
63356
  });
63062
63357
  const codebuddyFrontmatter = {
63063
63358
  description: rulesyncFrontmatter.codebuddy?.description ?? rulesyncFrontmatter.description,
63064
- paths: pathsValue,
63065
- alwaysApply
63359
+ paths: ruleType.paths,
63360
+ alwaysApply: ruleType.alwaysApply,
63361
+ enabled: rulesyncFrontmatter.codebuddy?.enabled
63066
63362
  };
63067
63363
  return new CodebuddyRule({
63068
63364
  outputRoot,
@@ -63092,17 +63388,19 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
63092
63388
  validate: true
63093
63389
  });
63094
63390
  }
63095
- const isAlways = this.frontmatter.alwaysApply === true;
63096
- const sourcePaths = this.frontmatter.paths ?? [];
63391
+ const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
63392
+ const isAlways = this.frontmatter.alwaysApply !== false;
63097
63393
  const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
63394
+ const alwaysApply = this.frontmatter.alwaysApply ?? (sourcePaths.length > 0 ? true : void 0);
63098
63395
  const rulesyncFrontmatter = {
63099
63396
  targets,
63100
63397
  root: false,
63101
63398
  description: this.frontmatter.description,
63102
63399
  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,
63400
+ ...(this.frontmatter.paths !== void 0 || this.frontmatter.alwaysApply !== void 0 || this.frontmatter.enabled !== void 0 || this.frontmatter.description !== void 0) && { codebuddy: {
63401
+ paths: sourcePaths.length > 0 ? sourcePaths : void 0,
63402
+ alwaysApply,
63403
+ enabled: this.frontmatter.enabled,
63106
63404
  description: this.frontmatter.description
63107
63405
  } }
63108
63406
  };
@@ -65885,33 +66183,49 @@ var ReplitRule = class ReplitRule extends ToolRule {
65885
66183
  * Supports plain Markdown without frontmatter, mode-specific rules,
65886
66184
  * and both directory-based and single-file configurations.
65887
66185
  *
65888
- * - Project scope writes the non-root directory `.roo/rules/`.
66186
+ * - Project scope writes the non-root directory `.roo/rules/`, and the root
66187
+ * rule to the workspace-root `AGENTS.md` the extension reads from `cwd`.
65889
66188
  * - Global scope writes the same non-root directory resolved under the home
65890
- * directory (`~/.roo/rules/`), which Roo loads before workspace rules.
66189
+ * directory (`~/.roo/rules/`), which Roo loads before workspace rules. The
66190
+ * root rule joins it as `~/.roo/rules/AGENTS.md`: agent-rules discovery is
66191
+ * workspace-only, so the default project-scope root would resolve to a
66192
+ * `~/AGENTS.md` nothing ever reads.
65891
66193
  * @see https://roocodeinc.github.io/Roo-Code/features/custom-instructions
65892
66194
  */
65893
66195
  var RooRule = class RooRule extends ToolRule {
65894
- static getSettablePaths(_options = {}) {
65895
- return { nonRoot: { relativeDirPath: buildToolPath(ROO_DIR, "rules", _options.excludeToolDir) } };
66196
+ static getSettablePaths({ global, excludeToolDir } = {}) {
66197
+ const rulesDirPath = buildToolPath(ROO_DIR, "rules", excludeToolDir);
66198
+ if (global) return {
66199
+ root: {
66200
+ relativeDirPath: rulesDirPath,
66201
+ relativeFilePath: ROO_GLOBAL_ROOT_RULE_FILE_NAME
66202
+ },
66203
+ nonRoot: { relativeDirPath: rulesDirPath }
66204
+ };
66205
+ return { nonRoot: { relativeDirPath: rulesDirPath } };
65896
66206
  }
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;
66207
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
66208
+ const paths = this.getSettablePaths({ global });
66209
+ const relativeDirPath = overrideDirPath !== void 0 && RooRule.extractModeFromDirPath(overrideDirPath) !== void 0 ? overrideDirPath : paths.nonRoot?.relativeDirPath ?? buildToolPath(".roo", "rules");
65899
66210
  const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
66211
+ const isRoot = "root" in paths && relativeDirPath === paths.root.relativeDirPath && relativeFilePath === paths.root.relativeFilePath;
65900
66212
  return new RooRule({
65901
66213
  outputRoot,
65902
66214
  relativeDirPath,
65903
66215
  relativeFilePath,
65904
66216
  fileContent,
65905
66217
  validate,
65906
- root: false
66218
+ root: isRoot
65907
66219
  });
65908
66220
  }
65909
- static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true }) {
66221
+ static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
66222
+ const paths = this.getSettablePaths({ global });
65910
66223
  const params = this.buildToolRuleParamsDefault({
65911
66224
  outputRoot,
65912
66225
  rulesyncRule,
65913
66226
  validate,
65914
- nonRootPath: this.getSettablePaths().nonRoot
66227
+ ..."root" in paths && { rootPath: paths.root },
66228
+ nonRootPath: paths.nonRoot
65915
66229
  });
65916
66230
  const mode = rulesyncRule.getFrontmatter().roo?.mode;
65917
66231
  if (!params.root && mode !== void 0 && mode !== "") {
@@ -66834,7 +67148,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
66834
67148
  extension: "md",
66835
67149
  supportsGlobal: true,
66836
67150
  ruleDiscoveryMode: "auto",
66837
- collisionPolicy: "fold"
67151
+ collisionPolicy: "fold",
67152
+ localRootMode: "separate-local-file",
67153
+ localRootFileName: CRUSH_LOCAL_RULE_FILE_NAME
66838
67154
  }
66839
67155
  }],
66840
67156
  ["cursor", {
@@ -67554,6 +67870,34 @@ var RulesProcessor = class extends FeatureProcessor {
67554
67870
  root: true,
67555
67871
  localRoot
67556
67872
  });
67873
+ if (isClassOrSubclassOf({
67874
+ candidate: factory.class,
67875
+ base: CodebuddyRule
67876
+ })) {
67877
+ const paths = CodebuddyRule.getSettablePaths({ global: this.global });
67878
+ return new CodebuddyRule({
67879
+ outputRoot: this.outputRoot,
67880
+ relativeDirPath: relativeDirPath ?? paths.root.relativeDirPath,
67881
+ relativeFilePath: fileName,
67882
+ frontmatter: {},
67883
+ body,
67884
+ validate: true,
67885
+ root: true,
67886
+ localRoot
67887
+ });
67888
+ }
67889
+ if (isClassOrSubclassOf({
67890
+ candidate: factory.class,
67891
+ base: CrushRule
67892
+ })) return new CrushRule({
67893
+ outputRoot: this.outputRoot,
67894
+ relativeDirPath: relativeDirPath ?? ".",
67895
+ relativeFilePath: fileName,
67896
+ fileContent: body,
67897
+ validate: true,
67898
+ root: true,
67899
+ localRoot
67900
+ });
67557
67901
  if (isClassOrSubclassOf({
67558
67902
  candidate: factory.class,
67559
67903
  base: QwencodeRule
@@ -70152,6 +70496,6 @@ async function importChecksCore(params) {
70152
70496
  return writtenCount;
70153
70497
  }
70154
70498
  //#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 };
70499
+ 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
70500
 
70157
- //# sourceMappingURL=import-B6p6c3L0.js.map
70501
+ //# sourceMappingURL=import-BlHibyDS.js.map