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.
@@ -540,6 +540,7 @@ const rulesProcessorToolTargetTuple = [
540
540
  "musecode",
541
541
  "opencode",
542
542
  "pi",
543
+ "pool",
543
544
  "qwencode",
544
545
  "reasonix",
545
546
  "replit",
@@ -553,7 +554,6 @@ const rulesProcessorToolTargetTuple = [
553
554
  "zcode",
554
555
  "zed",
555
556
  "zoocode",
556
- "pool",
557
557
  "dsh"
558
558
  ];
559
559
  const ignoreProcessorToolTargetTuple = [
@@ -4116,6 +4116,10 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
4116
4116
  model: zod_mini.z.optional(safeString),
4117
4117
  args: zod_mini.z.optional(zod_mini.z.array(safeString)),
4118
4118
  metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
4119
+ source: zod_mini.z.optional(zod_mini.z.looseObject({
4120
+ source: safeString,
4121
+ ref: zod_mini.z.optional(safeString)
4122
+ })),
4119
4123
  if: zod_mini.z.optional(safeString),
4120
4124
  commandWindows: zod_mini.z.optional(safeString),
4121
4125
  additionalContextLimit: zod_mini.z.optional(zod_mini.z.int().check((0, zod_mini.nonnegative)())),
@@ -4876,10 +4880,12 @@ const ZCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONIC
4876
4880
  *
4877
4881
  * Bob reads `hooks` from `.bob/settings.json` (project) and
4878
4882
  * `~/.bob/settings/settings.json` (user) in the Claude-Code shape and fires
4879
- * five events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse and
4880
- * Stop. Only `command` hooks exist, and `matcher` (a regex over the tool
4881
- * name) applies to the two tool events.
4883
+ * seven events: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse,
4884
+ * PreCompact, PostCompact (both added by Bob Shell 2.0.3) and Stop. Handlers
4885
+ * are `command` or `https` hooks, and `matcher` (a regex over the tool name)
4886
+ * applies to the two tool events.
4882
4887
  *
4888
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
4883
4889
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
4884
4890
  */
4885
4891
  const BOB_HOOK_EVENTS = [
@@ -4887,6 +4893,8 @@ const BOB_HOOK_EVENTS = [
4887
4893
  "beforeSubmitPrompt",
4888
4894
  "preToolUse",
4889
4895
  "postToolUse",
4896
+ "preCompact",
4897
+ "postCompact",
4890
4898
  "stop"
4891
4899
  ];
4892
4900
  const CANONICAL_TO_BOB_EVENT_NAMES = {
@@ -4894,6 +4902,8 @@ const CANONICAL_TO_BOB_EVENT_NAMES = {
4894
4902
  beforeSubmitPrompt: "UserPromptSubmit",
4895
4903
  preToolUse: "PreToolUse",
4896
4904
  postToolUse: "PostToolUse",
4905
+ preCompact: "PreCompact",
4906
+ postCompact: "PostCompact",
4897
4907
  stop: "Stop"
4898
4908
  };
4899
4909
  const BOB_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_BOB_EVENT_NAMES).map(([k, v]) => [v, k]));
@@ -7305,13 +7315,30 @@ const DeepagentsExtensionsOverrideSchema = zod_mini.z.looseObject({
7305
7315
  ]))
7306
7316
  });
7307
7317
  /**
7318
+ * The `[mcp]` table of `~/.deepagents/config.toml`, where dcode keeps its
7319
+ * per-server trust for the MCP servers a project's `.mcp.json` /
7320
+ * `.deepagents/.mcp.json` declares. dcode reads these lists from the user-level
7321
+ * file only — never from a project file — which is exactly why the global
7322
+ * permissions adapter is the place to author them.
7323
+ *
7324
+ * `disabled_project_servers` names project MCP servers dcode always rejects;
7325
+ * a rejection wins over any approval or trust. The sibling
7326
+ * `enabled_project_server_approvals` is the fingerprint-bound approval store
7327
+ * the interactive prompt fills in, and `enabled_project_servers` is a
7328
+ * deprecated flat allowlist dcode ignores, so neither has a key here.
7329
+ *
7330
+ * @see https://docs.langchain.com/oss/deepagents/code/configuration
7331
+ */
7332
+ const DeepagentsMcpOverrideSchema = zod_mini.z.looseObject({ disabled_project_servers: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())) });
7333
+ /**
7308
7334
  * The `[shell].allow_list` array itself is rulesync-owned, driven by the
7309
7335
  * shared `permission.bash` block, so it has no key here.
7310
7336
  */
7311
7337
  const DeepagentsPermissionsOverrideSchema = zod_mini.z.looseObject({
7312
7338
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
7313
7339
  startup: zod_mini.z.optional(DeepagentsStartupOverrideSchema),
7314
- extensions: zod_mini.z.optional(DeepagentsExtensionsOverrideSchema)
7340
+ extensions: zod_mini.z.optional(DeepagentsExtensionsOverrideSchema),
7341
+ mcp: zod_mini.z.optional(DeepagentsMcpOverrideSchema)
7315
7342
  });
7316
7343
  /**
7317
7344
  * The actions Junie's allowlist accepts. Verified against the shipped Junie
@@ -9625,7 +9652,13 @@ const RulesyncSkillFrontmatterSchema = zod_mini.z.looseObject({
9625
9652
  "user-invocable": zod_mini.z.optional(zod_mini.z.boolean()),
9626
9653
  "allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))
9627
9654
  })),
9628
- commandcode: zod_mini.z.optional(zod_mini.z.looseObject({})),
9655
+ commandcode: zod_mini.z.optional(zod_mini.z.looseObject({
9656
+ license: zod_mini.z.optional(zod_mini.z.string()),
9657
+ compatibility: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})])),
9658
+ metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
9659
+ "disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
9660
+ "user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
9661
+ })),
9629
9662
  cortexcode: zod_mini.z.optional(zod_mini.z.looseObject({})),
9630
9663
  continue: zod_mini.z.optional(zod_mini.z.looseObject({})),
9631
9664
  tabnine: zod_mini.z.optional(zod_mini.z.looseObject({})),
@@ -9869,7 +9902,7 @@ async function getLocalSkillDirNames(sourceTree) {
9869
9902
  *
9870
9903
  * The rulesync skill frontmatter exposes a root-level `disable-model-invocation`
9871
9904
  * default that applies to every tool supporting the flag (claudecode, copilot,
9872
- * copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh). Each tool's own section may override that
9905
+ * copilotcli, crush, cursor, zed, pi, qwencode, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that
9873
9906
  * default with a per-target value. A defined section value (including `false`)
9874
9907
  * always wins over the root default.
9875
9908
  *
@@ -9888,7 +9921,7 @@ function resolveDisableModelInvocation({ rootFrontmatter, section }) {
9888
9921
  *
9889
9922
  * The rulesync skill frontmatter exposes a root-level `user-invocable` default
9890
9923
  * that applies to every tool supporting the flag (claudecode, copilot,
9891
- * copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh). Each tool's own section may override that default with a
9924
+ * copilotcli, crush, cursor, qwencode, vibe, grokcli, factorydroid, dsh, commandcode). Each tool's own section may override that default with a
9892
9925
  * per-target value. A defined section value (including `false`) always wins
9893
9926
  * over the root default.
9894
9927
  *
@@ -15456,6 +15489,7 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
15456
15489
  const COMMANDCODE_DIR = ".commandcode";
15457
15490
  const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
15458
15491
  const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
15492
+ const COMMANDCODE_SETTINGS_LOCAL_FILE_NAME = "settings.local.json";
15459
15493
  const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
15460
15494
  const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
15461
15495
  const COMMANDCODE_COMMANDS_DIR_PATH = (0, node_path.join)(COMMANDCODE_DIR, "commands");
@@ -20670,6 +20704,18 @@ function isToolMatcherEntry(x) {
20670
20704
  return true;
20671
20705
  }
20672
20706
  /**
20707
+ * Convert a canonical `timeout` (seconds) into the tool's unit.
20708
+ */
20709
+ function emitTimeout({ timeout, converterConfig }) {
20710
+ return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? Math.round(timeout * 1e3) : timeout;
20711
+ }
20712
+ /**
20713
+ * Convert a tool `timeout` back into the canonical unit (seconds).
20714
+ */
20715
+ function importTimeout({ timeout, converterConfig }) {
20716
+ return converterConfig.timeoutUnit === "milliseconds" && Number.isFinite(timeout) ? timeout / 1e3 : timeout;
20717
+ }
20718
+ /**
20673
20719
  * Filter the shared canonical hooks to the supported events and merge tool overrides on top.
20674
20720
  */
20675
20721
  function buildEffectiveHooks$1({ config, toolOverrideHooks, supportedEvents }) {
@@ -20810,6 +20856,7 @@ const isEmittableString = ({ value }) => isNonEmptyString(value);
20810
20856
  const isEmittableArray = ({ value }) => isStringArray$1(value);
20811
20857
  const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
20812
20858
  const isImportableArray = ({ value }) => isSafeStringArray(value);
20859
+ const isEmittableObject = ({ value }) => isPlainObject$1(value);
20813
20860
  /**
20814
20861
  * The canonical schema of one hook field, looked up by name. Read off the
20815
20862
  * schema's own shape rather than by parsing a one-field object, so a `canonical`
@@ -20844,11 +20891,17 @@ const isImportableNumber = ({ value, canonical }) => Number.isFinite(value) && s
20844
20891
  value,
20845
20892
  canonical
20846
20893
  });
20894
+ const isImportableObject = ({ value, canonical }) => isPlainObject$1(value) && satisfiesCanonicalField({
20895
+ value,
20896
+ canonical
20897
+ });
20847
20898
  /**
20848
20899
  * Say which rule the value broke, so the warning names the actual constraint
20849
20900
  * rather than asserting a canonical rejection that may not be the reason. A
20850
20901
  * closed enum lists its members; a rule carrying its own message (the
20851
- * control-character check behind `safeString`) reuses it.
20902
+ * control-character check behind `safeString`) reuses it. An object field
20903
+ * names the offending key, since zod's generic "Invalid input" would not tell
20904
+ * the reader which key is missing or malformed.
20852
20905
  */
20853
20906
  function describeScalarConstraint({ canonical, value }) {
20854
20907
  const schema = canonicalFieldSchema(canonical);
@@ -20856,13 +20909,22 @@ function describeScalarConstraint({ canonical, value }) {
20856
20909
  if (result === void 0 || result.success) return `it is not a value this field carries through.`;
20857
20910
  const issue = result.error.issues[0];
20858
20911
  if (issue === void 0) return `it is not a value the canonical "${canonical}" field accepts.`;
20859
- return `it does not satisfy the canonical "${canonical}" field: ${issue.message}.`;
20912
+ return `it does not satisfy the canonical "${canonical}" field${issue.path.length > 0 ? ` at ${quoteValueForWarning(issue.path.map(String).join("."))}` : ""}: ${issue.message}.`;
20860
20913
  }
20861
20914
  const describeInvalidScalar = ({ tool, canonical, value }) => `Dropping "${tool}" (${quoteValueForWarning(value)}) while importing a hook: ${describeScalarConstraint({
20862
20915
  canonical,
20863
20916
  value
20864
20917
  })} Importing it would fail validation on the next run.`;
20865
20918
  const describeInvalidArray = ({ tool }) => `Dropping "${tool}" while importing a hook: it must be a list of strings without newline, carriage return or NUL characters.`;
20919
+ /**
20920
+ * An object is quoted by its offending key rather than its value: the payload
20921
+ * can be arbitrarily large, and the missing or malformed key is what the
20922
+ * reader needs to fix.
20923
+ */
20924
+ const describeInvalidObject = ({ tool, canonical, value }) => isPlainObject$1(value) ? `Dropping "${tool}" while importing a hook: ${describeScalarConstraint({
20925
+ canonical,
20926
+ value
20927
+ })} Importing it would fail validation on the next run.` : `Dropping "${tool}" while importing a hook: it must be an object.`;
20866
20928
  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.`;
20867
20929
  /**
20868
20930
  * Check a value against the shape its field documents. A string field also
@@ -20968,6 +21030,14 @@ function emitAllPassthroughFields({ def, hookType, eventName, converterConfig, w
20968
21030
  fields: converterConfig.recordPassthroughFields ?? [],
20969
21031
  isValid: isEmittableRecord,
20970
21032
  warn
21033
+ }),
21034
+ ...emitPassthroughFields({
21035
+ def,
21036
+ hookType,
21037
+ eventName,
21038
+ fields: converterConfig.objectPassthroughFields ?? [],
21039
+ isValid: isEmittableObject,
21040
+ warn
20971
21041
  })
20972
21042
  };
20973
21043
  }
@@ -20991,9 +21061,12 @@ function buildToolHooks({ defs, eventName, converterConfig, warn }) {
20991
21061
  converterConfig,
20992
21062
  warn
20993
21063
  }),
20994
- type: hookType,
21064
+ type: converterConfig.hookTypeNames?.[hookType] ?? hookType,
20995
21065
  ...command !== void 0 && command !== null && { command },
20996
- ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
21066
+ ...def.timeout !== void 0 && def.timeout !== null && { timeout: emitTimeout({
21067
+ timeout: def.timeout,
21068
+ converterConfig
21069
+ }) },
20997
21070
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
20998
21071
  ...emitTypePayloadFields({
20999
21072
  def,
@@ -21115,6 +21188,20 @@ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
21115
21188
  function isImportedHookType(value) {
21116
21189
  return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
21117
21190
  }
21191
+ /**
21192
+ * The canonical hook type a tool-side `type` value denotes: the tool's own
21193
+ * spelling from {@link ToolHooksConverterConfig.hookTypeNames} when it has
21194
+ * one, otherwise the value itself when it is a canonical type, and `command`
21195
+ * for anything else (the same coercion as before the renames existed).
21196
+ */
21197
+ function resolveImportedHookType({ value, converterConfig }) {
21198
+ const names = converterConfig.hookTypeNames;
21199
+ if (names !== void 0 && typeof value === "string") {
21200
+ for (const [canonical, tool] of Object.entries(names)) if (tool === value) return canonical;
21201
+ if (Object.hasOwn(names, value)) return "command";
21202
+ }
21203
+ return isImportedHookType(value) ? value : "command";
21204
+ }
21118
21205
  function isStringRecord(value) {
21119
21206
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
21120
21207
  return Object.values(value).every((v) => typeof v === "string");
@@ -21280,6 +21367,14 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21280
21367
  isValid: isEmittableRecord,
21281
21368
  describeInvalid: describeInvalidRecord,
21282
21369
  warn
21370
+ }),
21371
+ ...importPassthroughFields({
21372
+ h,
21373
+ hookType,
21374
+ fields: converterConfig.objectPassthroughFields ?? [],
21375
+ isValid: isImportableObject,
21376
+ describeInvalid: describeInvalidObject,
21377
+ warn
21283
21378
  })
21284
21379
  };
21285
21380
  }
@@ -21287,7 +21382,10 @@ function importAllPassthroughFields({ h, hookType, converterConfig, warn }) {
21287
21382
  * Convert a single tool hook record into a canonical hook definition.
21288
21383
  */
21289
21384
  function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21290
- const hookType = isImportedHookType(h.type) ? h.type : "command";
21385
+ const hookType = resolveImportedHookType({
21386
+ value: h.type,
21387
+ converterConfig
21388
+ });
21291
21389
  const command = importCanonicalString({
21292
21390
  value: stripCommandPrefix({
21293
21391
  command: h.command,
@@ -21296,7 +21394,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig, warn }) {
21296
21394
  canonical: "command",
21297
21395
  warn
21298
21396
  });
21299
- const timeout = typeof h.timeout === "number" ? h.timeout : void 0;
21397
+ const timeout = typeof h.timeout === "number" ? importTimeout({
21398
+ timeout: h.timeout,
21399
+ converterConfig
21400
+ }) : void 0;
21300
21401
  const prompt = importCanonicalString({
21301
21402
  value: h.prompt,
21302
21403
  canonical: "prompt",
@@ -21429,7 +21530,10 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig, warn }) {
21429
21530
  const skipReason = describeHookSkipReason({
21430
21531
  h,
21431
21532
  rawEntry,
21432
- hookType: isImportedHookType(h.type) ? h.type : "command",
21533
+ hookType: resolveImportedHookType({
21534
+ value: h.type,
21535
+ converterConfig
21536
+ }),
21433
21537
  converterConfig
21434
21538
  });
21435
21539
  if (skipReason !== void 0) {
@@ -21914,6 +22018,7 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
21914
22018
  "beforeSubmitPrompt"
21915
22019
  ]),
21916
22020
  supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
22021
+ timeoutUnit: "milliseconds",
21917
22022
  arrayPassthroughFields: [{
21918
22023
  canonical: "args",
21919
22024
  tool: "args"
@@ -22512,9 +22617,12 @@ const BOB_SPEC = {
22512
22617
  noMatcherEvents: /* @__PURE__ */ new Set([
22513
22618
  "sessionStart",
22514
22619
  "beforeSubmitPrompt",
22620
+ "preCompact",
22621
+ "postCompact",
22515
22622
  "stop"
22516
22623
  ]),
22517
- supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
22624
+ supportedHookTypes: /* @__PURE__ */ new Set(["command", "http"]),
22625
+ hookTypeNames: { http: "https" },
22518
22626
  wildcardMatcherMeansAll: true
22519
22627
  }
22520
22628
  };
@@ -22525,10 +22633,13 @@ const BOB_SPEC = {
22525
22633
  * `<project>/.bob/settings.json` (project scope) and
22526
22634
  * `~/.bob/settings/settings.json` (user scope) — in the Claude-Code shape:
22527
22635
  * `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{ "type": "command",
22528
- * "command": "...", "timeout"?: <seconds> }] }] }`. The file also holds
22529
- * settings rulesync does not own, so generation merges the `hooks` key into it
22530
- * (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting the file.
22636
+ * "command": "...", "timeout"?: <seconds> }] }] }`, where a handler may
22637
+ * instead be `{ "type": "https", "url": "...", "timeout"?: <seconds> }`. The
22638
+ * file also holds settings rulesync does not own, so generation merges the
22639
+ * `hooks` key into it (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting
22640
+ * the file.
22531
22641
  *
22642
+ * @see https://bob.ibm.com/docs/shell/configuration/lifecycle-hooks
22532
22643
  * @see https://bob.ibm.com/docs/ide/configuration/lifecycle-hooks
22533
22644
  */
22534
22645
  var BobHooks = class extends SettingsJsonHooks {
@@ -23958,6 +24069,11 @@ const CORTEXCODE_SPEC = {
23958
24069
  booleanPassthroughFields: [{
23959
24070
  canonical: "enabled",
23960
24071
  tool: "enabled"
24072
+ }],
24073
+ objectPassthroughFields: [{
24074
+ canonical: "source",
24075
+ tool: "source",
24076
+ commandOnly: true
23961
24077
  }]
23962
24078
  }
23963
24079
  };
@@ -23968,8 +24084,9 @@ const CORTEXCODE_SPEC = {
23968
24084
  * (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
23969
24085
  * scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
23970
24086
  * "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
23971
- * <seconds>, "enabled"?: <boolean> }] }] }`. The project file also holds
23972
- * settings rulesync does not own, so generation merges the `hooks` key into
24087
+ * <seconds>, "enabled"?: <boolean>, "source"?: { "source", "ref"? } }] }] }`.
24088
+ * The project file also holds settings rulesync does not own, so generation
24089
+ * merges the `hooks` key into
23973
24090
  * either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
23974
24091
  * neither file is removed wholesale (hooks.json sits in the CLI-owned
23975
24092
  * `~/.snowflake/cortex/` tree).
@@ -28615,7 +28732,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
28615
28732
  supportsImport: true
28616
28733
  },
28617
28734
  supportedEvents: BOB_HOOK_EVENTS,
28618
- supportedHookTypes: ["command"],
28735
+ supportedHookTypes: ["command", "http"],
28619
28736
  supportsMatcher: true,
28620
28737
  matcherEvents: ["preToolUse", "postToolUse"]
28621
28738
  }],
@@ -31429,6 +31546,50 @@ function parseBobMcpConfig({ fileContent, relativePath }) {
31429
31546
  /** The `type` Bob IDE writes for a streamable HTTP server. */
31430
31547
  const BOB_STREAMABLE_HTTP_TYPE = "streamable-http";
31431
31548
  /**
31549
+ * The OAuth keys Bob reads flat on a server entry, next to `oauth` (a
31550
+ * boolean: `true` forces the flow on, `false` off, absent lets Bob detect it).
31551
+ * The canonical shape is Claude Code's nested `oauth: { clientId, ... }`
31552
+ * object, so the two are translated in both directions.
31553
+ * @see https://bob.ibm.com/docs/ide/configuration/mcp/mcp-oauth
31554
+ */
31555
+ const BOB_FLAT_OAUTH_KEYS = [
31556
+ "clientId",
31557
+ "clientSecret",
31558
+ "scope"
31559
+ ];
31560
+ /**
31561
+ * Flatten the canonical `oauth` object onto a Bob server entry: `oauth: true`
31562
+ * plus the string-valued keys Bob documents. A flat key the server already
31563
+ * carries (authored in Bob's own spelling) is kept over the nested one. Other
31564
+ * nested keys (`callbackPort`, `redirectUri`) have no Bob counterpart and are
31565
+ * dropped rather than nested under a key Bob reads as a boolean.
31566
+ */
31567
+ function flattenOauthForBob({ oauth, converted }) {
31568
+ converted.oauth = true;
31569
+ for (const key of BOB_FLAT_OAUTH_KEYS) {
31570
+ const value = oauth[key];
31571
+ if (typeof value === "string" && converted[key] === void 0) converted[key] = value;
31572
+ }
31573
+ }
31574
+ /**
31575
+ * Reverse of {@link flattenOauthForBob}: gather Bob's flat OAuth keys into the
31576
+ * canonical `oauth` object. `oauth: true` always folds (even with no keys, so
31577
+ * the explicit enable survives the round-trip as an empty object); with no
31578
+ * `oauth` key at all the fold happens only when a flat key is present, since
31579
+ * that is what makes the entry an OAuth one for Bob; `oauth: false` and
31580
+ * anything else leave the entry as it is.
31581
+ */
31582
+ function nestOauthFromBob(converted) {
31583
+ const flat = BOB_FLAT_OAUTH_KEYS.filter((key) => typeof converted[key] === "string");
31584
+ if (!(converted.oauth === true || converted.oauth === void 0 && flat.length > 0)) return;
31585
+ const oauth = {};
31586
+ for (const key of flat) {
31587
+ oauth[key] = converted[key];
31588
+ delete converted[key];
31589
+ }
31590
+ converted.oauth = oauth;
31591
+ }
31592
+ /**
31432
31593
  * The remote transport Bob IDE reads a server as, spelled the way it writes
31433
31594
  * it: `streamable-http` needs an explicit `type`, while SSE (legacy) is a bare
31434
31595
  * `url` with no `type`. `http` is the canonical rulesync alias for streamable
@@ -31451,7 +31612,9 @@ function asBobRemoteType(stated, url) {
31451
31612
  * `headers`, `timeout`, `alwaysAllow` and `disabled` pass through, as Bob
31452
31613
  * documents all of them, with prototype-pollution keys dropped at every
31453
31614
  * nesting level (Bob spreads `env` and `headers` into the process environment
31454
- * and the HTTP requests).
31615
+ * and the HTTP requests). The canonical `oauth` object is flattened onto the
31616
+ * entry as `oauth: true` plus `clientId` / `clientSecret` / `scope`, the keys
31617
+ * Bob reads; a boolean `oauth` passes through as Bob's own on/off switch.
31455
31618
  *
31456
31619
  * Bob Shell documents the same file with an `httpURL` key instead of
31457
31620
  * `type` + `url` for streamable HTTP. rulesync writes the IDE spelling (the two
@@ -31517,10 +31680,16 @@ function convertToBobFormat(mcpServers, logger) {
31517
31680
  converted.command = command;
31518
31681
  if (args.length > 0) converted.args = args;
31519
31682
  }
31520
- for (const [key, value] of Object.entries(rest)) {
31683
+ const { oauth, ...passthrough } = rest;
31684
+ for (const [key, value] of Object.entries(passthrough)) {
31521
31685
  if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
31522
31686
  converted[key] = omitPrototypePollutionKeysDeep(value);
31523
31687
  }
31688
+ if (isRecord$1(oauth)) flattenOauthForBob({
31689
+ oauth,
31690
+ converted
31691
+ });
31692
+ else if (oauth !== void 0) converted.oauth = oauth;
31524
31693
  result[serverName] = converted;
31525
31694
  }
31526
31695
  return result;
@@ -31530,9 +31699,10 @@ function convertToBobFormat(mcpServers, logger) {
31530
31699
  * (`type: "streamable-http"` + `url`) is already canonical and passes through;
31531
31700
  * the Bob Shell spelling `httpURL` becomes `url` with `type: "http"`; a bare
31532
31701
  * `url` is an SSE server in Bob, so it gains `type: "sse"` to keep that reading
31533
- * on the next generate. Every other field passes through with its
31534
- * prototype-pollution keys dropped at every nesting level, mirroring the
31535
- * generate side's handling of `env` / `headers`.
31702
+ * on the next generate. Bob's flat OAuth keys are gathered back into the
31703
+ * canonical `oauth` object (see {@link nestOauthFromBob}). Every other field
31704
+ * passes through with its prototype-pollution keys dropped at every nesting
31705
+ * level, mirroring the generate side's handling of `env` / `headers`.
31536
31706
  */
31537
31707
  function convertFromBobFormat(mcpServers) {
31538
31708
  if (!isMcpServers(mcpServers)) return {};
@@ -31553,6 +31723,7 @@ function convertFromBobFormat(mcpServers) {
31553
31723
  converted.url = httpURL;
31554
31724
  converted.type = "http";
31555
31725
  } else if (typeof converted.url === "string" && converted.type === void 0) converted.type = "sse";
31726
+ nestOauthFromBob(converted);
31556
31727
  result[serverName] = converted;
31557
31728
  }
31558
31729
  return result;
@@ -32577,7 +32748,10 @@ function asContinueRemoteType(stated, url) {
32577
32748
  * file with a strict union — one server that matches neither shape (an
32578
32749
  * unknown transport, a non-string `env` value) makes Continue drop every
32579
32750
  * server in the file — so only the documented keys are emitted and anything
32580
- * else (`timeout`, `oauth`, rulesync-only fields) is left out. `envFile` is
32751
+ * else is left out. The canonical keys a user would expect to take effect
32752
+ * (`cwd`, `timeout`, `oauth`) are dropped with a warning, since the JSON
32753
+ * schema has no place for them (the YAML block form does, but Rulesync writes
32754
+ * the JSON file); rulesync-only fields are dropped silently. `envFile` is
32581
32755
  * accepted by Continue's schema but ignored with a warning at load time, so
32582
32756
  * it is dropped here with a warning instead of being written as if it worked.
32583
32757
  *
@@ -32613,6 +32787,17 @@ function convertToContinueFormat(mcpServers, logger) {
32613
32787
  }
32614
32788
  return result;
32615
32789
  }
32790
+ /**
32791
+ * Warn for each canonical key Continue's JSON MCP schema cannot carry, so a
32792
+ * dropped `cwd`, `timeout` or `oauth` does not go unnoticed in a config that
32793
+ * otherwise looks complete.
32794
+ */
32795
+ function warnUnsupportedContinueKeys({ serverName, serverConfig, keys, logger }) {
32796
+ for (const key of keys) {
32797
+ if (serverConfig[key] === void 0) continue;
32798
+ logger?.warn(`Continue's MCP config has no "${key}" field, so the ${key} of server ${quoteValueForWarning(serverName)} was not written.`);
32799
+ }
32800
+ }
32616
32801
  function convertRemoteServer({ serverName, serverConfig, logger }) {
32617
32802
  const url = resolveRemoteMcpUrl(serverConfig);
32618
32803
  if (!url) {
@@ -32640,6 +32825,12 @@ function convertRemoteServer({ serverName, serverConfig, logger }) {
32640
32825
  url
32641
32826
  };
32642
32827
  if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
32828
+ warnUnsupportedContinueKeys({
32829
+ serverName,
32830
+ serverConfig,
32831
+ keys: ["timeout", "oauth"],
32832
+ logger
32833
+ });
32643
32834
  return converted;
32644
32835
  }
32645
32836
  function convertStdioServer({ serverName, serverConfig, logger }) {
@@ -32660,6 +32851,12 @@ function convertStdioServer({ serverName, serverConfig, logger }) {
32660
32851
  if (args.length > 0) converted.args = args;
32661
32852
  if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
32662
32853
  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.`);
32854
+ warnUnsupportedContinueKeys({
32855
+ serverName,
32856
+ serverConfig,
32857
+ keys: ["cwd", "timeout"],
32858
+ logger
32859
+ });
32663
32860
  return converted;
32664
32861
  }
32665
32862
  /**
@@ -45158,6 +45355,27 @@ const DEEPAGENTS_EXTENSION_TRUST_POLICIES = [
45158
45355
  * @see https://github.com/langchain-ai/deepagents `config_manifest.py`
45159
45356
  */
45160
45357
  const DEEPAGENTS_EXTENSIONS_ENABLED_DEFAULT = true;
45358
+ /**
45359
+ * `[mcp]` holds dcode's per-server trust for the MCP servers a project's
45360
+ * `.mcp.json` / `.deepagents/.mcp.json` declares. `load_mcp_server_trust_lists`
45361
+ * reads it from the user-level `config.toml` only — never from a project file —
45362
+ * so the global adapter is the one place rulesync can author it.
45363
+ */
45364
+ const MCP_TABLE_KEY = "mcp";
45365
+ /**
45366
+ * Keys lifted back into the override on import. `disabled_project_servers` is
45367
+ * the deny list: a name on it is always rejected, over any approval or trust.
45368
+ */
45369
+ const DEEPAGENTS_MCP_KEYS = ["disabled_project_servers"];
45370
+ const MCP_DISABLED_PROJECT_SERVERS_KEY = "disabled_project_servers";
45371
+ /**
45372
+ * dcode's own store of the project servers the user approved at the prompt,
45373
+ * each bound to a repository or worktree and a definition fingerprint. It is
45374
+ * machine-local state and a way to pre-approve a project's servers, so a
45375
+ * repository's `.rulesync/permissions.jsonc` must not reach it: generate drops
45376
+ * the key here, and import never lifts it (`DEEPAGENTS_MCP_KEYS`).
45377
+ */
45378
+ const MCP_APPROVALS_KEY = "enabled_project_server_approvals";
45161
45379
  const ALLOW_ALL_SENTINEL = "all";
45162
45380
  const RECOMMENDED_SENTINEL = "recommended";
45163
45381
  const GLOB_CHARACTERS_PATTERN = /[*?[\]]/;
@@ -45230,7 +45448,9 @@ const TRAILING_ARGUMENT_WILDCARD_PATTERN = /:\*$/;
45230
45448
  * lifted back into the override on import. The same override carries
45231
45449
  * `[extensions].enabled` and `[extensions].trust` (`ask` / `always` / `never`),
45232
45450
  * which decide whether the Python in a checked-out project's
45233
- * `.deepagents/extensions/` is imported into the agent process.
45451
+ * `.deepagents/extensions/` is imported into the agent process, and
45452
+ * `[mcp].disabled_project_servers`, the names of project MCP servers dcode
45453
+ * always rejects.
45234
45454
  *
45235
45455
  * On **import** the allowlist comes back as `bash` `allow` rules named by the
45236
45456
  * executable — skipping any entry dcode could not match in the first place, so
@@ -45328,17 +45548,9 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45328
45548
  if (Object.keys(shell).length > 0) settings[SHELL_TABLE_KEY] = shell;
45329
45549
  else delete settings[SHELL_TABLE_KEY];
45330
45550
  }
45331
- const startupOverride = config.deepagents?.startup;
45332
- if (isPlainObject$1(startupOverride) && Object.keys(startupOverride).length > 0) mergeStartupOverride({
45333
- settings,
45334
- startupOverride,
45335
- filePath,
45336
- logger
45337
- });
45338
- const extensionsOverride = config.deepagents?.extensions;
45339
- if (isPlainObject$1(extensionsOverride) && Object.keys(extensionsOverride).length > 0) mergeExtensionsOverride({
45551
+ mergeDeepagentsOverrides({
45340
45552
  settings,
45341
- extensionsOverride,
45553
+ override: config.deepagents,
45342
45554
  filePath,
45343
45555
  logger
45344
45556
  });
@@ -45370,10 +45582,15 @@ var DeepagentsPermissions = class DeepagentsPermissions extends ToolPermissions
45370
45582
  extensions: isPlainObject$1(settings[EXTENSIONS_TABLE_KEY]) ? settings[EXTENSIONS_TABLE_KEY] : {},
45371
45583
  selfPath
45372
45584
  });
45585
+ const mcpOverride = liftMcpOverride({
45586
+ mcp: isPlainObject$1(settings[MCP_TABLE_KEY]) ? settings[MCP_TABLE_KEY] : {},
45587
+ selfPath
45588
+ });
45373
45589
  const result = { ...config };
45374
45590
  const deepagents = {};
45375
45591
  if (Object.keys(startupOverride).length > 0) deepagents.startup = startupOverride;
45376
45592
  if (Object.keys(extensionsOverride).length > 0) deepagents.extensions = extensionsOverride;
45593
+ if (Object.keys(mcpOverride).length > 0) deepagents.mcp = mcpOverride;
45377
45594
  if (Object.keys(deepagents).length > 0) result.deepagents = deepagents;
45378
45595
  return RulesyncPermissions.fromImportedFileContent({
45379
45596
  outputRoot: getDeepagentsRulesyncOutputRoot({
@@ -45481,7 +45698,7 @@ function warnAboutUnwrittenBashRules({ allowAll, requestedAllowAll, askPatterns,
45481
45698
  * read — a wrapper rather than the bare value, so a legitimately falsy setting
45482
45699
  * is not mistaken for a rejection.
45483
45700
  */
45484
- function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
45701
+ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath, describeRejection = `deepagents-cli falls back to its own default for a '[${tableKey}]' value it cannot read` }) {
45485
45702
  const override = {};
45486
45703
  const rejected = [];
45487
45704
  for (const key of keys) {
@@ -45494,7 +45711,7 @@ function liftOverrideTable({ table, tableKey, keys, normalize, selfPath }) {
45494
45711
  if (normalized) override[key] = normalized.value;
45495
45712
  else rejected.push(`${key} = ${JSON.stringify(value)}`);
45496
45713
  }
45497
- 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.`);
45714
+ if (rejected.length > 0) warnWithFallback(void 0, `${describeRejection}, so ${rejected.join(", ")} in ${selfPath} ${rejected.length === 1 ? "was" : "were"} not imported.`);
45498
45715
  return override;
45499
45716
  }
45500
45717
  /**
@@ -45542,6 +45759,37 @@ function liftExtensionsOverride({ extensions, selfPath }) {
45542
45759
  });
45543
45760
  }
45544
45761
  /**
45762
+ * Read a `[mcp]` name list the way `_toml_str_list` reads it: a bare string
45763
+ * is split on commas, names are trimmed and empty ones dropped, and a
45764
+ * non-string element of a list is dropped while the names around it survive.
45765
+ * Anything else (a number, a table, a bool) is a value dcode cannot read as
45766
+ * names at all, so `null` says to leave it behind.
45767
+ */
45768
+ function readMcpServerNames(value) {
45769
+ if (typeof value === "string") return value.split(",").map((name) => name.trim()).filter((name) => name.length > 0);
45770
+ if (!Array.isArray(value)) return null;
45771
+ return value.filter((name) => typeof name === "string").map((name) => name.trim()).filter((name) => name.length > 0);
45772
+ }
45773
+ /**
45774
+ * Lift the `[mcp]` keys rulesync models back into the `deepagents` override.
45775
+ * The deny list comes back as the array dcode resolves it to, so the
45776
+ * comma-separated string form it also accepts does not survive as one bogus
45777
+ * name in a permissions file the canonical schema types as `string[]`.
45778
+ */
45779
+ function liftMcpOverride({ mcp, selfPath }) {
45780
+ return liftOverrideTable({
45781
+ table: mcp,
45782
+ tableKey: MCP_TABLE_KEY,
45783
+ keys: DEEPAGENTS_MCP_KEYS,
45784
+ normalize: ({ value }) => {
45785
+ const names = readMcpServerNames(value);
45786
+ return names ? { value: names } : null;
45787
+ },
45788
+ selfPath,
45789
+ 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`
45790
+ });
45791
+ }
45792
+ /**
45545
45793
  * Merge one block of the `deepagents` override into its `config.toml` table,
45546
45794
  * preserving every other key of that table. A table that is not a table at all
45547
45795
  * is something rulesync did not write and cannot merge into, so it is left
@@ -45590,6 +45838,38 @@ function warnAboutUncheckedKeys({ tableKey, knownKeys, writtenKeys, filePath, lo
45590
45838
  if (unknownKeys.length === 0) return;
45591
45839
  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.`);
45592
45840
  }
45841
+ /** An override block with something in it to write. */
45842
+ function isSet(block) {
45843
+ return isPlainObject$1(block) && Object.keys(block).length > 0;
45844
+ }
45845
+ /**
45846
+ * Merge each block of the `deepagents` override into its `config.toml` table.
45847
+ * A block that is absent, not a table, or empty has nothing to write and is
45848
+ * skipped without touching the table it would have gone into.
45849
+ */
45850
+ function mergeDeepagentsOverrides({ settings, override, filePath, logger }) {
45851
+ const startupOverride = override?.startup;
45852
+ if (isSet(startupOverride)) mergeStartupOverride({
45853
+ settings,
45854
+ startupOverride,
45855
+ filePath,
45856
+ logger
45857
+ });
45858
+ const extensionsOverride = override?.extensions;
45859
+ if (isSet(extensionsOverride)) mergeExtensionsOverride({
45860
+ settings,
45861
+ extensionsOverride,
45862
+ filePath,
45863
+ logger
45864
+ });
45865
+ const mcpOverride = override?.mcp;
45866
+ if (isSet(mcpOverride)) mergeMcpOverride({
45867
+ settings,
45868
+ mcpOverride,
45869
+ filePath,
45870
+ logger
45871
+ });
45872
+ }
45593
45873
  /**
45594
45874
  * Merge the `deepagents.startup` override into `[startup]`.
45595
45875
  */
@@ -45623,6 +45903,40 @@ function mergeExtensionsOverride({ settings, extensionsOverride, filePath, logge
45623
45903
  });
45624
45904
  }
45625
45905
  /**
45906
+ * Merge the `deepagents.mcp` override into `[mcp]`. The approval store is
45907
+ * dropped (see `MCP_APPROVALS_KEY`); the deprecated flat
45908
+ * `enabled_project_servers` is not modeled but passes through like any other
45909
+ * unknown key, named by the unchecked-key warning — dcode ignores it anyway.
45910
+ */
45911
+ function mergeMcpOverride({ settings, mcpOverride, filePath, logger }) {
45912
+ mergeOverrideTable({
45913
+ settings,
45914
+ tableKey: MCP_TABLE_KEY,
45915
+ override: mcpOverride,
45916
+ knownKeys: DEEPAGENTS_MCP_KEYS,
45917
+ isDroppedKey: (key) => key === MCP_APPROVALS_KEY,
45918
+ warnAboutRelaxations: warnAboutMcpRelaxations,
45919
+ filePath,
45920
+ logger
45921
+ });
45922
+ }
45923
+ /**
45924
+ * Warn when the `deepagents` mcp override re-enables project MCP servers the
45925
+ * machine's global config had rejected. The deny list is written as a whole,
45926
+ * so a shorter list from a repository's `.rulesync/permissions.jsonc` would
45927
+ * otherwise silently lift a rejection the user had put in place — the very
45928
+ * fail-open dcode's own env merge refuses to allow.
45929
+ */
45930
+ function warnAboutMcpRelaxations({ override, previous, filePath, logger }) {
45931
+ 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.`);
45932
+ const written = override[MCP_DISABLED_PROJECT_SERVERS_KEY];
45933
+ if (written === void 0 || written === null) return;
45934
+ const kept = new Set(readMcpServerNames(written) ?? []);
45935
+ const reEnabled = (readMcpServerNames(previous[MCP_DISABLED_PROJECT_SERVERS_KEY]) ?? []).filter((name) => !kept.has(name));
45936
+ if (reEnabled.length === 0) return;
45937
+ 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.`);
45938
+ }
45939
+ /**
45626
45940
  * Name the value an override key replaces, so a setting the user had turned
45627
45941
  * down is visible as such rather than reported as a bare new value.
45628
45942
  */
@@ -57375,7 +57689,12 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
57375
57689
  //#region src/features/skills/commandcode-skill.ts
57376
57690
  const CommandcodeSkillFrontmatterSchema = zod_mini.z.looseObject({
57377
57691
  name: zod_mini.z.string(),
57378
- description: zod_mini.z.string()
57692
+ description: zod_mini.z.string(),
57693
+ license: zod_mini.z.optional(zod_mini.z.string()),
57694
+ compatibility: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})])),
57695
+ metadata: zod_mini.z.optional(zod_mini.z.looseObject({})),
57696
+ "disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean()),
57697
+ "user-invocable": zod_mini.z.optional(zod_mini.z.boolean())
57379
57698
  });
57380
57699
  /**
57381
57700
  * Represents a Command Code skill directory.
@@ -57458,10 +57777,35 @@ var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
57458
57777
  const settablePaths = CommandcodeSkill.getSettablePaths({ global });
57459
57778
  const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
57460
57779
  const { name: _sectionName, description: _sectionDescription, ...commandcodeSection } = rulesyncFrontmatter.commandcode ?? {};
57780
+ const license = resolveLicense({
57781
+ rootFrontmatter: rulesyncFrontmatter,
57782
+ section: commandcodeSection
57783
+ });
57784
+ const compatibility = resolveCompatibility({
57785
+ rootFrontmatter: rulesyncFrontmatter,
57786
+ section: commandcodeSection
57787
+ });
57788
+ const metadata = resolveMetadata({
57789
+ rootFrontmatter: rulesyncFrontmatter,
57790
+ section: commandcodeSection
57791
+ });
57792
+ const disableModelInvocation = resolveDisableModelInvocation({
57793
+ rootFrontmatter: rulesyncFrontmatter,
57794
+ section: commandcodeSection
57795
+ });
57796
+ const userInvocable = resolveUserInvocable({
57797
+ rootFrontmatter: rulesyncFrontmatter,
57798
+ section: commandcodeSection
57799
+ });
57461
57800
  const commandcodeFrontmatter = {
57462
57801
  ...commandcodeSection,
57463
57802
  name: rulesyncFrontmatter.name,
57464
- description: rulesyncFrontmatter.description
57803
+ description: rulesyncFrontmatter.description,
57804
+ ...license !== void 0 && { license },
57805
+ ...compatibility !== void 0 && { compatibility },
57806
+ ...metadata !== void 0 && { metadata },
57807
+ ...disableModelInvocation !== void 0 && { "disable-model-invocation": disableModelInvocation },
57808
+ ...userInvocable !== void 0 && { "user-invocable": userInvocable }
57465
57809
  };
57466
57810
  return new CommandcodeSkill({
57467
57811
  outputRoot,
@@ -64834,6 +65178,35 @@ const CommandcodeSubagentFrontmatterSchema = zod_mini.z.looseObject({
64834
65178
  showOutput: zod_mini.z.optional(zod_mini.z.boolean())
64835
65179
  });
64836
65180
  /**
65181
+ * Names Command Code's built-in agents own. The loader runs the frontmatter
65182
+ * `name` (or the file stem when it has none) through `sanitizeAgentName`,
65183
+ * which rewrites every character outside `[a-zA-Z0-9_-]` to `_`, lowercases
65184
+ * it, and skips the file outright when the result is one of these
65185
+ * (`isReservedAgentName`) — with no message. Verified in the `command-code`
65186
+ * 1.54.2 bundle.
65187
+ * @see https://commandcode.ai/docs/agents
65188
+ */
65189
+ const COMMANDCODE_RESERVED_AGENT_NAMES = /* @__PURE__ */ new Set([
65190
+ "explore",
65191
+ "plan",
65192
+ "review",
65193
+ "general"
65194
+ ]);
65195
+ /** The name Command Code compares against its reserved list. */
65196
+ function toCommandcodeAgentName(name) {
65197
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
65198
+ }
65199
+ /**
65200
+ * The subagent is still generated under the reserved name — the name is the
65201
+ * user's to change, and a rename here would make it diverge from the other
65202
+ * targets — but Command Code would drop it without a visible word, so say so
65203
+ * now (the same courtesy `VibeSkill` extends to its reserved skill names).
65204
+ */
65205
+ function warnAboutReservedAgentName({ name, relativeFilePath, logger }) {
65206
+ if (!COMMANDCODE_RESERVED_AGENT_NAMES.has(toCommandcodeAgentName(name))) return;
65207
+ 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.`);
65208
+ }
65209
+ /**
64837
65210
  * Command Code custom agent: a Markdown file with YAML frontmatter under
64838
65211
  * `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
64839
65212
  * body is the agent's system prompt.
@@ -64880,7 +65253,7 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64880
65253
  validate: true
64881
65254
  });
64882
65255
  }
64883
- static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
65256
+ static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false, logger }) {
64884
65257
  const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
64885
65258
  const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
64886
65259
  const commandcodeSubagentFrontmatter = {
@@ -64888,6 +65261,11 @@ var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
64888
65261
  description: rulesyncFrontmatter.description,
64889
65262
  ...commandcodeSection
64890
65263
  };
65264
+ warnAboutReservedAgentName({
65265
+ name: commandcodeSubagentFrontmatter.name,
65266
+ relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
65267
+ logger
65268
+ });
64891
65269
  const body = rulesyncSubagent.getBody();
64892
65270
  const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
64893
65271
  const paths = this.getSettablePaths({ global });
@@ -71260,8 +71638,12 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71260
71638
  * | `false` | none | not supported; the rule is dropped |
71261
71639
  *
71262
71640
  * The default being `true` is the opposite of Cursor, which the adapter was
71263
- * modeled on: `paths` alone scopes nothing, so a rule meant to be path
71264
- * triggered has to carry an explicit `alwaysApply: false` alongside it.
71641
+ * modeled on. The shipped loader (`@tencent-ai/codebuddy-code` 2.151.0,
71642
+ * `dist/codebuddy.js`) is looser than the table — it resolves
71643
+ * `alwaysApply = fm.alwaysApply === true || (!hasPaths && fm.alwaysApply !== false)`,
71644
+ * so `paths` without `alwaysApply` is already MANUAL — but the explicit
71645
+ * `alwaysApply: false` is written anyway: it is correct under both readings
71646
+ * and the documented one is what a reader of the file goes by.
71265
71647
  *
71266
71648
  * @see https://www.codebuddy.ai/docs/cli/memory
71267
71649
  */
@@ -71336,9 +71718,9 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
71336
71718
  });
71337
71719
  }
71338
71720
  const sourcePaths = normalizeCodebuddyPaths(this.frontmatter.paths) ?? [];
71339
- const isAlways = this.frontmatter.alwaysApply !== false;
71721
+ const isAlways = this.frontmatter.alwaysApply === true || sourcePaths.length === 0 && this.frontmatter.alwaysApply !== false;
71340
71722
  const globs = sourcePaths.length === 0 && isAlways ? ["**/*"] : sourcePaths;
71341
- const alwaysApply = this.frontmatter.alwaysApply ?? (sourcePaths.length > 0 ? true : void 0);
71723
+ const alwaysApply = this.frontmatter.alwaysApply;
71342
71724
  const rulesyncFrontmatter = {
71343
71725
  targets,
71344
71726
  root: false,
@@ -76029,6 +76411,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
76029
76411
  collisionPolicy: "fold"
76030
76412
  }
76031
76413
  }],
76414
+ ["pool", {
76415
+ class: PoolRule,
76416
+ meta: {
76417
+ extension: "md",
76418
+ supportsGlobal: true,
76419
+ ruleDiscoveryMode: "auto",
76420
+ collisionPolicy: "fold"
76421
+ }
76422
+ }],
76032
76423
  ["qwencode", {
76033
76424
  class: QwencodeRule,
76034
76425
  meta: {
@@ -76152,15 +76543,6 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
76152
76543
  ruleDiscoveryMode: "auto"
76153
76544
  }
76154
76545
  }],
76155
- ["pool", {
76156
- class: PoolRule,
76157
- meta: {
76158
- extension: "md",
76159
- supportsGlobal: true,
76160
- ruleDiscoveryMode: "auto",
76161
- collisionPolicy: "fold"
76162
- }
76163
- }],
76164
76546
  ["dsh", {
76165
76547
  class: DshRule,
76166
76548
  meta: {
@@ -79442,6 +79824,18 @@ Object.defineProperty(exports, "CODEXCLI_DIR", {
79442
79824
  return CODEXCLI_DIR;
79443
79825
  }
79444
79826
  });
79827
+ Object.defineProperty(exports, "COMMANDCODE_DIR", {
79828
+ enumerable: true,
79829
+ get: function() {
79830
+ return COMMANDCODE_DIR;
79831
+ }
79832
+ });
79833
+ Object.defineProperty(exports, "COMMANDCODE_SETTINGS_LOCAL_FILE_NAME", {
79834
+ enumerable: true,
79835
+ get: function() {
79836
+ return COMMANDCODE_SETTINGS_LOCAL_FILE_NAME;
79837
+ }
79838
+ });
79445
79839
  Object.defineProperty(exports, "CONFLICTING_TARGET_PAIRS", {
79446
79840
  enumerable: true,
79447
79841
  get: function() {