rulesync 16.34.1 → 16.35.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.
@@ -457,6 +457,22 @@ const RULESYNC_PERMISSIONS_FILE_NAME = "permissions.jsonc";
457
457
  const RULESYNC_MCP_LEGACY_FILE_NAME = "mcp.json";
458
458
  const RULESYNC_HOOKS_LEGACY_FILE_NAME = "hooks.json";
459
459
  const RULESYNC_PERMISSIONS_LEGACY_FILE_NAME = "permissions.json";
460
+ const RULESYNC_MCP_DEPRECATED_DOTFILE_NAME = ".mcp.json";
461
+ const FEATURE_SOURCE_TREE_ENTRIES = {
462
+ rules: [RULES_FEATURE_SUBDIR],
463
+ commands: [COMMANDS_FEATURE_SUBDIR],
464
+ subagents: [SUBAGENTS_FEATURE_SUBDIR],
465
+ skills: [SKILLS_FEATURE_SUBDIR],
466
+ checks: [CHECKS_FEATURE_SUBDIR],
467
+ ignore: [RULESYNC_AIIGNORE_FILE_NAME],
468
+ mcp: [
469
+ RULESYNC_MCP_FILE_NAME,
470
+ RULESYNC_MCP_LEGACY_FILE_NAME,
471
+ RULESYNC_MCP_DEPRECATED_DOTFILE_NAME
472
+ ],
473
+ hooks: [RULESYNC_HOOKS_FILE_NAME, RULESYNC_HOOKS_LEGACY_FILE_NAME],
474
+ permissions: [RULESYNC_PERMISSIONS_FILE_NAME, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME]
475
+ };
460
476
  const RULESYNC_CONFIG_SCHEMA_URL = "https://github.com/dyoshikawa/rulesync/releases/latest/download/config-schema.json";
461
477
  const RULESYNC_MCP_SCHEMA_URL = "https://github.com/dyoshikawa/rulesync/releases/latest/download/mcp-schema.json";
462
478
  const RULESYNC_PERMISSIONS_SCHEMA_URL = "https://github.com/dyoshikawa/rulesync/releases/latest/download/permissions-schema.json";
@@ -499,6 +515,7 @@ const rulesProcessorToolTargetTuple = [
499
515
  "musecode",
500
516
  "opencode",
501
517
  "pi",
518
+ "pool",
502
519
  "qwencode",
503
520
  "reasonix",
504
521
  "replit",
@@ -512,7 +529,6 @@ const rulesProcessorToolTargetTuple = [
512
529
  "zcode",
513
530
  "zed",
514
531
  "zoocode",
515
- "pool",
516
532
  "dsh"
517
533
  ];
518
534
  const ignoreProcessorToolTargetTuple = [
@@ -4075,6 +4091,10 @@ const HookDefinitionSchema = z.looseObject({
4075
4091
  model: z.optional(safeString),
4076
4092
  args: z.optional(z.array(safeString)),
4077
4093
  metadata: z.optional(z.looseObject({})),
4094
+ source: z.optional(z.looseObject({
4095
+ source: safeString,
4096
+ ref: z.optional(safeString)
4097
+ })),
4078
4098
  if: z.optional(safeString),
4079
4099
  commandWindows: z.optional(safeString),
4080
4100
  additionalContextLimit: z.optional(z.int().check(nonnegative())),
@@ -4835,10 +4855,12 @@ const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONIC
4835
4855
  *
4836
4856
  * Bob reads `hooks` from `.bob/settings.json` (project) and
4837
4857
  * `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
4838
- * five events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse and
4839
- * Stop. Only `command` hooks exist, and `matcher` (a regex over the tool
4840
- * name) applies to the two tool events.
4858
+ * seven events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
4859
+ * PreCompact, PostCompact (both added by Bob Shell 2.0.3) and Stop. Handlers
4860
+ * are `command` or `https` hooks, and `matcher` (a regex over the tool name)
4861
+ * applies to the two tool events.
4841
4862
  *
4863
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
4842
4864
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
4843
4865
  */
4844
4866
  const BOB_HOOK_EVENTS = [
@@ -4846,6 +4868,8 @@ const BOB_HOOK_EVENTS = [
4846
4868
  "beforeSubmitPrompt",
4847
4869
  "preToolUse",
4848
4870
  "postToolUse",
4871
+ "preCompact",
4872
+ "postCompact",
4849
4873
  "stop"
4850
4874
  ];
4851
4875
  const CANONICAL_TO_BOB_EVENT_NAMES = {
@@ -4853,6 +4877,8 @@ const CANONICAL_TO_BOB_EVENT_NAMES = {
4853
4877
  beforeSubmitPrompt: "UserPromptSubmit",
4854
4878
  preToolUse: "PreToolUse",
4855
4879
  postToolUse: "PostToolUse",
4880
+ preCompact: "PreCompact",
4881
+ postCompact: "PostCompact",
4856
4882
  stop: "Stop"
4857
4883
  };
4858
4884
  const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
@@ -6356,7 +6382,7 @@ var RulesyncMcp = class RulesyncMcp extends RulesyncFile {
6356
6382
  relativeFilePath: RULESYNC_MCP_LEGACY_FILE_NAME
6357
6383
  }, {
6358
6384
  relativeDirPath: RULESYNC_RELATIVE_DIR_PATH,
6359
- relativeFilePath: ".mcp.json"
6385
+ relativeFilePath: RULESYNC_MCP_DEPRECATED_DOTFILE_NAME
6360
6386
  }]
6361
6387
  };
6362
6388
  }
@@ -7264,13 +7290,30 @@ const DeepagentsExtensionsOverrideSchema = z.looseObject({
7264
7290
  ]))
7265
7291
  });
7266
7292
  /**
7293
+ * The `[mcp]` table of `~/.deepagents/config.toml`, where dcode keeps its
7294
+ * per-server trust for the MCP servers a project's `.mcp.json` /
7295
+ * `.deepagents/.mcp.json` declares. dcode reads these lists from the user-level
7296
+ * file only — never from a project file — which is exactly why the global
7297
+ * permissions adapter is the place to author them.
7298
+ *
7299
+ * `disabled_project_servers` names project MCP servers dcode always rejects;
7300
+ * a rejection wins over any approval or trust. The sibling
7301
+ * `enabled_project_server_approvals` is the fingerprint-bound approval store
7302
+ * the interactive prompt fills in, and `enabled_project_servers` is a
7303
+ * deprecated flat allowlist dcode ignores, so neither has a key here.
7304
+ *
7305
+ * @see https://docs.langchain.com/oss/deepagents/code/configuration
7306
+ */
7307
+ const DeepagentsMcpOverrideSchema = z.looseObject({ disabled_project_servers: z.optional(z.array(z.string())) });
7308
+ /**
7267
7309
  * The `[shell].allow_list` array itself is rulesync-owned, driven by the
7268
7310
  * shared `permission.bash` block, so it has no key here.
7269
7311
  */
7270
7312
  const DeepagentsPermissionsOverrideSchema = z.looseObject({
7271
7313
  permission: z.optional(ToolScopedPermissionSchema),
7272
7314
  startup: z.optional(DeepagentsStartupOverrideSchema),
7273
- extensions: z.optional(DeepagentsExtensionsOverrideSchema)
7315
+ extensions: z.optional(DeepagentsExtensionsOverrideSchema),
7316
+ mcp: z.optional(DeepagentsMcpOverrideSchema)
7274
7317
  });
7275
7318
  /**
7276
7319
  * The actions Junie's allowlist accepts. Verified against the shipped Junie
@@ -9584,7 +9627,13 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
9584
9627
  "user-invocable": z.optional(z.boolean()),
9585
9628
  "allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
9586
9629
  })),
9587
- commandcode: z.optional(z.looseObject({})),
9630
+ commandcode: z.optional(z.looseObject({
9631
+ license: z.optional(z.string()),
9632
+ compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
9633
+ metadata: z.optional(z.looseObject({})),
9634
+ "disable-model-invocation": z.optional(z.boolean()),
9635
+ "user-invocable": z.optional(z.boolean())
9636
+ })),
9588
9637
  cortexcode: z.optional(z.looseObject({})),
9589
9638
  continue: z.optional(z.looseObject({})),
9590
9639
  tabnine: z.optional(z.looseObject({})),
@@ -9828,7 +9877,7 @@ async function getLocalSkillDirNames(sourceTree) {
9828
9877
  *
9829
9878
  * The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
9830
9879
  * default that applies to every tool supporting the flag (claudecode, copilot,
9831
- * copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh). Each tool's own section may override that
9880
+ * copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that
9832
9881
  * default with a per-target value. A defined section value (including `false`)
9833
9882
  * always wins over the root default.
9834
9883
  *
@@ -9847,7 +9896,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
9847
9896
  *
9848
9897
  * The rulesync skill frontmatter exposes a root-level `user-invocable` default
9849
9898
  * that applies to every tool supporting the flag (claudecode, copilot,
9850
- * copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh). Each tool's own section may override that default with a
9899
+ * copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that default with a
9851
9900
  * per-target value. A defined section value (including `false`) always wins
9852
9901
  * over the root default.
9853
9902
  *
@@ -15415,6 +15464,7 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
15415
15464
  const COMMANDCODE_DIR = ".commandcode";
15416
15465
  const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
15417
15466
  const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
15467
+ const COMMANDCODE_SETTINGS_LOCAL_FILE_NAME = "settings.local.json";
15418
15468
  const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
15419
15469
  const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
15420
15470
  const COMMANDCODE_COMMANDS_DIR_PATH = join(COMMANDCODE_DIR, "commands");
@@ -20629,6 +20679,18 @@ function isToolMatcherEntry(x) {
20629
20679
  return true;
20630
20680
  }
20631
20681
  /**
20682
+ * Convert a canonical `timeout` (seconds) into the tool's unit.
20683
+ */
20684
+ function emitTimeout({ timeout, converterConfig }) {
20685
+ return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? Math.round(timeout * 1e3) : timeout;
20686
+ }
20687
+ /**
20688
+ * Convert a tool `timeout` back into the canonical unit (seconds).
20689
+ */
20690
+ function importTimeout({ timeout, converterConfig }) {
20691
+ return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? timeout / 1e3 : timeout;
20692
+ }
20693
+ /**
20632
20694
  * Filter the shared canonical hooks to the supported events and merge tool overrides on top.
20633
20695
  */
20634
20696
  function buildEffectiveHooks$1({ config, toolOverrideHooks, supportedEvents }) {
@@ -20769,6 +20831,7 @@ const isEmittableString = ({ value }) => isNonEmptyString(value);
20769
20831
  const isEmittableArray = ({ value }) => isStringArray$1(value);
20770
20832
  const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
20771
20833
  const isImportableArray = ({ value }) => isSafeStringArray(value);
20834
+ const isEmittableObject = ({ value }) => isPlainObject$1(value);
20772
20835
  /**
20773
20836
  * The canonical schema of one hook field, looked up by name. Read off the
20774
20837
  * schema's own shape rather than by parsing a one-field object, so a `canonical`
@@ -20803,11 +20866,17 @@ const isImportableNumber = ({ value, canonical }) => Number.isFinite(value) && s
20803
20866
  value,
20804
20867
  canonical
20805
20868
  });
20869
+ const isImportableObject = ({ value, canonical }) => isPlainObject$1(value) && satisfiesCanonicalField({
20870
+ value,
20871
+ canonical
20872
+ });
20806
20873
  /**
20807
20874
  * Say which rule the value broke, so the warning names the actual constraint
20808
20875
  * rather than asserting a canonical rejection that may not be the reason. A
20809
20876
  * closed enum lists its members; a rule carrying its own message (the
20810
- * control-character check behind `safeString`) reuses it.
20877
+ * control-character check behind `safeString`) reuses it. An object field
20878
+ * names the offending key, since zod's generic "Invalid input" would not tell
20879
+ * the reader which key is missing or malformed.
20811
20880
  */
20812
20881
  function describeScalarConstraint({ canonical, value }) {
20813
20882
  const schema = canonicalFieldSchema(canonical);
@@ -20815,13 +20884,22 @@ function describeScalarConstraint({ canonical, value }) {
20815
20884
  if (result === void 0 || result.success) return `it is not a value this field carries through.`;
20816
20885
  const issue = result.error.issues[0];
20817
20886
  if (issue === void 0) return `it is not a value the canonical "${canonical}" field accepts.`;
20818
- return `it does not satisfy the canonical "${canonical}" field: ${issue.message}.`;
20887
+ return `it does not satisfy the canonical "${canonical}" field${issue.path.length > 0 ? ` at ${quoteValueForWarning(issue.path.map(String).join("."))}` : ""}: ${issue.message}.`;
20819
20888
  }
20820
20889
  const describeInvalidScalar = ({ tool, canonical, value }) => `Dropping "${tool}" (${quoteValueForWarning(value)}) while importing a hook: ${describeScalarConstraint({
20821
20890
  canonical,
20822
20891
  value
20823
20892
  })} Importing it would fail validation on the next run.`;
20824
20893
  const describeInvalidArray = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a list of strings without newline, carriage return or NUL characters.`;
20894
+ /**
20895
+ * An object is quoted by its offending key rather than its value: the payload
20896
+ * can be arbitrarily large, and the missing or malformed key is what the
20897
+ * reader needs to fix.
20898
+ */
20899
+ const describeInvalidObject = ({ tool, canonical, value }) => isPlainObject$1(value) ? `Dropping "${tool}" while importing a hook: ${describeScalarConstraint({
20900
+ canonical,
20901
+ value
20902
+ })} Importing it would fail validation on the next run.` : `Dropping "${tool}" while importing a hook: it must be an object.`;
20825
20903
  const describeInvalidRecord = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a map of strings whose keys are non-empty and free of "=", and whose keys and values carry no newline, carriage return or NUL characters.`;
20826
20904
  /**
20827
20905
  * Check a value against the shape its field documents. A string field also
@@ -20927,6 +21005,14 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
20927
21005
  fields: converterConfig.recordPassthroughFields ?? [],
20928
21006
  isValid: isEmittableRecord,
20929
21007
  warn
21008
+ }),
21009
+ ...emitPassthroughFields({
21010
+ def,
21011
+ hookType,
21012
+ eventName,
21013
+ fields: converterConfig.objectPassthroughFields ?? [],
21014
+ isValid: isEmittableObject,
21015
+ warn
20930
21016
  })
20931
21017
  };
20932
21018
  }
@@ -20950,9 +21036,12 @@ function buildToolHooks({ defs, eventName, converterConfig, warn }) {
20950
21036
  converterConfig,
20951
21037
  warn
20952
21038
  }),
20953
- type: hookType,
21039
+ type: converterConfig.hookTypeNames?.[hookType] ?? hookType,
20954
21040
  ...command !== void 0 && command !== null && { command },
20955
- ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
21041
+ ...def.timeout !== void 0 && def.timeout !== null && { timeout: emitTimeout({
21042
+ timeout: def.timeout,
21043
+ converterConfig
21044
+ }) },
20956
21045
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
20957
21046
  ...emitTypePayloadFields({
20958
21047
  def,
@@ -21074,6 +21163,20 @@ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
21074
21163
  function isImportedHookType(value) {
21075
21164
  return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
21076
21165
  }
21166
+ /**
21167
+ * The canonical hook type a tool-side `type` value denotes: the tool's own
21168
+ * spelling from {@link ToolHooksConverterConfig.hookTypeNames} when it has
21169
+ * one, otherwise the value itself when it is a canonical type, and `command`
21170
+ * for anything else (the same coercion as before the renames existed).
21171
+ */
21172
+ function resolveImportedHookType({ value, converterConfig }) {
21173
+ const names = converterConfig.hookTypeNames;
21174
+ if (names !== void 0 && typeof value === "string") {
21175
+ for (const [canonical, tool] of Object.entries(names)) if (tool === value) return canonical;
21176
+ if (Object.hasOwn(names, value)) return "command";
21177
+ }
21178
+ return isImportedHookType(value) ? value : "command";
21179
+ }
21077
21180
  function isStringRecord(value) {
21078
21181
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
21079
21182
  return Object.values(value).every((v) => typeof v === "string");
@@ -21239,6 +21342,14 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21239
21342
  isValid: isEmittableRecord,
21240
21343
  describeInvalid: describeInvalidRecord,
21241
21344
  warn
21345
+ }),
21346
+ ...importPassthroughFields({
21347
+ h,
21348
+ hookType,
21349
+ fields: converterConfig.objectPassthroughFields ?? [],
21350
+ isValid: isImportableObject,
21351
+ describeInvalid: describeInvalidObject,
21352
+ warn
21242
21353
  })
21243
21354
  };
21244
21355
  }
@@ -21246,7 +21357,10 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21246
21357
  * Convert a single tool hook record into a canonical hook definition.
21247
21358
  */
21248
21359
  function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21249
- const hookType = isImportedHookType(h.type) ? h.type : "command";
21360
+ const hookType = resolveImportedHookType({
21361
+ value: h.type,
21362
+ converterConfig
21363
+ });
21250
21364
  const command = importCanonicalString({
21251
21365
  value: stripCommandPrefix({
21252
21366
  command: h.command,
@@ -21255,7 +21369,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21255
21369
  canonical: "command",
21256
21370
  warn
21257
21371
  });
21258
- const timeout = typeof h.timeout === "number" ? h.timeout : void 0;
21372
+ const timeout = typeof h.timeout === "number" ? importTimeout({
21373
+ timeout: h.timeout,
21374
+ converterConfig
21375
+ }) : void 0;
21259
21376
  const prompt = importCanonicalString({
21260
21377
  value: h.prompt,
21261
21378
  canonical: "prompt",
@@ -21388,7 +21505,10 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
21388
21505
  const skipReason = describeHookSkipReason({
21389
21506
  h,
21390
21507
  rawEntry,
21391
- hookType: isImportedHookType(h.type) ? h.type : "command",
21508
+ hookType: resolveImportedHookType({
21509
+ value: h.type,
21510
+ converterConfig
21511
+ }),
21392
21512
  converterConfig
21393
21513
  });
21394
21514
  if (skipReason !== void 0) {
@@ -21873,6 +21993,7 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
21873
21993
  "beforeSubmitPrompt"
21874
21994
  ]),
21875
21995
  supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
21996
+ timeoutUnit: "milliseconds",
21876
21997
  arrayPassthroughFields: [{
21877
21998
  canonical: "args",
21878
21999
  tool: "args"
@@ -22471,9 +22592,12 @@ const BOB_SPEC = {
22471
22592
  noMatcherEvents: /* @__PURE__ */ new Set([
22472
22593
  "sessionStart",
22473
22594
  "beforeSubmitPrompt",
22595
+ "preCompact",
22596
+ "postCompact",
22474
22597
  "stop"
22475
22598
  ]),
22476
- supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
22599
+ supportedHookTypes: /* @__PURE__ */ new Set(["command", "http"]),
22600
+ hookTypeNames: { http: "https" },
22477
22601
  wildcardMatcherMeansAll: true
22478
22602
  }
22479
22603
  };
@@ -22484,10 +22608,13 @@ const BOB_SPEC = {
22484
22608
  * `<project>/.bob/settings.json` (project scope) and
22485
22609
  * `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
22486
22610
  * `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
22487
- * "command": "...", "timeout"?: <seconds> }] }] }`. The file also holds
22488
- * settings rulesync does not own, so generation merges the `hooks` key into it
22489
- * (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting the file.
22611
+ * "command": "...", "timeout"?: <seconds> }] }] }`, where a handler may
22612
+ * instead be `{ "type": "https", "url": "...", "timeout"?: <seconds> }`. The
22613
+ * file also holds settings rulesync does not own, so generation merges the
22614
+ * `hooks` key into it (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting
22615
+ * the file.
22490
22616
  *
22617
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
22491
22618
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
22492
22619
  */
22493
22620
  var BobHooks = class extends SettingsJsonHooks {
@@ -23917,6 +24044,11 @@ const CORTEXCODE_SPEC = {
23917
24044
  booleanPassthroughFields: [{
23918
24045
  canonical: "enabled",
23919
24046
  tool: "enabled"
24047
+ }],
24048
+ objectPassthroughFields: [{
24049
+ canonical: "source",
24050
+ tool: "source",
24051
+ commandOnly: true
23920
24052
  }]
23921
24053
  }
23922
24054
  };
@@ -23927,8 +24059,9 @@ const CORTEXCODE_SPEC = {
23927
24059
  * (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
23928
24060
  * scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
23929
24061
  * "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
23930
- * <seconds>, "enabled"?: <boolean> }] }] }`. The project file also holds
23931
- * settings rulesync does not own, so generation merges the `hooks` key into
24062
+ * <seconds>, "enabled"?: <boolean>, "source"?: { "source", "ref"? } }] }] }`.
24063
+ * The project file also holds settings rulesync does not own, so generation
24064
+ * merges the `hooks` key into
23932
24065
  * either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
23933
24066
  * neither file is removed wholesale (hooks.json sits in the CLI-owned
23934
24067
  * `~/.snowflake/cortex/` tree).
@@ -28574,7 +28707,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
28574
28707
  supportsImport: true
28575
28708
  },
28576
28709
  supportedEvents: BOB_HOOK_EVENTS,
28577
- supportedHookTypes: ["command"],
28710
+ supportedHookTypes: ["command", "http"],
28578
28711
  supportsMatcher: true,
28579
28712
  matcherEvents: ["preToolUse", "postToolUse"]
28580
28713
  }],
@@ -31388,6 +31521,50 @@ function parseBobMcpConfig({ fileContent, relativePath }) {
31388
31521
  /** The `type` Bob IDE writes for a streamable HTTP server. */
31389
31522
  const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
31390
31523
  /**
31524
+ * The OAuth keys Bob reads flat on a server entry, next to `oauth` (a
31525
+ * boolean: `true` forces the flow on, `false` off, absent lets Bob detect it).
31526
+ * The canonical shape is Claude Code's nested `oauth: { clientId, ... }`
31527
+ * object, so the two are translated in both directions.
31528
+ * @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-oauth
31529
+ */
31530
+ const BOB_FLAT_OAUTH_KEYS = [
31531
+ "clientId",
31532
+ "clientSecret",
31533
+ "scope"
31534
+ ];
31535
+ /**
31536
+ * Flatten the canonical `oauth` object onto a Bob server entry: `oauth: true`
31537
+ * plus the string-valued keys Bob documents. A flat key the server already
31538
+ * carries (authored in Bob's own spelling) is kept over the nested one. Other
31539
+ * nested keys (`callbackPort`, `redirectUri`) have no Bob counterpart and are
31540
+ * dropped rather than nested under a key Bob reads as a boolean.
31541
+ */
31542
+ function flattenOauthForBob({ oauth, converted }) {
31543
+ converted.oauth = true;
31544
+ for (const key of BOB_FLAT_OAUTH_KEYS) {
31545
+ const value = oauth[key];
31546
+ if (typeof value === "string" && converted[key] === void 0) converted[key] = value;
31547
+ }
31548
+ }
31549
+ /**
31550
+ * Reverse of {@link flattenOauthForBob}: gather Bob's flat OAuth keys into the
31551
+ * canonical `oauth` object. `oauth: true` always folds (even with no keys, so
31552
+ * the explicit enable survives the round-trip as an empty object); with no
31553
+ * `oauth` key at all the fold happens only when a flat key is present, since
31554
+ * that is what makes the entry an OAuth one for Bob; `oauth: false` and
31555
+ * anything else leave the entry as it is.
31556
+ */
31557
+ function nestOauthFromBob(converted) {
31558
+ const flat = BOB_FLAT_OAUTH_KEYS.filter((key) => typeof converted[key] === "string");
31559
+ if (!(converted.oauth === true || converted.oauth === void 0 && flat.length > 0)) return;
31560
+ const oauth = {};
31561
+ for (const key of flat) {
31562
+ oauth[key] = converted[key];
31563
+ delete converted[key];
31564
+ }
31565
+ converted.oauth = oauth;
31566
+ }
31567
+ /**
31391
31568
  * The remote transport Bob IDE reads a server as, spelled the way it writes
31392
31569
  * it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
31393
31570
  * `url` with no `type`. `http` is the canonical rulesync alias for streamable
@@ -31410,7 +31587,9 @@ function asBobRemoteType(stated, url) {
31410
31587
  * `headers`, `timeout`, `alwaysAllow` and `disabled` pass through, as Bob
31411
31588
  * documents all of them, with prototype-pollution keys dropped at every
31412
31589
  * nesting level (Bob spreads `env` and `headers` into the process environment
31413
- * and the HTTP requests).
31590
+ * and the HTTP requests). The canonical `oauth` object is flattened onto the
31591
+ * entry as `oauth: true` plus `clientId` / `clientSecret` / `scope`, the keys
31592
+ * Bob reads; a boolean `oauth` passes through as Bob's own on/off switch.
31414
31593
  *
31415
31594
  * Bob Shell documents the same file with an `httpURL` key instead of
31416
31595
  * `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
@@ -31476,10 +31655,16 @@ function convertToBobFormat(mcpServers, logger) {
31476
31655
  converted.command = command;
31477
31656
  if (args.length > 0) converted.args = args;
31478
31657
  }
31479
- for (const [key, value] of Object.entries(rest)) {
31658
+ const { oauth, ...passthrough } = rest;
31659
+ for (const [key, value] of Object.entries(passthrough)) {
31480
31660
  if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
31481
31661
  converted[key] = omitPrototypePollutionKeysDeep(value);
31482
31662
  }
31663
+ if (isRecord$1(oauth)) flattenOauthForBob({
31664
+ oauth,
31665
+ converted
31666
+ });
31667
+ else if (oauth !== void 0) converted.oauth = oauth;
31483
31668
  result[serverName] = converted;
31484
31669
  }
31485
31670
  return result;
@@ -31489,9 +31674,10 @@ function convertToBobFormat(mcpServers, logger) {
31489
31674
  * (`type: "streamable-http"` + `url`) is already canonical and passes through;
31490
31675
  * the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
31491
31676
  * `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
31492
- * on the next generate. Every other field passes through with its
31493
- * prototype-pollution keys dropped at every nesting level, mirroring the
31494
- * generate side's handling of `env` / `headers`.
31677
+ * on the next generate. Bob's flat OAuth keys are gathered back into the
31678
+ * canonical `oauth` object (see {@link nestOauthFromBob}). Every other field
31679
+ * passes through with its prototype-pollution keys dropped at every nesting
31680
+ * level, mirroring the generate side's handling of `env` / `headers`.
31495
31681
  */
31496
31682
  function convertFromBobFormat(mcpServers) {
31497
31683
  if (!isMcpServers(mcpServers)) return {};
@@ -31512,6 +31698,7 @@ function convertFromBobFormat(mcpServers) {
31512
31698
  converted.url = httpURL;
31513
31699
  converted.type = "http";
31514
31700
  } else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
31701
+ nestOauthFromBob(converted);
31515
31702
  result[serverName] = converted;
31516
31703
  }
31517
31704
  return result;
@@ -32536,7 +32723,10 @@ function asContinueRemoteType(stated, url) {
32536
32723
  * file with a strict union — one server that matches neither shape (an
32537
32724
  * unknown transport, a non-string `env` value) makes Continue drop every
32538
32725
  * server in the file — so only the documented keys are emitted and anything
32539
- * else (`timeout`, `oauth`, rulesync-only fields) is left out. `envFile` is
32726
+ * else is left out. The canonical keys a user would expect to take effect
32727
+ * (`cwd`, `timeout`, `oauth`) are dropped with a warning, since the JSON
32728
+ * schema has no place for them (the YAML block form does, but Rulesync writes
32729
+ * the JSON file); rulesync-only fields are dropped silently. `envFile` is
32540
32730
  * accepted by Continue's schema but ignored with a warning at load time, so
32541
32731
  * it is dropped here with a warning instead of being written as if it worked.
32542
32732
  *
@@ -32572,6 +32762,17 @@ function convertToContinueFormat(mcpServers, logger) {
32572
32762
  }
32573
32763
  return result;
32574
32764
  }
32765
+ /**
32766
+ * Warn for each canonical key Continue's JSON MCP schema cannot carry, so a
32767
+ * dropped `cwd`, `timeout` or `oauth` does not go unnoticed in a config that
32768
+ * otherwise looks complete.
32769
+ */
32770
+ function warnUnsupportedContinueKeys({ serverName, serverConfig, keys, logger }) {
32771
+ for (const key of keys) {
32772
+ if (serverConfig[key] === void 0) continue;
32773
+ logger?.warn(`Continue's MCP config has no "${key}" field, so the ${key} of server ${quoteValueForWarning(serverName)} was not written.`);
32774
+ }
32775
+ }
32575
32776
  function convertRemoteServer({ serverName, serverConfig, logger }) {
32576
32777
  const url = resolveRemoteMcpUrl(serverConfig);
32577
32778
  if (!url) {
@@ -32599,6 +32800,12 @@ function convertRemoteServer({ serverName, serverConfig, logger }) {
32599
32800
  url
32600
32801
  };
32601
32802
  if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
32803
+ warnUnsupportedContinueKeys({
32804
+ serverName,
32805
+ serverConfig,
32806
+ keys: ["timeout", "oauth"],
32807
+ logger
32808
+ });
32602
32809
  return converted;
32603
32810
  }
32604
32811
  function convertStdioServer({ serverName, serverConfig, logger }) {
@@ -32619,6 +32826,12 @@ function convertStdioServer({ serverName, serverConfig, logger }) {
32619
32826
  if (args.length > 0) converted.args = args;
32620
32827
  if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
32621
32828
  if (typeof serverConfig.envFile === "string") logger?.warn(`Continue ignores "envFile" for MCP servers, so the envFile of server ${quoteValueForWarning(serverName)} was not written; put the variables in "env" instead.`);
32829
+ warnUnsupportedContinueKeys({
32830
+ serverName,
32831
+ serverConfig,
32832
+ keys: ["cwd", "timeout"],
32833
+ logger
32834
+ });
32622
32835
  return converted;
32623
32836
  }
32624
32837
  /**
@@ -45117,6 +45330,27 @@ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
45117
45330
  * @see https://github.com/langchain-ai/deepagents `config_manifest.py`
45118
45331
  */
45119
45332
  const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
45333
+ /**
45334
+ * `[mcp]` holds dcode's per-server trust for the MCP servers a project's
45335
+ * `.mcp.json` / `.deepagents/.mcp.json` declares. `load_mcp_server_trust_lists`
45336
+ * reads it from the user-level `config.toml` only — never from a project file —
45337
+ * so the global adapter is the one place rulesync can author it.
45338
+ */
45339
+ const MCP_TABLE_KEY = "mcp";
45340
+ /**
45341
+ * Keys lifted back into the override on import. `disabled_project_servers` is
45342
+ * the deny list: a name on it is always rejected, over any approval or trust.
45343
+ */
45344
+ const DEEPAGENTS_MCP_KEYS = ["disabled_project_servers"];
45345
+ const MCP_DISABLED_PROJECT_SERVERS_KEY = "disabled_project_servers";
45346
+ /**
45347
+ * dcode's own store of the project servers the user approved at the prompt,
45348
+ * each bound to a repository or worktree and a definition fingerprint. It is
45349
+ * machine-local state and a way to pre-approve a project's servers, so a
45350
+ * repository's `.rulesync/permissions.jsonc` must not reach it: generate drops
45351
+ * the key here, and import never lifts it (`DEEPAGENTS_MCP_KEYS`).
45352
+ */
45353
+ const MCP_APPROVALS_KEY = "enabled_project_server_approvals";
45120
45354
  const ALLOW_ALL_SENTINEL = "all";
45121
45355
  const RECOMMENDED_SENTINEL = "recommended";
45122
45356
  const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
@@ -45189,7 +45423,9 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
45189
45423
  * lifted back into the override on import. The same override carries
45190
45424
  * `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
45191
45425
  * which decide whether the Python in a checked-out project's
45192
- * `.deepagents/extensions/` is imported into the agent process.
45426
+ * `.deepagents/extensions/` is imported into the agent process, and
45427
+ * `[mcp].disabled_project_servers`, the names of project MCP servers dcode
45428
+ * always rejects.
45193
45429
  *
45194
45430
  * On **import** the allowlist comes back as `bash` `allow` rules named by the
45195
45431
  * executable — skipping any entry dcode could not match in the first place, so
@@ -45287,17 +45523,9 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45287
45523
  if (Object.keys(shell).length > 0) settings[SHELL_TABLE_KEY] = shell;
45288
45524
  else delete settings[SHELL_TABLE_KEY];
45289
45525
  }
45290
- const startupOverride = config.deepagents?.startup;
45291
- if (isPlainObject$1(startupOverride) && Object.keys(startupOverride).length > 0) mergeStartupOverride({
45292
- settings,
45293
- startupOverride,
45294
- filePath,
45295
- logger
45296
- });
45297
- const extensionsOverride = config.deepagents?.extensions;
45298
- if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
45526
+ mergeDeepagentsOverrides({
45299
45527
  settings,
45300
- extensionsOverride,
45528
+ override: config.deepagents,
45301
45529
  filePath,
45302
45530
  logger
45303
45531
  });
@@ -45329,10 +45557,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45329
45557
  extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
45330
45558
  selfPath
45331
45559
  });
45560
+ const mcpOverride = liftMcpOverride({
45561
+ mcp: isPlainObject$1(settings[MCP_TABLE_KEY]) ? settings[MCP_TABLE_KEY] : {},
45562
+ selfPath
45563
+ });
45332
45564
  const result = { ...config };
45333
45565
  const deepagents = {};
45334
45566
  if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
45335
45567
  if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
45568
+ if (Object.keys(mcpOverride).length > 0) deepagents.mcp = mcpOverride;
45336
45569
  if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
45337
45570
  return RulesyncPermissions.fromImportedFileContent({
45338
45571
  outputRoot: getDeepagentsRulesyncOutputRoot({
@@ -45440,7 +45673,7 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
45440
45673
  * read — a wrapper rather than the bare value, so a legitimately falsy setting
45441
45674
  * is not mistaken for a rejection.
45442
45675
  */
45443
- function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
45676
+ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath, describeRejection = `deepagents-cli falls back to its own default for a '[${tableKey}]' value it cannot read` }) {
45444
45677
  const override = {};
45445
45678
  const rejected = [];
45446
45679
  for (const key of keys) {
@@ -45453,7 +45686,7 @@ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
45453
45686
  if (normalized) override[key] = normalized.value;
45454
45687
  else rejected.push(`${key} = ${JSON.stringify(value)}`);
45455
45688
  }
45456
- 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.`);
45689
+ if (rejected.length > 0) warnWithFallback(void 0, `${describeRejection}, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
45457
45690
  return override;
45458
45691
  }
45459
45692
  /**
@@ -45501,6 +45734,37 @@ function liftExtensionsOverride({ extensions, selfPath }) {
45501
45734
  });
45502
45735
  }
45503
45736
  /**
45737
+ * Read a `[mcp]` name list the way `_toml_str_list` reads it: a bare string
45738
+ * is split on commas, names are trimmed and empty ones dropped, and a
45739
+ * non-string element of a list is dropped while the names around it survive.
45740
+ * Anything else (a number, a table, a bool) is a value dcode cannot read as
45741
+ * names at all, so `null` says to leave it behind.
45742
+ */
45743
+ function readMcpServerNames(value) {
45744
+ if (typeof value === "string") return value.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
45745
+ if (!Array.isArray(value)) return null;
45746
+ return value.filter((name) => typeof name === "string").map((name) => name.trim()).filter((name) => name.length > 0);
45747
+ }
45748
+ /**
45749
+ * Lift the `[mcp]` keys rulesync models back into the `deepagents` override.
45750
+ * The deny list comes back as the array dcode resolves it to, so the
45751
+ * comma-separated string form it also accepts does not survive as one bogus
45752
+ * name in a permissions file the canonical schema types as `string[]`.
45753
+ */
45754
+ function liftMcpOverride({ mcp, selfPath }) {
45755
+ return liftOverrideTable({
45756
+ table: mcp,
45757
+ tableKey: MCP_TABLE_KEY,
45758
+ keys: DEEPAGENTS_MCP_KEYS,
45759
+ normalize: ({ value }) => {
45760
+ const names = readMcpServerNames(value);
45761
+ return names ? { value: names } : null;
45762
+ },
45763
+ selfPath,
45764
+ describeRejection: `deepagents-cli treats a '[${MCP_TABLE_KEY}]' value it cannot read as a broken trust policy and rejects every project MCP server until it is fixed`
45765
+ });
45766
+ }
45767
+ /**
45504
45768
  * Merge one block of the `deepagents` override into its `config.toml` table,
45505
45769
  * preserving every other key of that table. A table that is not a table at all
45506
45770
  * is something rulesync did not write and cannot merge into, so it is left
@@ -45549,6 +45813,38 @@ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, lo
45549
45813
  if (unknownKeys.length === 0) return;
45550
45814
  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.`);
45551
45815
  }
45816
+ /** An override block with something in it to write. */
45817
+ function isSet(block) {
45818
+ return isPlainObject$1(block) && Object.keys(block).length > 0;
45819
+ }
45820
+ /**
45821
+ * Merge each block of the `deepagents` override into its `config.toml` table.
45822
+ * A block that is absent, not a table, or empty has nothing to write and is
45823
+ * skipped without touching the table it would have gone into.
45824
+ */
45825
+ function mergeDeepagentsOverrides({ settings, override, filePath, logger }) {
45826
+ const startupOverride = override?.startup;
45827
+ if (isSet(startupOverride)) mergeStartupOverride({
45828
+ settings,
45829
+ startupOverride,
45830
+ filePath,
45831
+ logger
45832
+ });
45833
+ const extensionsOverride = override?.extensions;
45834
+ if (isSet(extensionsOverride)) mergeExtensionsOverride({
45835
+ settings,
45836
+ extensionsOverride,
45837
+ filePath,
45838
+ logger
45839
+ });
45840
+ const mcpOverride = override?.mcp;
45841
+ if (isSet(mcpOverride)) mergeMcpOverride({
45842
+ settings,
45843
+ mcpOverride,
45844
+ filePath,
45845
+ logger
45846
+ });
45847
+ }
45552
45848
  /**
45553
45849
  * Merge the `deepagents.startup` override into `[startup]`.
45554
45850
  */
@@ -45582,6 +45878,40 @@ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logge
45582
45878
  });
45583
45879
  }
45584
45880
  /**
45881
+ * Merge the `deepagents.mcp` override into `[mcp]`. The approval store is
45882
+ * dropped (see `MCP_APPROVALS_KEY`); the deprecated flat
45883
+ * `enabled_project_servers` is not modeled but passes through like any other
45884
+ * unknown key, named by the unchecked-key warning — dcode ignores it anyway.
45885
+ */
45886
+ function mergeMcpOverride({ settings, mcpOverride, filePath, logger }) {
45887
+ mergeOverrideTable({
45888
+ settings,
45889
+ tableKey: MCP_TABLE_KEY,
45890
+ override: mcpOverride,
45891
+ knownKeys: DEEPAGENTS_MCP_KEYS,
45892
+ isDroppedKey: (key) => key === MCP_APPROVALS_KEY,
45893
+ warnAboutRelaxations: warnAboutMcpRelaxations,
45894
+ filePath,
45895
+ logger
45896
+ });
45897
+ }
45898
+ /**
45899
+ * Warn when the `deepagents` mcp override re-enables project MCP servers the
45900
+ * machine's global config had rejected. The deny list is written as a whole,
45901
+ * so a shorter list from a repository's `.rulesync/permissions.jsonc` would
45902
+ * otherwise silently lift a rejection the user had put in place — the very
45903
+ * fail-open dcode's own env merge refuses to allow.
45904
+ */
45905
+ function warnAboutMcpRelaxations({ override, previous, filePath, logger }) {
45906
+ if (override[MCP_APPROVALS_KEY] !== void 0) warnWithFallback(logger, `The deepagents mcp override's '${MCP_APPROVALS_KEY}' was not written to ${filePath}: dcode builds that approval store itself from the project MCP servers you accept at launch, so a permissions file cannot pre-approve them.`);
45907
+ const written = override[MCP_DISABLED_PROJECT_SERVERS_KEY];
45908
+ if (written === void 0 || written === null) return;
45909
+ const kept = new Set(readMcpServerNames(written) ?? []);
45910
+ const reEnabled = (readMcpServerNames(previous[MCP_DISABLED_PROJECT_SERVERS_KEY]) ?? []).filter((name) => !kept.has(name));
45911
+ if (reEnabled.length === 0) return;
45912
+ warnWithFallback(logger, `The deepagents mcp override removed ${reEnabled.map((name) => JSON.stringify(name)).join(", ")} from [${MCP_TABLE_KEY}].${MCP_DISABLED_PROJECT_SERVERS_KEY} in ${filePath}, which is your global deepagents-cli config: dcode no longer rejects ${reEnabled.length === 1 ? "that project MCP server" : "those project MCP servers"} outright, for every project on this machine, not just this one.`);
45913
+ }
45914
+ /**
45585
45915
  * Name the value an override key replaces, so a setting the user had turned
45586
45916
  * down is visible as such rather than reported as a bare new value.
45587
45917
  */
@@ -57334,7 +57664,12 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
57334
57664
  //#region src/features/skills/commandcode-skill.ts
57335
57665
  const CommandcodeSkillFrontmatterSchema = z.looseObject({
57336
57666
  name: z.string(),
57337
- description: z.string()
57667
+ description: z.string(),
57668
+ license: z.optional(z.string()),
57669
+ compatibility: z.optional(z.union([z.string(), z.looseObject({})])),
57670
+ metadata: z.optional(z.looseObject({})),
57671
+ "disable-model-invocation": z.optional(z.boolean()),
57672
+ "user-invocable": z.optional(z.boolean())
57338
57673
  });
57339
57674
  /**
57340
57675
  * Represents a Command Code skill directory.
@@ -57417,10 +57752,35 @@ var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
57417
57752
  const settablePaths = CommandcodeSkill.getSettablePaths({ global });
57418
57753
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
57419
57754
  const { name: _sectionName, description: _sectionDescription, ...commandcodeSection } = rulesyncFrontmatter.commandcode ?? {};
57755
+ const license = resolveLicense({
57756
+ rootFrontmatter: rulesyncFrontmatter,
57757
+ section: commandcodeSection
57758
+ });
57759
+ const compatibility = resolveCompatibility({
57760
+ rootFrontmatter: rulesyncFrontmatter,
57761
+ section: commandcodeSection
57762
+ });
57763
+ const metadata = resolveMetadata({
57764
+ rootFrontmatter: rulesyncFrontmatter,
57765
+ section: commandcodeSection
57766
+ });
57767
+ const disableModelInvocation = resolveDisableModelInvocation({
57768
+ rootFrontmatter: rulesyncFrontmatter,
57769
+ section: commandcodeSection
57770
+ });
57771
+ const userInvocable = resolveUserInvocable({
57772
+ rootFrontmatter: rulesyncFrontmatter,
57773
+ section: commandcodeSection
57774
+ });
57420
57775
  const commandcodeFrontmatter = {
57421
57776
  ...commandcodeSection,
57422
57777
  name: rulesyncFrontmatter.name,
57423
- description: rulesyncFrontmatter.description
57778
+ description: rulesyncFrontmatter.description,
57779
+ ...license !== void 0 && { license },
57780
+ ...compatibility !== void 0 && { compatibility },
57781
+ ...metadata !== void 0 && { metadata },
57782
+ ...disableModelInvocation !== void 0 && { "disable-model-invocation": disableModelInvocation },
57783
+ ...userInvocable !== void 0 && { "user-invocable": userInvocable }
57424
57784
  };
57425
57785
  return new CommandcodeSkill({
57426
57786
  outputRoot,
@@ -64793,6 +65153,35 @@ const CommandcodeSubagentFrontmatterSchema = z.looseObject({
64793
65153
  showOutput: z.optional(z.boolean())
64794
65154
  });
64795
65155
  /**
65156
+ * Names Command Code's built-in agents own. The loader runs the frontmatter
65157
+ * `name` (or the file stem when it has none) through `sanitizeAgentName`,
65158
+ * which rewrites every character outside `[a-zA-Z0-9_-]` to `_`, lowercases
65159
+ * it, and skips the file outright when the result is one of these
65160
+ * (`isReservedAgentName`) — with no message. Verified in the `command-code`
65161
+ * 1.54.2 bundle.
65162
+ * @see https://commandcode.ai/docs/agents
65163
+ */
65164
+ const COMMANDCODE_RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
65165
+ "explore",
65166
+ "plan",
65167
+ "review",
65168
+ "general"
65169
+ ]);
65170
+ /** The name Command Code compares against its reserved list. */
65171
+ function toCommandcodeAgentName(name) {
65172
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
65173
+ }
65174
+ /**
65175
+ * The subagent is still generated under the reserved name — the name is the
65176
+ * user's to change, and a rename here would make it diverge from the other
65177
+ * targets — but Command Code would drop it without a visible word, so say so
65178
+ * now (the same courtesy `VibeSkill` extends to its reserved skill names).
65179
+ */
65180
+ function warnAboutReservedAgentName({ name, relativeFilePath, logger }) {
65181
+ if (!COMMANDCODE_RESERVED_AGENT_NAMES.has(toCommandcodeAgentName(name))) return;
65182
+ logger?.warn(`Command Code subagent ${relativeFilePath}: the name ${quoteValueForWarning(name)} is reserved for a Command Code built-in agent, so Command Code ignores a custom agent by that name when it loads agents. Rename the subagent for it to be available in Command Code.`);
65183
+ }
65184
+ /**
64796
65185
  * Command Code custom agent: a Markdown file with YAML frontmatter under
64797
65186
  * `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
64798
65187
  * body is the agent's system prompt.
@@ -64839,7 +65228,7 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64839
65228
  validate: true
64840
65229
  });
64841
65230
  }
64842
- static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
65231
+ static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false, logger }) {
64843
65232
  const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
64844
65233
  const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
64845
65234
  const commandcodeSubagentFrontmatter = {
@@ -64847,6 +65236,11 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64847
65236
  description: rulesyncFrontmatter.description,
64848
65237
  ...commandcodeSection
64849
65238
  };
65239
+ warnAboutReservedAgentName({
65240
+ name: commandcodeSubagentFrontmatter.name,
65241
+ relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
65242
+ logger
65243
+ });
64850
65244
  const body = rulesyncSubagent.getBody();
64851
65245
  const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
64852
65246
  const paths = this.getSettablePaths({ global });
@@ -71219,8 +71613,12 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71219
71613
  * | `false` | none | not supported; the rule is dropped |
71220
71614
  *
71221
71615
  * The default being `true` is the opposite of Cursor, which the adapter was
71222
- * modeled on: `paths` alone scopes nothing, so a rule meant to be path
71223
- * triggered has to carry an explicit `alwaysApply: false` alongside it.
71616
+ * modeled on. The shipped loader (`@tencent-ai/codebuddy-code` 2.151.0,
71617
+ * `dist/codebuddy.js`) is looser than the table — it resolves
71618
+ * `alwaysApply = fm.alwaysApply === true || (!hasPaths && fm.alwaysApply !== false)`,
71619
+ * so `paths` without `alwaysApply` is already MANUAL — but the explicit
71620
+ * `alwaysApply: false` is written anyway: it is correct under both readings
71621
+ * and the documented one is what a reader of the file goes by.
71224
71622
  *
71225
71623
  * @see https://www.codebuddy.ai/docs/cli/memory
71226
71624
  */
@@ -71295,9 +71693,9 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71295
71693
  });
71296
71694
  }
71297
71695
  const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
71298
- const isAlways = this.frontmatter.alwaysApply !== false;
71696
+ const isAlways = this.frontmatter.alwaysApply === true || sourcePaths.length === 0 && this.frontmatter.alwaysApply !== false;
71299
71697
  const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
71300
- const alwaysApply = this.frontmatter.alwaysApply ?? (sourcePaths.length > 0 ? true : void 0);
71698
+ const alwaysApply = this.frontmatter.alwaysApply;
71301
71699
  const rulesyncFrontmatter = {
71302
71700
  targets,
71303
71701
  root: false,
@@ -75988,6 +76386,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
75988
76386
  collisionPolicy: "fold"
75989
76387
  }
75990
76388
  }],
76389
+ ["pool", {
76390
+ class: PoolRule,
76391
+ meta: {
76392
+ extension: "md",
76393
+ supportsGlobal: true,
76394
+ ruleDiscoveryMode: "auto",
76395
+ collisionPolicy: "fold"
76396
+ }
76397
+ }],
75991
76398
  ["qwencode", {
75992
76399
  class: QwencodeRule,
75993
76400
  meta: {
@@ -76111,15 +76518,6 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
76111
76518
  ruleDiscoveryMode: "auto"
76112
76519
  }
76113
76520
  }],
76114
- ["pool", {
76115
- class: PoolRule,
76116
- meta: {
76117
- extension: "md",
76118
- supportsGlobal: true,
76119
- ruleDiscoveryMode: "auto",
76120
- collisionPolicy: "fold"
76121
- }
76122
- }],
76123
76521
  ["dsh", {
76124
76522
  class: DshRule,
76125
76523
  meta: {
@@ -79305,6 +79703,6 @@ async function importChecksCore(params) {
79305
79703
  return writtenCount;
79306
79704
  }
79307
79705
  //#endregion
79308
- export { RulesyncCheck as $, writeFileContent as $t, CLAUDECODE_SKILLS_DIR_PATH as A, RULESYNC_SKILLS_RELATIVE_DIR_PATH as An, directoryExists as At, RulesyncSkill as B, truncateText as Bn, listSubdirectoryNames as Bt, ChecksProcessor as C, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Cn, ErrorCodes as Ct, CLAUDECODE_LOCAL_RULE_FILE_NAME as D, RULESYNC_PERMISSIONS_SCHEMA_URL as Dn, assertWritablePathInsideRoot as Dt, CLAUDECODE_DIR as E, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as En, assertTreeContainsNoSymlinks as Et, AUGMENTCODE_DIR as F, parseCommaSeparatedList as Fn, isFileNotFoundError as Ft, RulesyncMcp as G, stripControlCharactersKeepingLineFeeds as Gn, removeDirectoryStrict as Gt, RulesyncRule as H, hasEnclosingMarkOutsideKeycap as Hn, readFileContent as Ht, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as I, ALL_FEATURES as In, isFileSystemError as It, getRulesyncSourceCandidates as J, removeTempDirectory as Jt, RulesyncIgnore as K, stripHiddenCharacters as Kn, removeFile as Kt, getLocalSkillDirNames as L, ALL_FEATURES_WITH_WILDCARD as Ln, isSymlink as Lt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as M, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Mn, fileExists as Mt, caseFoldIdentity as N, RULESYNC_USER_CONFIG_DIR_NAME as Nn, getFileSize as Nt, CLAUDECODE_MEMORIES_DIR_NAME as O, RULESYNC_RELATIVE_DIR_PATH as On, checkPathTraversal as Ot, groupSpellingsByCaseFoldedIdentity as P, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as Pn, getHomeDirectory as Pt, RulesyncCommandFrontmatterSchema as Q, writeFileBuffer as Qt, RulesyncSubagent as R, DEPRECATED_FEATURE_REPLACEMENTS as Rn, listDirectoryEntryNames as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_MCP_SCHEMA_URL as Sn, CLIError as St, CODEXCLI_DIR as T, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as Tn, assertDirectoryIfExists as Tt, RulesyncRuleFrontmatterSchema as U, quoteForLog as Un, readFileContentOrNull as Ut, RulesyncSkillFrontmatterSchema as V, hasDeceptiveHiddenCharacters as Vn, pathEscapesRoot as Vt, RulesyncPermissions as W, stripControlCharacters as Wn, removeDirectory as Wt, parseJsonc as X, runWithDirectoryRollback as Xt, resolveRulesyncSourceWritePath as Y, resolvePath as Yt, RulesyncCommand as Z, toPosixPath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, fallbackLogger as _t, getProcessorRegistryEntry as a, MAX_FILE_SIZE as an, ConfigResolver as at, CommandsProcessor as b, RULESYNC_MCP_LEGACY_FILE_NAME as bn, resetRunWarningState as bt, RulesProcessor as c, RULESYNC_CHECKS_RELATIVE_DIR_PATH as cn, CONFLICTING_TARGET_PAIRS as ct, CODEBUDDY_DIR as d, RULESYNC_CONFIG_SCHEMA_URL as dn, SourceEntrySchema as dt, ALL_TOOL_TARGETS as en, RulesyncCheckFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as fn, assertTargetsFeaturesExclusive as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, WarningCollectingLogger as gt, shortenToWidth as h, RULESYNC_HOOKS_LEGACY_FILE_NAME as hn, JsonLogger as ht, inspectInputRoots as i, CURATED_RULES_FEATURE_SUBDIR as in, SKILL_FILE_NAME as it, FACTORYDROID_DIR as j, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as jn, ensureDir as jt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as k, RULESYNC_RULES_RELATIVE_DIR_PATH as kn, createTempDirectory as kt, SubagentsProcessor as l, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as ln, ConfigFileSchema as lt, displayWidthOf as m, RULESYNC_HOOKS_FILE_NAME as mn, ConsoleLogger as mt, formatSourceLoadFailure as n, PACKAGING_TOOL_TARGETS as nn, loadYaml as nt, convertFromTool as o, RULESYNC_AIIGNORE_FILE_NAME as on, mergeInputRootConfigs as ot, ELLIPSIS_WIDTH as p, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as pn, findControlCharacter as pt, RulesyncHooks as q, removeFileStrict as qt, generate as r, ToolTargetSchema as rn, SHARED_USER_MANAGED_CONFIG_PATHS as rt, isPackagingToolTarget as s, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as sn, resolveEffectiveInputRoots as st, importFromTool as t, ALL_TOOL_TARGETS_WITH_WILDCARD as tn, stringifyFrontmatter as tt, SkillsProcessor as u, RULESYNC_CONFIG_RELATIVE_FILE_PATH as un, GITIGNORE_DESTINATION_KEY as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, warnOnConflictingFlags as vt, CODEXCLI_BASH_RULES_FILE_NAME as w, RULESYNC_PERMISSIONS_FILE_NAME as wn, applyFileMode as wt, QWENCODE_DIR as x, RULESYNC_MCP_RELATIVE_FILE_PATH as xn, withWarnOnceScope as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_FILE_NAME as yn, withFallbackLoggerTarget as yt, RulesyncSubagentFrontmatterSchema as z, formatError as zn, listFilePathsRecursively as zt };
79706
+ export { RulesyncCommand as $, toPosixPath as $t, CLAUDECODE_MEMORIES_DIR_NAME as A, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as An, checkPathTraversal as At, RulesyncSubagent as B, stripControlCharacters as Bn, listDirectoryEntryNames as Bt, COMMANDCODE_DIR as C, RULESYNC_PERMISSIONS_SCHEMA_URL as Cn, withWarnOnceScope as Ct, CODEXCLI_DIR as D, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Dn, assertDirectoryIfExists as Dt, CODEXCLI_BASH_RULES_FILE_NAME as E, RULESYNC_SKILLS_RELATIVE_DIR_PATH as En, applyFileMode as Et, caseFoldIdentity as F, formatError as Fn, getFileSize as Ft, RulesyncRuleFrontmatterSchema as G, readFileContentOrNull as Gt, RulesyncSkill as H, stripHiddenCharacters as Hn, listSubdirectoryNames as Ht, groupSpellingsByCaseFoldedIdentity as I, truncateText as In, getHomeDirectory as It, RulesyncIgnore as J, removeFile as Jt, RulesyncPermissions as K, removeDirectory as Kt, AUGMENTCODE_DIR as L, hasDeceptiveHiddenCharacters as Ln, isFileNotFoundError as Lt, CLAUDECODE_SKILLS_DIR_PATH as M, ALL_FEATURES as Mn, directoryExists as Mt, FACTORYDROID_DIR as N, ALL_FEATURES_WITH_WILDCARD as Nn, ensureDir as Nt, CLAUDECODE_DIR as O, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as On, assertTreeContainsNoSymlinks as Ot, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as P, DEPRECATED_FEATURE_REPLACEMENTS as Pn, fileExists as Pt, parseJsonc as Q, runWithDirectoryRollback as Qt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as R, hasEnclosingMarkOutsideKeycap as Rn, isFileSystemError as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Sn, resetRunWarningState as St, ChecksProcessor as T, RULESYNC_RULES_RELATIVE_DIR_PATH as Tn, ErrorCodes as Tt, RulesyncSkillFrontmatterSchema as U, pathEscapesRoot as Ut, RulesyncSubagentFrontmatterSchema as V, stripControlCharactersKeepingLineFeeds as Vn, listFilePathsRecursively as Vt, RulesyncRule as W, readFileContent as Wt, getRulesyncSourceCandidates as X, removeTempDirectory as Xt, RulesyncHooks as Y, removeFileStrict as Yt, resolveRulesyncSourceWritePath as Z, resolvePath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, JsonLogger as _t, getProcessorRegistryEntry as a, ToolTargetSchema as an, SHARED_USER_MANAGED_CONFIG_PATHS as at, CommandsProcessor as b, RULESYNC_MCP_SCHEMA_URL as bn, warnOnConflictingFlags as bt, RulesProcessor as c, MAX_FILE_SIZE as cn, mergeInputRootConfigs as ct, CODEBUDDY_DIR as d, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as dn, ConfigFileSchema as dt, writeFileBuffer as en, RulesyncCommandFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CONFIG_RELATIVE_FILE_PATH as fn, GITIGNORE_DESTINATION_KEY as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, ConsoleLogger as gt, shortenToWidth as h, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as hn, findControlCharacter as ht, inspectInputRoots as i, PACKAGING_TOOL_TARGETS as in, loadYaml as it, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as j, parseCommaSeparatedList as jn, createTempDirectory as jt, CLAUDECODE_LOCAL_RULE_FILE_NAME as k, RULESYNC_USER_CONFIG_DIR_NAME as kn, assertWritablePathInsideRoot as kt, SubagentsProcessor as l, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as ln, resolveEffectiveInputRoots as lt, displayWidthOf as m, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as mn, assertTargetsFeaturesExclusive as mt, formatSourceLoadFailure as n, ALL_TOOL_TARGETS as nn, RulesyncCheckFrontmatterSchema as nt, convertFromTool as o, CURATED_RULES_FEATURE_SUBDIR as on, SKILL_FILE_NAME as ot, ELLIPSIS_WIDTH as p, RULESYNC_CONFIG_SCHEMA_URL as pn, SourceEntrySchema as pt, RulesyncMcp as q, removeDirectoryStrict as qt, generate as r, ALL_TOOL_TARGETS_WITH_WILDCARD as rn, stringifyFrontmatter as rt, isPackagingToolTarget as s, FEATURE_SOURCE_TREE_ENTRIES as sn, ConfigResolver as st, importFromTool as t, writeFileContent as tn, RulesyncCheck as tt, SkillsProcessor as u, RULESYNC_CHECKS_RELATIVE_DIR_PATH as un, CONFLICTING_TARGET_PAIRS as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, WarningCollectingLogger as vt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as w, RULESYNC_RELATIVE_DIR_PATH as wn, CLIError as wt, QWENCODE_DIR as x, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as xn, withFallbackLoggerTarget as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_RELATIVE_FILE_PATH as yn, fallbackLogger as yt, getLocalSkillDirNames as z, quoteForLog as zn, isSymlink as zt };
79309
79707
 
79310
- //# sourceMappingURL=import-BJsTd50i.js.map
79708
+ //# sourceMappingURL=import-DxRza3o_.js.map