rulesync 16.34.2 → 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.
@@ -515,6 +515,7 @@ const rulesProcessorToolTargetTuple = [
515
515
  "musecode",
516
516
  "opencode",
517
517
  "pi",
518
+ "pool",
518
519
  "qwencode",
519
520
  "reasonix",
520
521
  "replit",
@@ -528,7 +529,6 @@ const rulesProcessorToolTargetTuple = [
528
529
  "zcode",
529
530
  "zed",
530
531
  "zoocode",
531
- "pool",
532
532
  "dsh"
533
533
  ];
534
534
  const ignoreProcessorToolTargetTuple = [
@@ -4091,6 +4091,10 @@ const HookDefinitionSchema = z.looseObject({
4091
4091
  model: z.optional(safeString),
4092
4092
  args: z.optional(z.array(safeString)),
4093
4093
  metadata: z.optional(z.looseObject({})),
4094
+ source: z.optional(z.looseObject({
4095
+ source: safeString,
4096
+ ref: z.optional(safeString)
4097
+ })),
4094
4098
  if: z.optional(safeString),
4095
4099
  commandWindows: z.optional(safeString),
4096
4100
  additionalContextLimit: z.optional(z.int().check(nonnegative())),
@@ -4851,10 +4855,12 @@ const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONIC
4851
4855
  *
4852
4856
  * Bob reads `hooks` from `.bob/settings.json` (project) and
4853
4857
  * `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
4854
- * five events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse and
4855
- * Stop. Only `command` hooks exist, and `matcher` (a regex over the tool
4856
- * 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.
4857
4862
  *
4863
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
4858
4864
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
4859
4865
  */
4860
4866
  const BOB_HOOK_EVENTS = [
@@ -4862,6 +4868,8 @@ const BOB_HOOK_EVENTS = [
4862
4868
  "beforeSubmitPrompt",
4863
4869
  "preToolUse",
4864
4870
  "postToolUse",
4871
+ "preCompact",
4872
+ "postCompact",
4865
4873
  "stop"
4866
4874
  ];
4867
4875
  const CANONICAL_TO_BOB_EVENT_NAMES = {
@@ -4869,6 +4877,8 @@ const CANONICAL_TO_BOB_EVENT_NAMES = {
4869
4877
  beforeSubmitPrompt: "UserPromptSubmit",
4870
4878
  preToolUse: "PreToolUse",
4871
4879
  postToolUse: "PostToolUse",
4880
+ preCompact: "PreCompact",
4881
+ postCompact: "PostCompact",
4872
4882
  stop: "Stop"
4873
4883
  };
4874
4884
  const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
@@ -7280,13 +7290,30 @@ const DeepagentsExtensionsOverrideSchema = z.looseObject({
7280
7290
  ]))
7281
7291
  });
7282
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
+ /**
7283
7309
  * The `[shell].allow_list` array itself is rulesync-owned, driven by the
7284
7310
  * shared `permission.bash` block, so it has no key here.
7285
7311
  */
7286
7312
  const DeepagentsPermissionsOverrideSchema = z.looseObject({
7287
7313
  permission: z.optional(ToolScopedPermissionSchema),
7288
7314
  startup: z.optional(DeepagentsStartupOverrideSchema),
7289
- extensions: z.optional(DeepagentsExtensionsOverrideSchema)
7315
+ extensions: z.optional(DeepagentsExtensionsOverrideSchema),
7316
+ mcp: z.optional(DeepagentsMcpOverrideSchema)
7290
7317
  });
7291
7318
  /**
7292
7319
  * The actions Junie's allowlist accepts. Verified against the shipped Junie
@@ -9600,7 +9627,13 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
9600
9627
  "user-invocable": z.optional(z.boolean()),
9601
9628
  "allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
9602
9629
  })),
9603
- 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
+ })),
9604
9637
  cortexcode: z.optional(z.looseObject({})),
9605
9638
  continue: z.optional(z.looseObject({})),
9606
9639
  tabnine: z.optional(z.looseObject({})),
@@ -9844,7 +9877,7 @@ async function getLocalSkillDirNames(sourceTree) {
9844
9877
  *
9845
9878
  * The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
9846
9879
  * default that applies to every tool supporting the flag (claudecode, copilot,
9847
- * 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
9848
9881
  * default with a per-target value. A defined section value (including `false`)
9849
9882
  * always wins over the root default.
9850
9883
  *
@@ -9863,7 +9896,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
9863
9896
  *
9864
9897
  * The rulesync skill frontmatter exposes a root-level `user-invocable` default
9865
9898
  * that applies to every tool supporting the flag (claudecode, copilot,
9866
- * 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
9867
9900
  * per-target value. A defined section value (including `false`) always wins
9868
9901
  * over the root default.
9869
9902
  *
@@ -15431,6 +15464,7 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
15431
15464
  const COMMANDCODE_DIR = ".commandcode";
15432
15465
  const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
15433
15466
  const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
15467
+ const COMMANDCODE_SETTINGS_LOCAL_FILE_NAME = "settings.local.json";
15434
15468
  const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
15435
15469
  const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
15436
15470
  const COMMANDCODE_COMMANDS_DIR_PATH = join(COMMANDCODE_DIR, "commands");
@@ -20645,6 +20679,18 @@ function isToolMatcherEntry(x) {
20645
20679
  return true;
20646
20680
  }
20647
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
+ /**
20648
20694
  * Filter the shared canonical hooks to the supported events and merge tool overrides on top.
20649
20695
  */
20650
20696
  function buildEffectiveHooks$1({ config, toolOverrideHooks, supportedEvents }) {
@@ -20785,6 +20831,7 @@ const isEmittableString = ({ value }) => isNonEmptyString(value);
20785
20831
  const isEmittableArray = ({ value }) => isStringArray$1(value);
20786
20832
  const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
20787
20833
  const isImportableArray = ({ value }) => isSafeStringArray(value);
20834
+ const isEmittableObject = ({ value }) => isPlainObject$1(value);
20788
20835
  /**
20789
20836
  * The canonical schema of one hook field, looked up by name. Read off the
20790
20837
  * schema's own shape rather than by parsing a one-field object, so a `canonical`
@@ -20819,11 +20866,17 @@ const isImportableNumber = ({ value, canonical }) => Number.isFinite(value) && s
20819
20866
  value,
20820
20867
  canonical
20821
20868
  });
20869
+ const isImportableObject = ({ value, canonical }) => isPlainObject$1(value) && satisfiesCanonicalField({
20870
+ value,
20871
+ canonical
20872
+ });
20822
20873
  /**
20823
20874
  * Say which rule the value broke, so the warning names the actual constraint
20824
20875
  * rather than asserting a canonical rejection that may not be the reason. A
20825
20876
  * closed enum lists its members; a rule carrying its own message (the
20826
- * 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.
20827
20880
  */
20828
20881
  function describeScalarConstraint({ canonical, value }) {
20829
20882
  const schema = canonicalFieldSchema(canonical);
@@ -20831,13 +20884,22 @@ function describeScalarConstraint({ canonical, value }) {
20831
20884
  if (result === void 0 || result.success) return `it is not a value this field carries through.`;
20832
20885
  const issue = result.error.issues[0];
20833
20886
  if (issue === void 0) return `it is not a value the canonical "${canonical}" field accepts.`;
20834
- 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}.`;
20835
20888
  }
20836
20889
  const describeInvalidScalar = ({ tool, canonical, value }) => `Dropping "${tool}" (${quoteValueForWarning(value)}) while importing a hook: ${describeScalarConstraint({
20837
20890
  canonical,
20838
20891
  value
20839
20892
  })} Importing it would fail validation on the next run.`;
20840
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.`;
20841
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.`;
20842
20904
  /**
20843
20905
  * Check a value against the shape its field documents. A string field also
@@ -20943,6 +21005,14 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
20943
21005
  fields: converterConfig.recordPassthroughFields ?? [],
20944
21006
  isValid: isEmittableRecord,
20945
21007
  warn
21008
+ }),
21009
+ ...emitPassthroughFields({
21010
+ def,
21011
+ hookType,
21012
+ eventName,
21013
+ fields: converterConfig.objectPassthroughFields ?? [],
21014
+ isValid: isEmittableObject,
21015
+ warn
20946
21016
  })
20947
21017
  };
20948
21018
  }
@@ -20966,9 +21036,12 @@ function buildToolHooks({ defs, eventName, converterConfig, warn }) {
20966
21036
  converterConfig,
20967
21037
  warn
20968
21038
  }),
20969
- type: hookType,
21039
+ type: converterConfig.hookTypeNames?.[hookType] ?? hookType,
20970
21040
  ...command !== void 0 && command !== null && { command },
20971
- ...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
+ }) },
20972
21045
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
20973
21046
  ...emitTypePayloadFields({
20974
21047
  def,
@@ -21090,6 +21163,20 @@ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
21090
21163
  function isImportedHookType(value) {
21091
21164
  return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
21092
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
+ }
21093
21180
  function isStringRecord(value) {
21094
21181
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
21095
21182
  return Object.values(value).every((v) => typeof v === "string");
@@ -21255,6 +21342,14 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21255
21342
  isValid: isEmittableRecord,
21256
21343
  describeInvalid: describeInvalidRecord,
21257
21344
  warn
21345
+ }),
21346
+ ...importPassthroughFields({
21347
+ h,
21348
+ hookType,
21349
+ fields: converterConfig.objectPassthroughFields ?? [],
21350
+ isValid: isImportableObject,
21351
+ describeInvalid: describeInvalidObject,
21352
+ warn
21258
21353
  })
21259
21354
  };
21260
21355
  }
@@ -21262,7 +21357,10 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21262
21357
  * Convert a single tool hook record into a canonical hook definition.
21263
21358
  */
21264
21359
  function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21265
- const hookType = isImportedHookType(h.type) ? h.type : "command";
21360
+ const hookType = resolveImportedHookType({
21361
+ value: h.type,
21362
+ converterConfig
21363
+ });
21266
21364
  const command = importCanonicalString({
21267
21365
  value: stripCommandPrefix({
21268
21366
  command: h.command,
@@ -21271,7 +21369,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21271
21369
  canonical: "command",
21272
21370
  warn
21273
21371
  });
21274
- 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;
21275
21376
  const prompt = importCanonicalString({
21276
21377
  value: h.prompt,
21277
21378
  canonical: "prompt",
@@ -21404,7 +21505,10 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
21404
21505
  const skipReason = describeHookSkipReason({
21405
21506
  h,
21406
21507
  rawEntry,
21407
- hookType: isImportedHookType(h.type) ? h.type : "command",
21508
+ hookType: resolveImportedHookType({
21509
+ value: h.type,
21510
+ converterConfig
21511
+ }),
21408
21512
  converterConfig
21409
21513
  });
21410
21514
  if (skipReason !== void 0) {
@@ -21889,6 +21993,7 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
21889
21993
  "beforeSubmitPrompt"
21890
21994
  ]),
21891
21995
  supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
21996
+ timeoutUnit: "milliseconds",
21892
21997
  arrayPassthroughFields: [{
21893
21998
  canonical: "args",
21894
21999
  tool: "args"
@@ -22487,9 +22592,12 @@ const BOB_SPEC = {
22487
22592
  noMatcherEvents: /* @__PURE__ */ new Set([
22488
22593
  "sessionStart",
22489
22594
  "beforeSubmitPrompt",
22595
+ "preCompact",
22596
+ "postCompact",
22490
22597
  "stop"
22491
22598
  ]),
22492
- supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
22599
+ supportedHookTypes: /* @__PURE__ */ new Set(["command", "http"]),
22600
+ hookTypeNames: { http: "https" },
22493
22601
  wildcardMatcherMeansAll: true
22494
22602
  }
22495
22603
  };
@@ -22500,10 +22608,13 @@ const BOB_SPEC = {
22500
22608
  * `<project>/.bob/settings.json` (project scope) and
22501
22609
  * `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
22502
22610
  * `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
22503
- * "command": "...", "timeout"?: <seconds> }] }] }`. The file also holds
22504
- * settings rulesync does not own, so generation merges the `hooks` key into it
22505
- * (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.
22506
22616
  *
22617
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
22507
22618
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
22508
22619
  */
22509
22620
  var BobHooks = class extends SettingsJsonHooks {
@@ -23933,6 +24044,11 @@ const CORTEXCODE_SPEC = {
23933
24044
  booleanPassthroughFields: [{
23934
24045
  canonical: "enabled",
23935
24046
  tool: "enabled"
24047
+ }],
24048
+ objectPassthroughFields: [{
24049
+ canonical: "source",
24050
+ tool: "source",
24051
+ commandOnly: true
23936
24052
  }]
23937
24053
  }
23938
24054
  };
@@ -23943,8 +24059,9 @@ const CORTEXCODE_SPEC = {
23943
24059
  * (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
23944
24060
  * scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
23945
24061
  * "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
23946
- * <seconds>, "enabled"?: <boolean> }] }] }`. The project file also holds
23947
- * 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
23948
24065
  * either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
23949
24066
  * neither file is removed wholesale (hooks.json sits in the CLI-owned
23950
24067
  * `~/.snowflake/cortex/` tree).
@@ -28590,7 +28707,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
28590
28707
  supportsImport: true
28591
28708
  },
28592
28709
  supportedEvents: BOB_HOOK_EVENTS,
28593
- supportedHookTypes: ["command"],
28710
+ supportedHookTypes: ["command", "http"],
28594
28711
  supportsMatcher: true,
28595
28712
  matcherEvents: ["preToolUse", "postToolUse"]
28596
28713
  }],
@@ -31404,6 +31521,50 @@ function parseBobMcpConfig({ fileContent, relativePath }) {
31404
31521
  /** The `type` Bob IDE writes for a streamable HTTP server. */
31405
31522
  const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
31406
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
+ /**
31407
31568
  * The remote transport Bob IDE reads a server as, spelled the way it writes
31408
31569
  * it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
31409
31570
  * `url` with no `type`. `http` is the canonical rulesync alias for streamable
@@ -31426,7 +31587,9 @@ function asBobRemoteType(stated, url) {
31426
31587
  * `headers`, `timeout`, `alwaysAllow` and `disabled` pass through, as Bob
31427
31588
  * documents all of them, with prototype-pollution keys dropped at every
31428
31589
  * nesting level (Bob spreads `env` and `headers` into the process environment
31429
- * 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.
31430
31593
  *
31431
31594
  * Bob Shell documents the same file with an `httpURL` key instead of
31432
31595
  * `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
@@ -31492,10 +31655,16 @@ function convertToBobFormat(mcpServers, logger) {
31492
31655
  converted.command = command;
31493
31656
  if (args.length > 0) converted.args = args;
31494
31657
  }
31495
- for (const [key, value] of Object.entries(rest)) {
31658
+ const { oauth, ...passthrough } = rest;
31659
+ for (const [key, value] of Object.entries(passthrough)) {
31496
31660
  if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
31497
31661
  converted[key] = omitPrototypePollutionKeysDeep(value);
31498
31662
  }
31663
+ if (isRecord$1(oauth)) flattenOauthForBob({
31664
+ oauth,
31665
+ converted
31666
+ });
31667
+ else if (oauth !== void 0) converted.oauth = oauth;
31499
31668
  result[serverName] = converted;
31500
31669
  }
31501
31670
  return result;
@@ -31505,9 +31674,10 @@ function convertToBobFormat(mcpServers, logger) {
31505
31674
  * (`type: "streamable-http"` + `url`) is already canonical and passes through;
31506
31675
  * the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
31507
31676
  * `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
31508
- * on the next generate. Every other field passes through with its
31509
- * prototype-pollution keys dropped at every nesting level, mirroring the
31510
- * 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`.
31511
31681
  */
31512
31682
  function convertFromBobFormat(mcpServers) {
31513
31683
  if (!isMcpServers(mcpServers)) return {};
@@ -31528,6 +31698,7 @@ function convertFromBobFormat(mcpServers) {
31528
31698
  converted.url = httpURL;
31529
31699
  converted.type = "http";
31530
31700
  } else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
31701
+ nestOauthFromBob(converted);
31531
31702
  result[serverName] = converted;
31532
31703
  }
31533
31704
  return result;
@@ -32552,7 +32723,10 @@ function asContinueRemoteType(stated, url) {
32552
32723
  * file with a strict union — one server that matches neither shape (an
32553
32724
  * unknown transport, a non-string `env` value) makes Continue drop every
32554
32725
  * server in the file — so only the documented keys are emitted and anything
32555
- * 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
32556
32730
  * accepted by Continue's schema but ignored with a warning at load time, so
32557
32731
  * it is dropped here with a warning instead of being written as if it worked.
32558
32732
  *
@@ -32588,6 +32762,17 @@ function convertToContinueFormat(mcpServers, logger) {
32588
32762
  }
32589
32763
  return result;
32590
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
+ }
32591
32776
  function convertRemoteServer({ serverName, serverConfig, logger }) {
32592
32777
  const url = resolveRemoteMcpUrl(serverConfig);
32593
32778
  if (!url) {
@@ -32615,6 +32800,12 @@ function convertRemoteServer({ serverName, serverConfig, logger }) {
32615
32800
  url
32616
32801
  };
32617
32802
  if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
32803
+ warnUnsupportedContinueKeys({
32804
+ serverName,
32805
+ serverConfig,
32806
+ keys: ["timeout", "oauth"],
32807
+ logger
32808
+ });
32618
32809
  return converted;
32619
32810
  }
32620
32811
  function convertStdioServer({ serverName, serverConfig, logger }) {
@@ -32635,6 +32826,12 @@ function convertStdioServer({ serverName, serverConfig, logger }) {
32635
32826
  if (args.length > 0) converted.args = args;
32636
32827
  if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
32637
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
+ });
32638
32835
  return converted;
32639
32836
  }
32640
32837
  /**
@@ -45133,6 +45330,27 @@ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
45133
45330
  * @see https://github.com/langchain-ai/deepagents `config_manifest.py`
45134
45331
  */
45135
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";
45136
45354
  const ALLOW_ALL_SENTINEL = "all";
45137
45355
  const RECOMMENDED_SENTINEL = "recommended";
45138
45356
  const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
@@ -45205,7 +45423,9 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
45205
45423
  * lifted back into the override on import. The same override carries
45206
45424
  * `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
45207
45425
  * which decide whether the Python in a checked-out project's
45208
- * `.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.
45209
45429
  *
45210
45430
  * On **import** the allowlist comes back as `bash` `allow` rules named by the
45211
45431
  * executable — skipping any entry dcode could not match in the first place, so
@@ -45303,17 +45523,9 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45303
45523
  if (Object.keys(shell).length > 0) settings[SHELL_TABLE_KEY] = shell;
45304
45524
  else delete settings[SHELL_TABLE_KEY];
45305
45525
  }
45306
- const startupOverride = config.deepagents?.startup;
45307
- if (isPlainObject$1(startupOverride) && Object.keys(startupOverride).length > 0) mergeStartupOverride({
45526
+ mergeDeepagentsOverrides({
45308
45527
  settings,
45309
- startupOverride,
45310
- filePath,
45311
- logger
45312
- });
45313
- const extensionsOverride = config.deepagents?.extensions;
45314
- if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
45315
- settings,
45316
- extensionsOverride,
45528
+ override: config.deepagents,
45317
45529
  filePath,
45318
45530
  logger
45319
45531
  });
@@ -45345,10 +45557,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45345
45557
  extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
45346
45558
  selfPath
45347
45559
  });
45560
+ const mcpOverride = liftMcpOverride({
45561
+ mcp: isPlainObject$1(settings[MCP_TABLE_KEY]) ? settings[MCP_TABLE_KEY] : {},
45562
+ selfPath
45563
+ });
45348
45564
  const result = { ...config };
45349
45565
  const deepagents = {};
45350
45566
  if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
45351
45567
  if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
45568
+ if (Object.keys(mcpOverride).length > 0) deepagents.mcp = mcpOverride;
45352
45569
  if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
45353
45570
  return RulesyncPermissions.fromImportedFileContent({
45354
45571
  outputRoot: getDeepagentsRulesyncOutputRoot({
@@ -45456,7 +45673,7 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
45456
45673
  * read — a wrapper rather than the bare value, so a legitimately falsy setting
45457
45674
  * is not mistaken for a rejection.
45458
45675
  */
45459
- 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` }) {
45460
45677
  const override = {};
45461
45678
  const rejected = [];
45462
45679
  for (const key of keys) {
@@ -45469,7 +45686,7 @@ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
45469
45686
  if (normalized) override[key] = normalized.value;
45470
45687
  else rejected.push(`${key} = ${JSON.stringify(value)}`);
45471
45688
  }
45472
- 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.`);
45473
45690
  return override;
45474
45691
  }
45475
45692
  /**
@@ -45517,6 +45734,37 @@ function liftExtensionsOverride({ extensions, selfPath }) {
45517
45734
  });
45518
45735
  }
45519
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
+ /**
45520
45768
  * Merge one block of the `deepagents` override into its `config.toml` table,
45521
45769
  * preserving every other key of that table. A table that is not a table at all
45522
45770
  * is something rulesync did not write and cannot merge into, so it is left
@@ -45565,6 +45813,38 @@ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, lo
45565
45813
  if (unknownKeys.length === 0) return;
45566
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.`);
45567
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
+ }
45568
45848
  /**
45569
45849
  * Merge the `deepagents.startup` override into `[startup]`.
45570
45850
  */
@@ -45598,6 +45878,40 @@ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logge
45598
45878
  });
45599
45879
  }
45600
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
+ /**
45601
45915
  * Name the value an override key replaces, so a setting the user had turned
45602
45916
  * down is visible as such rather than reported as a bare new value.
45603
45917
  */
@@ -57350,7 +57664,12 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
57350
57664
  //#region src/features/skills/commandcode-skill.ts
57351
57665
  const CommandcodeSkillFrontmatterSchema = z.looseObject({
57352
57666
  name: z.string(),
57353
- 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())
57354
57673
  });
57355
57674
  /**
57356
57675
  * Represents a Command Code skill directory.
@@ -57433,10 +57752,35 @@ var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
57433
57752
  const settablePaths = CommandcodeSkill.getSettablePaths({ global });
57434
57753
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
57435
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
+ });
57436
57775
  const commandcodeFrontmatter = {
57437
57776
  ...commandcodeSection,
57438
57777
  name: rulesyncFrontmatter.name,
57439
- 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 }
57440
57784
  };
57441
57785
  return new CommandcodeSkill({
57442
57786
  outputRoot,
@@ -64809,6 +65153,35 @@ const CommandcodeSubagentFrontmatterSchema = z.looseObject({
64809
65153
  showOutput: z.optional(z.boolean())
64810
65154
  });
64811
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
+ /**
64812
65185
  * Command Code custom agent: a Markdown file with YAML frontmatter under
64813
65186
  * `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
64814
65187
  * body is the agent's system prompt.
@@ -64855,7 +65228,7 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64855
65228
  validate: true
64856
65229
  });
64857
65230
  }
64858
- static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
65231
+ static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false, logger }) {
64859
65232
  const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
64860
65233
  const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
64861
65234
  const commandcodeSubagentFrontmatter = {
@@ -64863,6 +65236,11 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64863
65236
  description: rulesyncFrontmatter.description,
64864
65237
  ...commandcodeSection
64865
65238
  };
65239
+ warnAboutReservedAgentName({
65240
+ name: commandcodeSubagentFrontmatter.name,
65241
+ relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
65242
+ logger
65243
+ });
64866
65244
  const body = rulesyncSubagent.getBody();
64867
65245
  const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
64868
65246
  const paths = this.getSettablePaths({ global });
@@ -71235,8 +71613,12 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71235
71613
  * | `false` | none | not supported; the rule is dropped |
71236
71614
  *
71237
71615
  * The default being `true` is the opposite of Cursor, which the adapter was
71238
- * modeled on: `paths` alone scopes nothing, so a rule meant to be path
71239
- * 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.
71240
71622
  *
71241
71623
  * @see https://www.codebuddy.ai/docs/cli/memory
71242
71624
  */
@@ -71311,9 +71693,9 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71311
71693
  });
71312
71694
  }
71313
71695
  const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
71314
- const isAlways = this.frontmatter.alwaysApply !== false;
71696
+ const isAlways = this.frontmatter.alwaysApply === true || sourcePaths.length === 0 && this.frontmatter.alwaysApply !== false;
71315
71697
  const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
71316
- const alwaysApply = this.frontmatter.alwaysApply ?? (sourcePaths.length > 0 ? true : void 0);
71698
+ const alwaysApply = this.frontmatter.alwaysApply;
71317
71699
  const rulesyncFrontmatter = {
71318
71700
  targets,
71319
71701
  root: false,
@@ -76004,6 +76386,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
76004
76386
  collisionPolicy: "fold"
76005
76387
  }
76006
76388
  }],
76389
+ ["pool", {
76390
+ class: PoolRule,
76391
+ meta: {
76392
+ extension: "md",
76393
+ supportsGlobal: true,
76394
+ ruleDiscoveryMode: "auto",
76395
+ collisionPolicy: "fold"
76396
+ }
76397
+ }],
76007
76398
  ["qwencode", {
76008
76399
  class: QwencodeRule,
76009
76400
  meta: {
@@ -76127,15 +76518,6 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
76127
76518
  ruleDiscoveryMode: "auto"
76128
76519
  }
76129
76520
  }],
76130
- ["pool", {
76131
- class: PoolRule,
76132
- meta: {
76133
- extension: "md",
76134
- supportsGlobal: true,
76135
- ruleDiscoveryMode: "auto",
76136
- collisionPolicy: "fold"
76137
- }
76138
- }],
76139
76521
  ["dsh", {
76140
76522
  class: DshRule,
76141
76523
  meta: {
@@ -79321,6 +79703,6 @@ async function importChecksCore(params) {
79321
79703
  return writtenCount;
79322
79704
  }
79323
79705
  //#endregion
79324
- export { RulesyncCheck as $, writeFileContent as $t, CLAUDECODE_SKILLS_DIR_PATH as A, ALL_FEATURES as An, directoryExists as At, RulesyncSkill as B, stripHiddenCharacters as Bn, listSubdirectoryNames as Bt, ChecksProcessor as C, RULESYNC_RULES_RELATIVE_DIR_PATH as Cn, ErrorCodes as Ct, CLAUDECODE_LOCAL_RULE_FILE_NAME as D, RULESYNC_USER_CONFIG_DIR_NAME as Dn, assertWritablePathInsideRoot as Dt, CLAUDECODE_DIR as E, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as En, assertTreeContainsNoSymlinks as Et, AUGMENTCODE_DIR as F, hasDeceptiveHiddenCharacters as Fn, isFileNotFoundError as Ft, RulesyncMcp as G, removeDirectoryStrict as Gt, RulesyncRule as H, readFileContent as Ht, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as I, hasEnclosingMarkOutsideKeycap as In, isFileSystemError as It, getRulesyncSourceCandidates as J, removeTempDirectory as Jt, RulesyncIgnore as K, removeFile as Kt, getLocalSkillDirNames as L, quoteForLog as Ln, isSymlink as Lt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as M, DEPRECATED_FEATURE_REPLACEMENTS as Mn, fileExists as Mt, caseFoldIdentity as N, formatError as Nn, getFileSize as Nt, CLAUDECODE_MEMORIES_DIR_NAME as O, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as On, checkPathTraversal as Ot, groupSpellingsByCaseFoldedIdentity as P, truncateText as Pn, getHomeDirectory as Pt, RulesyncCommandFrontmatterSchema as Q, writeFileBuffer as Qt, RulesyncSubagent as R, stripControlCharacters as Rn, listDirectoryEntryNames as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_RELATIVE_DIR_PATH as Sn, CLIError as St, CODEXCLI_DIR as T, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Tn, assertDirectoryIfExists as Tt, RulesyncRuleFrontmatterSchema as U, readFileContentOrNull as Ut, RulesyncSkillFrontmatterSchema as V, pathEscapesRoot as Vt, RulesyncPermissions as W, removeDirectory as Wt, parseJsonc as X, runWithDirectoryRollback as Xt, resolveRulesyncSourceWritePath as Y, resolvePath as Yt, RulesyncCommand as Z, toPosixPath as Zt, IgnoreProcessor as _, RULESYNC_MCP_RELATIVE_FILE_PATH as _n, fallbackLogger as _t, getProcessorRegistryEntry as a, FEATURE_SOURCE_TREE_ENTRIES as an, ConfigResolver as at, CommandsProcessor as b, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH 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_LOCAL_CONFIG_RELATIVE_FILE_PATH as gn, WarningCollectingLogger as gt, shortenToWidth as h, RULESYNC_IGNORE_RELATIVE_FILE_PATH as hn, JsonLogger as ht, inspectInputRoots as i, CURATED_RULES_FEATURE_SUBDIR as in, SKILL_FILE_NAME as it, FACTORYDROID_DIR as j, ALL_FEATURES_WITH_WILDCARD as jn, ensureDir as jt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as k, parseCommaSeparatedList as kn, createTempDirectory as kt, SubagentsProcessor as l, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as ln, ConfigFileSchema as lt, displayWidthOf as m, RULESYNC_HOOKS_RELATIVE_FILE_PATH as mn, ConsoleLogger as mt, formatSourceLoadFailure as n, PACKAGING_TOOL_TARGETS as nn, loadYaml as nt, convertFromTool as o, MAX_FILE_SIZE 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_MCP_SCHEMA_URL as vn, warnOnConflictingFlags as vt, CODEXCLI_BASH_RULES_FILE_NAME as w, RULESYNC_SKILLS_RELATIVE_DIR_PATH as wn, applyFileMode as wt, QWENCODE_DIR as x, RULESYNC_PERMISSIONS_SCHEMA_URL as xn, withWarnOnceScope as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as yn, withFallbackLoggerTarget as yt, RulesyncSubagentFrontmatterSchema as z, stripControlCharactersKeepingLineFeeds 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 };
79325
79707
 
79326
- //# sourceMappingURL=import-DoCUTXaU.js.map
79708
+ //# sourceMappingURL=import-DxRza3o_.js.map