rulesync 30.0.0 → 31.0.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.
@@ -935,6 +935,7 @@ const checksProcessorToolTargetTuple = [
935
935
  "cursor",
936
936
  "factorydroid",
937
937
  "gitlabduo",
938
+ "goose",
938
939
  "hermesagent",
939
940
  "kilo",
940
941
  "qwencode",
@@ -14221,12 +14222,12 @@ const AmpCheckFrontmatterSchema = zod_mini.z.looseObject({
14221
14222
  * this format continue to apply.
14222
14223
  * @see https://ampcode.com/docs/review
14223
14224
  */
14224
- var AmpCheck = class AmpCheck extends ToolCheck {
14225
+ var AmpCheck = class extends ToolCheck {
14225
14226
  frontmatter;
14226
14227
  body;
14227
14228
  constructor({ frontmatter, body, fileContent, ...rest }) {
14228
14229
  if (rest.validate !== false) {
14229
- const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
14230
+ const result = new.target.getFrontmatterSchema().safeParse(frontmatter);
14230
14231
  if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
14231
14232
  }
14232
14233
  super({
@@ -14236,6 +14237,26 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14236
14237
  this.frontmatter = frontmatter;
14237
14238
  this.body = body;
14238
14239
  }
14240
+ /**
14241
+ * The tool target this check format belongs to, which is also the name of
14242
+ * its tool-specific frontmatter section. Goose reads the same per-file
14243
+ * format with a few extra keys, so `GooseCheck` reuses this class through
14244
+ * these hooks.
14245
+ */
14246
+ static getCheckToolTarget() {
14247
+ return "amp";
14248
+ }
14249
+ /** The frontmatter schema a check file of this tool is validated against. */
14250
+ static getFrontmatterSchema() {
14251
+ return AmpCheckFrontmatterSchema;
14252
+ }
14253
+ /**
14254
+ * Frontmatter keys only this tool reads. Import moves them into the tool's
14255
+ * own section so they are not passed on to every other checks target.
14256
+ */
14257
+ static getToolOnlyKeys() {
14258
+ return [];
14259
+ }
14239
14260
  static getSettablePaths({ global = false } = {}) {
14240
14261
  return { relativeDirPath: global ? AMP_CHECKS_GLOBAL_DIR : AMP_CHECKS_PROJECT_DIR };
14241
14262
  }
@@ -14247,6 +14268,10 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14247
14268
  }
14248
14269
  toRulesyncCheck() {
14249
14270
  const { name: _name, description, "severity-default": severityDefault, tools, ...restFields } = this.frontmatter;
14271
+ const checkClass = this.constructor;
14272
+ const toolOnlyKeys = new Set(checkClass.getToolOnlyKeys());
14273
+ const sharedFields = Object.fromEntries(Object.entries(restFields).filter(([key]) => !toolOnlyKeys.has(key)));
14274
+ const toolSection = Object.fromEntries(Object.entries(restFields).filter(([key]) => toolOnlyKeys.has(key)));
14250
14275
  return new RulesyncCheck({
14251
14276
  outputRoot: ".",
14252
14277
  frontmatter: {
@@ -14254,7 +14279,8 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14254
14279
  ...description !== void 0 && { description },
14255
14280
  ...severityDefault !== void 0 && { severity: severityDefault },
14256
14281
  ...tools !== void 0 && { tools },
14257
- ...restFields
14282
+ ...sharedFields,
14283
+ ...Object.keys(toolSection).length > 0 && { [checkClass.getCheckToolTarget()]: toolSection }
14258
14284
  },
14259
14285
  body: this.body,
14260
14286
  relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH,
@@ -14269,22 +14295,24 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14269
14295
  const { targets: _targets, description, severity, tools, ...restFields } = rulesyncFrontmatter;
14270
14296
  const toolTargetKeys = new Set(ALL_TOOL_TARGETS);
14271
14297
  const passthroughFields = Object.fromEntries(Object.entries(restFields).filter(([key]) => !toolTargetKeys.has(key) && key !== "name"));
14272
- const ampSection = this.filterToolSpecificSection(rulesyncFrontmatter.amp ?? {}, ["name"]);
14298
+ const toolTarget = this.getCheckToolTarget();
14299
+ const rawSection = rulesyncFrontmatter[toolTarget];
14300
+ const toolSection = this.filterToolSpecificSection(rawSection !== null && typeof rawSection === "object" && !Array.isArray(rawSection) ? rawSection : {}, ["name"]);
14273
14301
  const rawAmpFrontmatter = {
14274
14302
  name,
14275
14303
  ...description !== void 0 && { description },
14276
14304
  ...severity !== void 0 && { "severity-default": severity },
14277
14305
  ...tools !== void 0 && { tools },
14278
14306
  ...passthroughFields,
14279
- ...ampSection
14307
+ ...toolSection
14280
14308
  };
14281
- const result = AmpCheckFrontmatterSchema.safeParse(rawAmpFrontmatter);
14282
- if (!result.success) throw new Error(`Invalid amp check frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
14309
+ const result = this.getFrontmatterSchema().safeParse(rawAmpFrontmatter);
14310
+ if (!result.success) throw new Error(`Invalid ${toolTarget} check frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
14283
14311
  const ampFrontmatter = result.data;
14284
14312
  const body = rulesyncCheck.getBody();
14285
14313
  const fileContent = stringifyFrontmatter(body, ampFrontmatter);
14286
14314
  const paths = this.getSettablePaths({ global });
14287
- return new AmpCheck({
14315
+ return new this({
14288
14316
  outputRoot,
14289
14317
  frontmatter: ampFrontmatter,
14290
14318
  body,
@@ -14299,7 +14327,7 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14299
14327
  success: true,
14300
14328
  error: null
14301
14329
  };
14302
- const result = AmpCheckFrontmatterSchema.safeParse(this.frontmatter);
14330
+ const result = this.constructor.getFrontmatterSchema().safeParse(this.frontmatter);
14303
14331
  if (result.success) return {
14304
14332
  success: true,
14305
14333
  error: null
@@ -14312,7 +14340,7 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14312
14340
  static isTargetedByRulesyncCheck(rulesyncCheck) {
14313
14341
  return this.isTargetedByRulesyncCheckDefault({
14314
14342
  rulesyncCheck,
14315
- toolTarget: "amp"
14343
+ toolTarget: this.getCheckToolTarget()
14316
14344
  });
14317
14345
  }
14318
14346
  static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
@@ -14320,9 +14348,9 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14320
14348
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
14321
14349
  const fileContent = await readFileContent(filePath);
14322
14350
  const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
14323
- const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
14351
+ const result = this.getFrontmatterSchema().safeParse(frontmatter);
14324
14352
  if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
14325
- return new AmpCheck({
14353
+ return new this({
14326
14354
  outputRoot,
14327
14355
  relativeDirPath: paths.relativeDirPath,
14328
14356
  relativeFilePath,
@@ -14333,7 +14361,7 @@ var AmpCheck = class AmpCheck extends ToolCheck {
14333
14361
  });
14334
14362
  }
14335
14363
  static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
14336
- return new AmpCheck({
14364
+ return new this({
14337
14365
  outputRoot,
14338
14366
  relativeDirPath,
14339
14367
  relativeFilePath,
@@ -15433,6 +15461,60 @@ var GitlabduoCheck = class GitlabduoCheck extends ToolCheck {
15433
15461
  }
15434
15462
  };
15435
15463
  //#endregion
15464
+ //#region src/constants/goose-paths.ts
15465
+ const GOOSE_DIR = ".goose";
15466
+ const GOOSE_GLOBAL_DIR = (0, node_path.join)(".config", "goose");
15467
+ const GOOSE_RULE_FILE_NAME = ".goosehints";
15468
+ const GOOSE_MCP_FILE_NAME = "config.yaml";
15469
+ const GOOSE_PERMISSIONS_FILE_NAME = "permission.yaml";
15470
+ const GOOSE_HOOKS_DIR_PATH = (0, node_path.join)(".agents", "plugins", "rulesync", "hooks");
15471
+ const GOOSE_HOOKS_FILE_NAME = "hooks.json";
15472
+ const GOOSE_PLUGIN_MCP_DIR_PATH = (0, node_path.join)(".agents", "plugins", "rulesync");
15473
+ const GOOSE_PLUGIN_MCP_FILE_NAME = ".mcp.json";
15474
+ const GOOSE_SKILLS_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "skills");
15475
+ const GOOSE_GLOBAL_SKILLS_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "skills");
15476
+ const GOOSE_RECIPES_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "recipes");
15477
+ const GOOSE_GLOBAL_RECIPES_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "recipes");
15478
+ const GOOSE_AGENTS_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "agents");
15479
+ const GOOSE_GLOBAL_AGENTS_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "agents");
15480
+ const GOOSE_CHECKS_PROJECT_DIR = (0, node_path.join)(".agents", "checks");
15481
+ const GOOSE_CHECKS_GLOBAL_DIR = (0, node_path.join)(GOOSE_GLOBAL_DIR, "checks");
15482
+ //#endregion
15483
+ //#region src/features/checks/goose-check.ts
15484
+ const GooseCheckFrontmatterSchema = zod_mini.z.extend(AmpCheckFrontmatterSchema, {
15485
+ model: zod_mini.z.optional(zod_mini.z.string()),
15486
+ "turn-limit": zod_mini.z.optional(zod_mini.z.int().check(zod_mini.z.nonnegative()))
15487
+ });
15488
+ /**
15489
+ * Represents a Goose review check (`goose review`), written to the user-wide
15490
+ * `~/.config/goose/checks/` (global scope only).
15491
+ *
15492
+ * Goose discovers repository checks in `.agents/checks/*.md`, the same
15493
+ * cross-tool directory the `amp` checks target already writes (Goose's
15494
+ * `tools` and `severity-default` fields exist for parity with Amp's), so the
15495
+ * project scope is left to `amp` rather than having two targets own the same
15496
+ * files. Its global discovery reads `~/.config/goose/checks/` first, then
15497
+ * `~/.config/agents/checks/`. The file format is Amp's, so this class reuses
15498
+ * `AmpCheck` and adds Goose's `model` and `turn-limit`, which live in the
15499
+ * `goose:` section of a rulesync check; `name` is derived from the file name.
15500
+ *
15501
+ * @see https://github.com/aaif-goose/goose/blob/main/crates/goose/src/checks/mod.rs
15502
+ */
15503
+ var GooseCheck = class extends AmpCheck {
15504
+ static getCheckToolTarget() {
15505
+ return "goose";
15506
+ }
15507
+ static getFrontmatterSchema() {
15508
+ return GooseCheckFrontmatterSchema;
15509
+ }
15510
+ static getToolOnlyKeys() {
15511
+ return ["model", "turn-limit"];
15512
+ }
15513
+ static getSettablePaths({ global = false } = {}) {
15514
+ return { relativeDirPath: global ? GOOSE_CHECKS_GLOBAL_DIR : GOOSE_CHECKS_PROJECT_DIR };
15515
+ }
15516
+ };
15517
+ //#endregion
15436
15518
  //#region src/constants/hermesagent-paths.ts
15437
15519
  /**
15438
15520
  * Hermes Agent configuration-layout conventions.
@@ -16161,6 +16243,14 @@ const toolCheckFactories = /* @__PURE__ */ new Map([
16161
16243
  committedOutput: true
16162
16244
  }
16163
16245
  }],
16246
+ ["goose", {
16247
+ class: GooseCheck,
16248
+ meta: {
16249
+ supportsGlobal: true,
16250
+ supportsProject: false,
16251
+ filePattern: "*.md"
16252
+ }
16253
+ }],
16164
16254
  ["hermesagent", {
16165
16255
  class: HermesagentCheck,
16166
16256
  meta: {
@@ -16206,7 +16296,9 @@ const defaultGetFactory$6 = (target) => {
16206
16296
  return factory;
16207
16297
  };
16208
16298
  const allToolTargetKeys$5 = [...toolCheckFactories.keys()];
16209
- const checksProcessorToolTargets = allToolTargetKeys$5;
16299
+ const checksProcessorToolTargets = allToolTargetKeys$5.filter((target) => {
16300
+ return toolCheckFactories.get(target)?.meta.supportsProject !== false;
16301
+ });
16210
16302
  const checksProcessorToolTargetsGlobal = allToolTargetKeys$5.filter((target) => {
16211
16303
  return toolCheckFactories.get(target)?.meta.supportsGlobal ?? false;
16212
16304
  });
@@ -16377,23 +16469,6 @@ var ChecksProcessor = class extends FeatureProcessor {
16377
16469
  }
16378
16470
  };
16379
16471
  //#endregion
16380
- //#region src/constants/goose-paths.ts
16381
- const GOOSE_DIR = ".goose";
16382
- const GOOSE_GLOBAL_DIR = (0, node_path.join)(".config", "goose");
16383
- const GOOSE_RULE_FILE_NAME = ".goosehints";
16384
- const GOOSE_MCP_FILE_NAME = "config.yaml";
16385
- const GOOSE_PERMISSIONS_FILE_NAME = "permission.yaml";
16386
- const GOOSE_HOOKS_DIR_PATH = (0, node_path.join)(".agents", "plugins", "rulesync", "hooks");
16387
- const GOOSE_HOOKS_FILE_NAME = "hooks.json";
16388
- const GOOSE_PLUGIN_MCP_DIR_PATH = (0, node_path.join)(".agents", "plugins", "rulesync");
16389
- const GOOSE_PLUGIN_MCP_FILE_NAME = ".mcp.json";
16390
- const GOOSE_SKILLS_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "skills");
16391
- const GOOSE_GLOBAL_SKILLS_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "skills");
16392
- const GOOSE_RECIPES_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "recipes");
16393
- const GOOSE_GLOBAL_RECIPES_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "recipes");
16394
- const GOOSE_AGENTS_DIR_PATH = (0, node_path.join)(GOOSE_DIR, "agents");
16395
- const GOOSE_GLOBAL_AGENTS_DIR_PATH = (0, node_path.join)(GOOSE_GLOBAL_DIR, "agents");
16396
- //#endregion
16397
16472
  //#region src/utils/tool-home.ts
16398
16473
  /**
16399
16474
  * Where the rulesync-side source files of a tool with a home override belong.
@@ -18115,27 +18190,71 @@ const CLINE_HOOKS_GLOBAL_DIR_PATH = (0, node_path.join)("Documents", "Cline", "H
18115
18190
  const CLINE_HOOKS_MANIFEST_FILE_NAME = "rulesync-hooks.json";
18116
18191
  //#endregion
18117
18192
  //#region src/features/commands/cline-command.ts
18193
+ const CLINE_WORKFLOW_FIELD_TYPES = {
18194
+ name: "string",
18195
+ disabled: "boolean",
18196
+ enabled: "boolean"
18197
+ };
18198
+ function sanitizeClineWorkflowFrontmatter({ cline, relativeFilePath, logger }) {
18199
+ if (!isRecord$1(cline)) return {};
18200
+ return Object.fromEntries(Object.entries(cline).filter(([key, value]) => {
18201
+ const expected = Object.hasOwn(CLINE_WORKFLOW_FIELD_TYPES, key) ? CLINE_WORKFLOW_FIELD_TYPES[key] : void 0;
18202
+ if (expected === void 0 || typeof value === expected) return true;
18203
+ warnWithFallback(logger, `Cline commands: dropping cline.${key} from ${relativeFilePath}: Cline rejects the whole workflow unless it is a ${expected}.`);
18204
+ return false;
18205
+ }));
18206
+ }
18207
+ /**
18208
+ * Cline workflows (commands). A workflow may carry YAML frontmatter that Cline
18209
+ * reads: `name` (the slash-command name) and a `disabled` / `enabled` switch
18210
+ * (`parseWorkflowConfigFromMarkdown` in Cline's
18211
+ * `user-instruction-config-loader.ts`). The `cline:` section of a
18212
+ * `.rulesync/commands/*.md` file is written as that frontmatter, and every key
18213
+ * of an imported workflow's frontmatter is lifted back into `cline:`, so
18214
+ * hand-written workflow frontmatter survives an import → generate round trip.
18215
+ * Cline's own UI toggle is kept in Cline's state, not in the file. A workflow
18216
+ * without a `cline:` section is written as a bare body, as before.
18217
+ *
18218
+ * @see https://github.com/cline/cline/blob/main/sdk/packages/core/src/extensions/config/user-instruction-config-loader.ts
18219
+ */
18118
18220
  var ClineCommand = class ClineCommand extends ToolCommand {
18119
18221
  static getSettablePaths({ global } = {}) {
18120
18222
  if (global) return { relativeDirPath: CLINE_COMMANDS_GLOBAL_DIR_PATH };
18121
18223
  return { relativeDirPath: CLINE_COMMANDS_DIR_PATH };
18122
18224
  }
18225
+ /** The workflow's own frontmatter, or an empty object for a bare body. */
18226
+ getClineFrontmatter() {
18227
+ const { frontmatter } = parseFrontmatter(this.getFileContent(), this.getFilePath());
18228
+ return isRecord$1(frontmatter) ? frontmatter : {};
18229
+ }
18123
18230
  toRulesyncCommand() {
18231
+ const cline = this.getClineFrontmatter();
18232
+ const rulesyncFrontmatter = {
18233
+ targets: ["*"],
18234
+ ...Object.keys(cline).length > 0 && { cline }
18235
+ };
18236
+ const body = this.getBody();
18124
18237
  return new RulesyncCommand({
18125
18238
  outputRoot: process.cwd(),
18126
- frontmatter: { targets: ["*"] },
18127
- body: this.getFileContent(),
18239
+ frontmatter: rulesyncFrontmatter,
18240
+ body,
18128
18241
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
18129
18242
  relativeFilePath: this.relativeFilePath,
18130
- fileContent: this.getFileContent(),
18243
+ fileContent: stringifyFrontmatter(body, rulesyncFrontmatter),
18131
18244
  validate: true
18132
18245
  });
18133
18246
  }
18134
- static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
18247
+ static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false, logger }) {
18135
18248
  const paths = this.getSettablePaths({ global });
18249
+ const cline = sanitizeClineWorkflowFrontmatter({
18250
+ cline: rulesyncCommand.getFrontmatter().cline,
18251
+ relativeFilePath: rulesyncCommand.getRelativeFilePath(),
18252
+ logger
18253
+ });
18254
+ const body = rulesyncCommand.getBody();
18136
18255
  return new ClineCommand({
18137
18256
  outputRoot,
18138
- fileContent: rulesyncCommand.getBody(),
18257
+ fileContent: Object.keys(cline).length > 0 ? stringifyFrontmatter(body, cline) : body,
18139
18258
  relativeDirPath: paths.relativeDirPath,
18140
18259
  relativeFilePath: rulesyncCommand.getRelativeFilePath(),
18141
18260
  validate
@@ -18148,7 +18267,7 @@ var ClineCommand = class ClineCommand extends ToolCommand {
18148
18267
  };
18149
18268
  }
18150
18269
  getBody() {
18151
- return this.getFileContent();
18270
+ return parseFrontmatter(this.getFileContent(), this.getFilePath()).body.trim();
18152
18271
  }
18153
18272
  static isTargetedByRulesyncCommand(rulesyncCommand) {
18154
18273
  return this.isTargetedByRulesyncCommandDefault({
@@ -18159,12 +18278,13 @@ var ClineCommand = class ClineCommand extends ToolCommand {
18159
18278
  static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
18160
18279
  const paths = this.getSettablePaths({ global });
18161
18280
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
18162
- const { body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
18281
+ const { frontmatter, body } = parseFrontmatter(await readFileContent(filePath), filePath);
18282
+ const cline = isRecord$1(frontmatter) ? frontmatter : {};
18163
18283
  return new ClineCommand({
18164
18284
  outputRoot,
18165
18285
  relativeDirPath: paths.relativeDirPath,
18166
18286
  relativeFilePath,
18167
- fileContent: content.trim(),
18287
+ fileContent: Object.keys(cline).length > 0 ? stringifyFrontmatter(body.trim(), cline) : body.trim(),
18168
18288
  validate
18169
18289
  });
18170
18290
  }
@@ -40544,6 +40664,35 @@ function convertFromCodexFormat(codexMcp) {
40544
40664
  }
40545
40665
  return result;
40546
40666
  }
40667
+ /**
40668
+ * Keys Codex refuses on each transport. Its `McpServerConfig` deserializer
40669
+ * calls `throw_if_set` for these, and the error fails the whole `config.toml`,
40670
+ * not just the one server, so they are dropped with a warning instead of
40671
+ * written. Checked against `codex-rs/config/src/mcp_types.rs` at rust-v0.162.1.
40672
+ */
40673
+ const CODEX_STDIO_REJECTED_KEYS = [
40674
+ "url",
40675
+ "bearer_token_env_var",
40676
+ "bearer_token",
40677
+ "http_headers_helper",
40678
+ "http_headers",
40679
+ "env_http_headers",
40680
+ "oauth",
40681
+ "oauth_resource",
40682
+ "auth"
40683
+ ];
40684
+ const CODEX_HTTP_REJECTED_KEYS = [
40685
+ "args",
40686
+ "env",
40687
+ "env_vars",
40688
+ "cwd",
40689
+ "bearer_token"
40690
+ ];
40691
+ function dropTransportRejectedKeys({ converted, isStdio, serverName }) {
40692
+ const dropped = (isStdio ? CODEX_STDIO_REJECTED_KEYS : CODEX_HTTP_REJECTED_KEYS).filter((key) => converted[key] !== void 0);
40693
+ for (const key of dropped) delete converted[key];
40694
+ if (dropped.length > 0) warnWithFallback(void 0, `[CodexCliMcp] Dropping ${dropped.map((key) => `'${key}'`).join(", ")} from ${isStdio ? "stdio" : "url-based"} MCP server "${serverName}": Codex rejects ${dropped.length === 1 ? "this key" : "these keys"} on a ${isStdio ? "stdio" : "url-based"} server and would fail to load the whole config.toml.`);
40695
+ }
40547
40696
  function convertToCodexFormat(mcpServers) {
40548
40697
  const result = {};
40549
40698
  const originalNames = /* @__PURE__ */ new Map();
@@ -40586,6 +40735,11 @@ function convertToCodexFormat(mcpServers) {
40586
40735
  converted,
40587
40736
  serverName: name
40588
40737
  });
40738
+ dropTransportRejectedKeys({
40739
+ converted,
40740
+ isStdio,
40741
+ serverName: name
40742
+ });
40589
40743
  const previousName = originalNames.get(codexName);
40590
40744
  if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}"; "${name}" (processed last) overwrites "${previousName}".`);
40591
40745
  originalNames.set(codexName, name);
@@ -67806,10 +67960,15 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
67806
67960
  outputRoot,
67807
67961
  logger
67808
67962
  });
67963
+ const inheritedProvider = global ? void 0 : await readGlobalTaktProvider({
67964
+ projectFilePath: filePath,
67965
+ logger
67966
+ });
67809
67967
  const rulesyncJson = rulesyncPermissions.getJson();
67810
67968
  const provider = resolveActiveProvider({
67811
67969
  config,
67812
- runtime: runtime.active ? runtime.config : void 0
67970
+ runtime: runtime.active ? runtime.config : void 0,
67971
+ inheritedProvider
67813
67972
  });
67814
67973
  const mode = deriveTaktPermissionMode(rulesyncJson);
67815
67974
  const override = isPlainObject$1(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
@@ -67919,10 +68078,12 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
67919
68078
  * `provider_profiles` (unless that key is the pre-0.68.0 `claude` fallback),
67920
68079
  * else the `claude-sdk` default (Takt 0.68.0+).
67921
68080
  */
67922
- function resolveActiveProvider({ config, runtime }) {
68081
+ function resolveActiveProvider({ config, runtime, inheritedProvider }) {
67923
68082
  const fromRuntime = runtime === void 0 ? void 0 : resolveRuntimeProvider(runtime);
67924
68083
  if (fromRuntime !== void 0) return fromRuntime;
67925
- if (typeof config[TAKT_PROVIDER_KEY] === "string" && config[TAKT_PROVIDER_KEY].trim() !== "") return config[TAKT_PROVIDER_KEY];
68084
+ const local = readProviderName(config);
68085
+ if (local !== void 0) return local;
68086
+ if (inheritedProvider !== void 0) return inheritedProvider;
67926
68087
  const profiles = config[TAKT_PROVIDER_PROFILES_KEY];
67927
68088
  if (isPlainObject$1(profiles)) {
67928
68089
  const keys = Object.keys(profiles);
@@ -67930,6 +68091,43 @@ function resolveActiveProvider({ config, runtime }) {
67930
68091
  }
67931
68092
  return TAKT_DEFAULT_PROVIDER;
67932
68093
  }
68094
+ /**
68095
+ * The top-level `provider:` of a Takt `config.yaml`, when it names one: either
68096
+ * the plain name or the block form `provider: { type: <name>, ... }` Takt also
68097
+ * accepts (`ConfigProviderReference`).
68098
+ */
68099
+ function readProviderName(config) {
68100
+ const raw = config[TAKT_PROVIDER_KEY];
68101
+ const provider = isPlainObject$1(raw) ? raw.type : raw;
68102
+ return typeof provider === "string" && provider.trim() !== "" ? provider : void 0;
68103
+ }
68104
+ /**
68105
+ * The top-level `provider:` of the global `~/.takt/config.yaml`, read for a
68106
+ * project-scope generate. An unresolvable home directory, a missing or
68107
+ * unparsable file, or one that names no provider all mean "nothing inherited".
68108
+ */
68109
+ async function readGlobalTaktProvider({ projectFilePath, logger }) {
68110
+ let globalPath;
68111
+ try {
68112
+ globalPath = (0, node_path.join)(getHomeDirectory(), TAKT_DIR, TAKT_CONFIG_FILE_NAME);
68113
+ } catch {
68114
+ return;
68115
+ }
68116
+ if (globalPath === projectFilePath) return;
68117
+ try {
68118
+ const content = await readFileContentOrNull(globalPath);
68119
+ if (content === null) return;
68120
+ return readProviderName(parseSharedConfig({
68121
+ format: "yaml",
68122
+ fileContent: content,
68123
+ filePath: globalPath,
68124
+ invalidRootPolicy: "error"
68125
+ }));
68126
+ } catch (error) {
68127
+ logger?.warn(`Takt permissions: could not read the provider from ${globalPath} (${formatError(error)}); falling back to the project config alone.`);
68128
+ return;
68129
+ }
68130
+ }
67933
68131
  /** A plain object carrying at least one entry (Takt's "assignment" test). */
67934
68132
  function hasEntries(value) {
67935
68133
  return isPlainObject$1(value) && Object.keys(value).length > 0;