rulesync 21.0.0 → 22.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.
@@ -12270,7 +12270,7 @@ const CODEXCLI_AGENTS_DIR_PATH = join(CODEXCLI_DIR, "agents");
12270
12270
  const CODEXCLI_SKILLS_DIR_PATH = join(".agents", "skills");
12271
12271
  const CODEXCLI_HOOKS_FILE_NAME = "hooks.json";
12272
12272
  const CODEXCLI_MCP_FILE_NAME = "config.toml";
12273
- const CODEXCLI_RULE_FILE_NAME = "AGENTS.md";
12273
+ const CODEXCLI_PROJECT_DOC_MAX_BYTES = 32768;
12274
12274
  const CODEXCLI_BASH_RULES_FILE_NAME = "rulesync.rules";
12275
12275
  const CODEXCLI_OPENAI_YAML_RELATIVE_PATH = join("agents", "openai.yaml");
12276
12276
  const CODEXCLI_OVERRIDE_KEYS = [
@@ -15455,12 +15455,50 @@ var BobCommand = class BobCommand extends ToolCommand {
15455
15455
  }
15456
15456
  };
15457
15457
  //#endregion
15458
+ //#region src/features/shared/claudecode-argument-hint.ts
15459
+ /**
15460
+ * Turns a YAML-list `argument-hint` back into the bracketed string form Claude
15461
+ * Code documents (`[issue-number]`, `[filename] [format]`), one bracketed
15462
+ * placeholder per list entry. The per-entry form is chosen over re-joining with
15463
+ * commas because it is the shape every Claude Code example uses. An empty list
15464
+ * carries no hint, so it yields `undefined` and the key is dropped.
15465
+ */
15466
+ function joinArgumentHint(value) {
15467
+ if (typeof value === "string") return value;
15468
+ if (value.length === 0) return;
15469
+ return value.map((entry) => `[${String(entry)}]`).join(" ");
15470
+ }
15471
+ /**
15472
+ * `argument-hint` as Claude Code reads it from skill and command frontmatter.
15473
+ *
15474
+ * The documented examples (`[issue-number]`) are written unquoted, and YAML
15475
+ * parses an unquoted `[issue-number]` as a one-element list. Claude Code
15476
+ * accepts that form, so it is accepted here too and normalized to the
15477
+ * bracketed string, keeping the parsed value (and everything downstream of it)
15478
+ * a plain string. A scalar entry YAML did not read as a string (`[1]`,
15479
+ * `[true]`, `[~]`) is stringified rather than rejected.
15480
+ * https://code.claude.com/docs/en/skills
15481
+ *
15482
+ * A list entry is stringified from the value YAML produced, so its original
15483
+ * spelling is not kept (`[1.0]` becomes `[1]`), and an entry YAML reads as a
15484
+ * date (`[2026-01-01]`) is rejected.
15485
+ *
15486
+ * The unquoted multi-placeholder form (`argument-hint: [filename] [format]`)
15487
+ * is not valid YAML at all, so it fails before this schema is reached.
15488
+ */
15489
+ const ClaudecodeArgumentHintSchema = z.pipe(z.union([z.string(), z.array(z.union([
15490
+ z.string(),
15491
+ z.number(),
15492
+ z.boolean(),
15493
+ z.null()
15494
+ ]))]), z.transform(joinArgumentHint));
15495
+ //#endregion
15458
15496
  //#region src/features/commands/claudecode-command.ts
15459
15497
  const ClaudecodeCommandFrontmatterSchema = z.looseObject({
15460
15498
  description: z.optional(z.string()),
15461
15499
  "allowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
15462
15500
  "disallowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
15463
- "argument-hint": z.optional(z.string()),
15501
+ "argument-hint": z.optional(ClaudecodeArgumentHintSchema),
15464
15502
  model: z.optional(z.string()),
15465
15503
  "disable-model-invocation": z.optional(z.boolean())
15466
15504
  });
@@ -15489,7 +15527,8 @@ var ClaudecodeCommand = class ClaudecodeCommand extends ToolCommand {
15489
15527
  return this.frontmatter;
15490
15528
  }
15491
15529
  toRulesyncCommand() {
15492
- const { description, ...restFields } = this.frontmatter;
15530
+ const { description, ...extraFields } = this.frontmatter;
15531
+ const restFields = Object.fromEntries(Object.entries(extraFields).filter(([, value]) => value !== void 0));
15493
15532
  const rulesyncFrontmatter = {
15494
15533
  targets: ["*"],
15495
15534
  description,
@@ -33122,6 +33161,74 @@ function translateCanonicalKeyToCodex({ key, value, isStdio, serverName }) {
33122
33161
  }
33123
33162
  }
33124
33163
  /**
33164
+ * A header value that is exactly one canonical `${VAR}` reference. Anchored and
33165
+ * without the `g` flag, so `.exec()` carries no `lastIndex` state between
33166
+ * calls. `${VAR:-default}` does not match (the name excludes `:`), because
33167
+ * Codex has no way to express the fallback.
33168
+ */
33169
+ const WHOLE_ENV_VAR_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
33170
+ /**
33171
+ * An `Authorization` value that is exactly `Bearer ${VAR}`. The scheme is
33172
+ * matched case-insensitively, as HTTP authentication schemes are.
33173
+ */
33174
+ const BEARER_ENV_VAR_REF_PATTERN = /^Bearer \$\{([A-Za-z_][A-Za-z0-9_]*)\}$/i;
33175
+ const AUTHORIZATION_HEADER_NAME = "authorization";
33176
+ /**
33177
+ * Whether a string still contains an unexpanded `${…}` reference. Codex writes
33178
+ * `url` and `http_headers` values as-is and never expands them, so such a value
33179
+ * reaches the server literally.
33180
+ */
33181
+ function hasUnexpandedEnvVarRef(value) {
33182
+ return value.includes("${");
33183
+ }
33184
+ /**
33185
+ * Rewrite canonical `${VAR}` header references into the keys Codex reads them
33186
+ * from, since Codex sends `http_headers` values verbatim:
33187
+ * - `Authorization: Bearer ${VAR}` → `bearer_token_env_var = "VAR"`
33188
+ * - a header whose whole value is `${VAR}` → `env_http_headers.<name> = "VAR"`
33189
+ *
33190
+ * Runs after every key is translated, so a `bearer_token_env_var` or
33191
+ * `env_http_headers` the source already carries (they pass through under their
33192
+ * own names) is seen regardless of key order. Those explicit values win: a
33193
+ * derived bearer is not written over an existing one, and an existing
33194
+ * `env_http_headers` entry for the same header is kept. Any value the rewrite
33195
+ * cannot express — a partial reference, a `${VAR:-default}` fallback — stays in
33196
+ * `http_headers` verbatim with a warning, as does a `${…}` in `url`, for which
33197
+ * Codex has no environment-sourced counterpart at all.
33198
+ * @see https://learn.chatgpt.com/docs/config-file/config-reference
33199
+ */
33200
+ function convertEnvVarRefsForCodex({ converted, serverName }) {
33201
+ const url = converted["url"];
33202
+ if (typeof url === "string" && hasUnexpandedEnvVarRef(url)) warnWithFallback(void 0, `[CodexCliMcp] MCP server "${serverName}": 'url' contains an environment variable reference that Codex does not expand, so it is written verbatim and the server will likely fail to start.`);
33203
+ const httpHeaders = converted["http_headers"];
33204
+ if (!isHeadersRecord(httpHeaders)) return;
33205
+ const existingEnvHeaders = converted["env_http_headers"];
33206
+ const canWriteEnvHeaders = existingEnvHeaders === void 0 || isHeadersRecord(existingEnvHeaders);
33207
+ const envHeaders = {};
33208
+ const staticHeaders = {};
33209
+ let bearerTokenEnvVar;
33210
+ for (const [name, value] of Object.entries(httpHeaders)) {
33211
+ const bearerMatch = BEARER_ENV_VAR_REF_PATTERN.exec(value);
33212
+ if (bearerMatch?.[1] && name.toLowerCase() === AUTHORIZATION_HEADER_NAME) {
33213
+ if (converted["bearer_token_env_var"] === void 0 && bearerTokenEnvVar === void 0) bearerTokenEnvVar = bearerMatch[1];
33214
+ continue;
33215
+ }
33216
+ const wholeMatch = WHOLE_ENV_VAR_REF_PATTERN.exec(value);
33217
+ if (wholeMatch?.[1] && canWriteEnvHeaders) {
33218
+ envHeaders[name] = wholeMatch[1];
33219
+ continue;
33220
+ }
33221
+ if (hasUnexpandedEnvVarRef(value)) warnWithFallback(void 0, `[CodexCliMcp] MCP server "${serverName}": header '${name}' contains an environment variable reference that Codex cannot source from the environment, so it is written verbatim to 'http_headers' and sent literally. Only 'Authorization: Bearer \${VAR}' and a whole-value '\${VAR}' are converted.`);
33222
+ staticHeaders[name] = value;
33223
+ }
33224
+ converted["http_headers"] = staticHeaders;
33225
+ if (bearerTokenEnvVar !== void 0) converted["bearer_token_env_var"] = bearerTokenEnvVar;
33226
+ if (Object.keys(envHeaders).length > 0) converted["env_http_headers"] = {
33227
+ ...envHeaders,
33228
+ ...isHeadersRecord(existingEnvHeaders) ? existingEnvHeaders : {}
33229
+ };
33230
+ }
33231
+ /**
33125
33232
  * Codex states no transport of its own — it infers one from `command` versus
33126
33233
  * `url`, which is why generate drops the canonical `type`. Restate it for a url
33127
33234
  * server on the way back, so a config imported from Codex reaches the adapters
@@ -33208,6 +33315,10 @@ function convertToCodexFormat(mcpServers) {
33208
33315
  }
33209
33316
  } else converted[key] = value;
33210
33317
  }
33318
+ convertEnvVarRefsForCodex({
33319
+ converted,
33320
+ serverName: name
33321
+ });
33211
33322
  const previousName = originalNames.get(codexName);
33212
33323
  if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}"; "${name}" (processed last) overwrites "${previousName}".`);
33213
33324
  originalNames.set(codexName, name);
@@ -59207,7 +59318,7 @@ const ClaudecodeSkillFrontmatterSchema = z.looseObject({
59207
59318
  "disallowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
59208
59319
  model: z.optional(z.string()),
59209
59320
  effort: z.optional(z.string()),
59210
- "argument-hint": z.optional(z.string()),
59321
+ "argument-hint": z.optional(ClaudecodeArgumentHintSchema),
59211
59322
  arguments: z.optional(z.union([z.string(), z.array(z.string())])),
59212
59323
  context: z.optional(z.string()),
59213
59324
  agent: z.optional(z.string()),
@@ -65363,7 +65474,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
65363
65474
  meta: {
65364
65475
  supportsProject: true,
65365
65476
  supportsSimulated: false,
65366
- supportsGlobal: true
65477
+ supportsGlobal: true,
65478
+ lenientImport: true
65367
65479
  }
65368
65480
  }],
65369
65481
  ["claudecode-plugin", {
@@ -72312,7 +72424,7 @@ var ToolRule = class extends ToolFile {
72312
72424
  static buildToolRuleParamsAgentsmd({ outputRoot = process.cwd(), rulesyncRule, validate = true, rootPath = {
72313
72425
  relativeDirPath: ".",
72314
72426
  relativeFilePath: "AGENTS.md"
72315
- }, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH } }) {
72427
+ }, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH }, global = false }) {
72316
72428
  const params = this.buildToolRuleParamsDefault({
72317
72429
  outputRoot,
72318
72430
  rulesyncRule,
@@ -72321,7 +72433,7 @@ var ToolRule = class extends ToolFile {
72321
72433
  nonRootPath
72322
72434
  });
72323
72435
  const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
72324
- if (!rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
72436
+ if (!global && !rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
72325
72437
  params.relativeDirPath = join(rulesyncFrontmatter.agentsmd.subprojectPath);
72326
72438
  params.relativeFilePath = "AGENTS.md";
72327
72439
  }
@@ -72978,7 +73090,8 @@ var AmpRule = class AmpRule extends ToolRule {
72978
73090
  rulesyncRule,
72979
73091
  validate,
72980
73092
  rootPath: paths.root,
72981
- nonRootPath: paths.nonRoot
73093
+ nonRootPath: paths.nonRoot,
73094
+ global
72982
73095
  });
72983
73096
  if (!params.root && params.globs !== void 0 && params.globs.length > 0) params.fileContent = stringifyFrontmatter(params.fileContent, { globs: params.globs });
72984
73097
  return new AmpRule(params);
@@ -74273,7 +74386,8 @@ var ClineRule = class ClineRule extends ToolRule {
74273
74386
  outputRoot,
74274
74387
  rulesyncRule,
74275
74388
  validate,
74276
- rootPath: paths.root
74389
+ rootPath: paths.root,
74390
+ global
74277
74391
  }));
74278
74392
  }
74279
74393
  if (isRoot) return new ClineRule({
@@ -74693,26 +74807,62 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
74693
74807
  }
74694
74808
  };
74695
74809
  //#endregion
74696
- //#region src/features/rules/codexcli-rule.ts
74697
- var CodexcliRule = class CodexcliRule extends ToolRule {
74698
- constructor({ fileContent, root, ...rest }) {
74699
- super({
74700
- ...rest,
74701
- fileContent,
74702
- root: root ?? false
74703
- });
74810
+ //#region src/features/rules/nested-agentsmd-rule.ts
74811
+ /**
74812
+ * Shared adapter for the targets that read instructions exactly the way the
74813
+ * AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
74814
+ * project root, nested per-directory `AGENTS.md` files loaded only while working
74815
+ * under that directory, and a single personal root file under a tool-specific
74816
+ * home directory. Such a target has no modular non-root instruction directory,
74817
+ * so every other non-root rule folds into the root file.
74818
+ *
74819
+ * Concrete targets (Codex CLI, Pool, Vibe, DeepSeek Harness) only supply the global
74820
+ * directory and their tool target through {@link getFamily}; the vendor docs
74821
+ * describing each one's discovery walk live on the subclass.
74822
+ *
74823
+ * The nested-file scan mirrors the AGENTS.md standard's nested discovery —
74824
+ * same file name, same exclusions, import-only, project scope — because it
74825
+ * discovers literally the same files.
74826
+ */
74827
+ var NestedAgentsmdRule = class extends ToolRule {
74828
+ static getFamily() {
74829
+ throw new Error("Please implement this method in the subclass.");
74704
74830
  }
74705
74831
  static getSettablePaths({ global = false } = {}) {
74832
+ if (global) return { root: {
74833
+ relativeDirPath: this.getFamily().globalDir,
74834
+ relativeFilePath: AGENTSMD_RULE_FILE_NAME
74835
+ } };
74706
74836
  return { root: {
74707
- relativeDirPath: global ? CODEXCLI_DIR : ".",
74708
- relativeFilePath: CODEXCLI_RULE_FILE_NAME
74837
+ relativeDirPath: ".",
74838
+ relativeFilePath: AGENTSMD_RULE_FILE_NAME
74709
74839
  } };
74710
74840
  }
74711
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
74841
+ static getNestedFilePatterns() {
74842
+ return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
74843
+ }
74844
+ /**
74845
+ * The subproject directory this rule scopes, or `undefined` for the root file
74846
+ * (project or global).
74847
+ */
74848
+ getSubprojectPath() {
74849
+ return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
74850
+ }
74851
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
74712
74852
  const { root } = this.getSettablePaths({ global });
74713
- const relativePath = join(root.relativeDirPath, root.relativeFilePath);
74714
- const fileContent = await readFileContent(join(outputRoot, relativePath));
74715
- return new CodexcliRule({
74853
+ if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
74854
+ const fileContent = await readFileContent(join(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
74855
+ return new this({
74856
+ outputRoot,
74857
+ relativeDirPath: overrideDirPath,
74858
+ relativeFilePath: AGENTSMD_RULE_FILE_NAME,
74859
+ fileContent,
74860
+ validate,
74861
+ root: false
74862
+ });
74863
+ }
74864
+ const fileContent = await readFileContent(join(outputRoot, root.relativeDirPath, root.relativeFilePath));
74865
+ return new this({
74716
74866
  outputRoot,
74717
74867
  relativeDirPath: root.relativeDirPath,
74718
74868
  relativeFilePath: root.relativeFilePath,
@@ -74723,8 +74873,18 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
74723
74873
  }
74724
74874
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
74725
74875
  const { root } = this.getSettablePaths({ global });
74726
- const isRoot = rulesyncRule.getFrontmatter().root ?? false;
74727
- return new CodexcliRule({
74876
+ const frontmatter = rulesyncRule.getFrontmatter();
74877
+ const isRoot = frontmatter.root ?? false;
74878
+ const subprojectPath = frontmatter.agentsmd?.subprojectPath;
74879
+ if (!global && !isRoot && subprojectPath) return new this({
74880
+ outputRoot,
74881
+ relativeDirPath: join(subprojectPath),
74882
+ relativeFilePath: AGENTSMD_RULE_FILE_NAME,
74883
+ fileContent: rulesyncRule.getBody(),
74884
+ validate,
74885
+ root: false
74886
+ });
74887
+ return new this({
74728
74888
  outputRoot,
74729
74889
  relativeDirPath: root.relativeDirPath,
74730
74890
  relativeFilePath: root.relativeFilePath,
@@ -74734,7 +74894,9 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
74734
74894
  });
74735
74895
  }
74736
74896
  toRulesyncRule() {
74737
- return this.toRulesyncRuleDefault();
74897
+ const subprojectPath = this.getSubprojectPath();
74898
+ if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
74899
+ return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
74738
74900
  }
74739
74901
  validate() {
74740
74902
  return {
@@ -74743,23 +74905,54 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
74743
74905
  };
74744
74906
  }
74745
74907
  static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
74746
- return new CodexcliRule({
74908
+ const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
74909
+ return new this({
74747
74910
  outputRoot,
74748
74911
  relativeDirPath,
74749
74912
  relativeFilePath,
74750
74913
  fileContent: "",
74751
74914
  validate: false,
74752
- root: relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === ".codex")
74915
+ root: isRoot
74753
74916
  });
74754
74917
  }
74755
74918
  static isTargetedByRulesyncRule(rulesyncRule) {
74756
74919
  return this.isTargetedByRulesyncRuleDefault({
74757
74920
  rulesyncRule,
74758
- toolTarget: "codexcli"
74921
+ toolTarget: this.getFamily().toolTarget
74759
74922
  });
74760
74923
  }
74761
74924
  };
74762
74925
  //#endregion
74926
+ //#region src/features/rules/codexcli-rule.ts
74927
+ /**
74928
+ * Rule generator for OpenAI Codex CLI.
74929
+ *
74930
+ * Codex CLI loads instructions only from the `AGENTS.md` family: the global
74931
+ * `~/.codex/AGENTS.md` (or `AGENTS.override.md`), then one file per directory
74932
+ * level from the project root down to the current working directory, joined
74933
+ * root-first so deeper files take precedence. Nested per-directory files are
74934
+ * therefore a real scoping surface, and Codex's docs recommend splitting large
74935
+ * instruction sets across nested directories. It does NOT scan a
74936
+ * `.codex/memories/` directory for instruction files — that directory belongs
74937
+ * to Codex's separate SQLite-backed auto-memory system.
74938
+ *
74939
+ * A non-root rule carrying `agentsmd.subprojectPath` is emitted as a nested
74940
+ * `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
74941
+ * folds into the root `AGENTS.md`, since Codex has no modular rules directory.
74942
+ * The project files on the root-to-cwd chain share one `project_doc_max_bytes`
74943
+ * budget (32 KiB by default), which the RulesProcessor warns about.
74944
+ *
74945
+ * @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
74946
+ */
74947
+ var CodexcliRule = class extends NestedAgentsmdRule {
74948
+ static getFamily() {
74949
+ return {
74950
+ globalDir: CODEXCLI_DIR,
74951
+ toolTarget: "codexcli"
74952
+ };
74953
+ }
74954
+ };
74955
+ //#endregion
74763
74956
  //#region src/features/rules/commandcode-rule.ts
74764
74957
  var CommandcodeRule = class CommandcodeRule extends ToolRule {
74765
74958
  constructor({ fileContent, root, ...rest }) {
@@ -76064,122 +76257,6 @@ var DevinRule = class DevinRule extends ToolRule {
76064
76257
  }
76065
76258
  };
76066
76259
  //#endregion
76067
- //#region src/features/rules/nested-agentsmd-rule.ts
76068
- /**
76069
- * Shared adapter for the targets that read instructions exactly the way the
76070
- * AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
76071
- * project root, nested per-directory `AGENTS.md` files loaded only while working
76072
- * under that directory, and a single personal root file under a tool-specific
76073
- * home directory. Such a target has no modular non-root instruction directory,
76074
- * so every other non-root rule folds into the root file.
76075
- *
76076
- * Concrete targets (Pool, Vibe, DeepSeek Harness) only supply the global
76077
- * directory and their tool target through {@link getFamily}; the vendor docs
76078
- * describing each one's discovery walk live on the subclass.
76079
- *
76080
- * The nested-file scan mirrors the AGENTS.md standard's nested discovery —
76081
- * same file name, same exclusions, import-only, project scope — because it
76082
- * discovers literally the same files.
76083
- */
76084
- var NestedAgentsmdRule = class extends ToolRule {
76085
- static getFamily() {
76086
- throw new Error("Please implement this method in the subclass.");
76087
- }
76088
- static getSettablePaths({ global = false } = {}) {
76089
- if (global) return { root: {
76090
- relativeDirPath: this.getFamily().globalDir,
76091
- relativeFilePath: AGENTSMD_RULE_FILE_NAME
76092
- } };
76093
- return { root: {
76094
- relativeDirPath: ".",
76095
- relativeFilePath: AGENTSMD_RULE_FILE_NAME
76096
- } };
76097
- }
76098
- static getNestedFilePatterns() {
76099
- return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
76100
- }
76101
- /**
76102
- * The subproject directory this rule scopes, or `undefined` for the root file
76103
- * (project or global).
76104
- */
76105
- getSubprojectPath() {
76106
- return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
76107
- }
76108
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
76109
- const { root } = this.getSettablePaths({ global });
76110
- if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
76111
- const fileContent = await readFileContent(join(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
76112
- return new this({
76113
- outputRoot,
76114
- relativeDirPath: overrideDirPath,
76115
- relativeFilePath: AGENTSMD_RULE_FILE_NAME,
76116
- fileContent,
76117
- validate,
76118
- root: false
76119
- });
76120
- }
76121
- const fileContent = await readFileContent(join(outputRoot, root.relativeDirPath, root.relativeFilePath));
76122
- return new this({
76123
- outputRoot,
76124
- relativeDirPath: root.relativeDirPath,
76125
- relativeFilePath: root.relativeFilePath,
76126
- fileContent,
76127
- validate,
76128
- root: true
76129
- });
76130
- }
76131
- static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
76132
- const { root } = this.getSettablePaths({ global });
76133
- const frontmatter = rulesyncRule.getFrontmatter();
76134
- const isRoot = frontmatter.root ?? false;
76135
- const subprojectPath = frontmatter.agentsmd?.subprojectPath;
76136
- if (!global && !isRoot && subprojectPath) return new this({
76137
- outputRoot,
76138
- relativeDirPath: join(subprojectPath),
76139
- relativeFilePath: AGENTSMD_RULE_FILE_NAME,
76140
- fileContent: rulesyncRule.getBody(),
76141
- validate,
76142
- root: false
76143
- });
76144
- return new this({
76145
- outputRoot,
76146
- relativeDirPath: root.relativeDirPath,
76147
- relativeFilePath: root.relativeFilePath,
76148
- fileContent: rulesyncRule.getBody(),
76149
- validate,
76150
- root: isRoot
76151
- });
76152
- }
76153
- toRulesyncRule() {
76154
- const subprojectPath = this.getSubprojectPath();
76155
- if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
76156
- return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
76157
- }
76158
- validate() {
76159
- return {
76160
- success: true,
76161
- error: null
76162
- };
76163
- }
76164
- static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
76165
- const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
76166
- return new this({
76167
- outputRoot,
76168
- relativeDirPath,
76169
- relativeFilePath,
76170
- fileContent: "",
76171
- validate: false,
76172
- root: isRoot
76173
- });
76174
- }
76175
- static isTargetedByRulesyncRule(rulesyncRule) {
76176
- return this.isTargetedByRulesyncRuleDefault({
76177
- rulesyncRule,
76178
- toolTarget: this.getFamily().toolTarget
76179
- });
76180
- }
76181
- };
76182
- //#endregion
76183
76260
  //#region src/features/rules/dsh-rule.ts
76184
76261
  /**
76185
76262
  * DeepSeek Harness (`dsh`) rules.
@@ -76391,7 +76468,8 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
76391
76468
  rulesyncRule,
76392
76469
  validate,
76393
76470
  rootPath: paths.root,
76394
- nonRootPath: paths.nonRoot
76471
+ nonRootPath: paths.nonRoot,
76472
+ global
76395
76473
  }));
76396
76474
  }
76397
76475
  toRulesyncRule() {
@@ -76858,7 +76936,8 @@ var KiloRule = class KiloRule extends ToolRule {
76858
76936
  rulesyncRule,
76859
76937
  validate,
76860
76938
  rootPath: paths.root,
76861
- nonRootPath: paths.nonRoot
76939
+ nonRootPath: paths.nonRoot,
76940
+ global
76862
76941
  }));
76863
76942
  }
76864
76943
  toRulesyncRule() {
@@ -77362,7 +77441,8 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
77362
77441
  rulesyncRule,
77363
77442
  validate,
77364
77443
  rootPath: paths.root,
77365
- nonRootPath: paths.nonRoot
77444
+ nonRootPath: paths.nonRoot,
77445
+ global
77366
77446
  }));
77367
77447
  }
77368
77448
  toRulesyncRule() {
@@ -78831,6 +78911,14 @@ var ZoocodeRule = class extends RooRule {
78831
78911
  //#endregion
78832
78912
  //#region src/features/rules/rules-processor.ts
78833
78913
  const RulesProcessorToolTargetSchema = z.enum(rulesProcessorToolTargetTuple);
78914
+ /**
78915
+ * Normalizes a relative directory path for comparison: POSIX separators, no
78916
+ * `./` segments and no trailing slash, with the current directory as `"."`
78917
+ * (so `"./"`, `"a/"` and `"a"` compare the way `join()` groups them).
78918
+ */
78919
+ const normalizeRelativeDir = (dir) => posix.normalize(toPosixPath(dir)).replace(/\/+$/, "") || ".";
78920
+ /** A tool rule's output path relative to its output root, for comparison. */
78921
+ const toolOutputPath = (toolRule) => posix.join(normalizeRelativeDir(toolRule.getRelativeDirPath()), toolRule.getRelativeFilePath());
78834
78922
  const formatRulePaths = (rules) => rules.map((r) => join(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
78835
78923
  const RulesFeatureOptionsSchema = z.looseObject({
78836
78924
  ruleDiscoveryMode: z.optional(z.enum(["none", "explicit"])),
@@ -78999,7 +79087,11 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
78999
79087
  extension: "md",
79000
79088
  supportsGlobal: true,
79001
79089
  ruleDiscoveryMode: "auto",
79002
- collisionPolicy: "fold"
79090
+ collisionPolicy: "fold",
79091
+ projectInstructionBudget: {
79092
+ bytes: CODEXCLI_PROJECT_DOC_MAX_BYTES,
79093
+ remedy: "Codex CLI stops reading project `AGENTS.md` files once their combined size on the root-to-working-directory chain reaches `project_doc_max_bytes` (32 KiB by default) and silently drops the rest. Move directory-scoped rules into nested `AGENTS.md` files with `agentsmd.subprojectPath` (or `deriveSubprojectPathFromGlobs: true` in rulesync.jsonc), trim the rules, or raise `project_doc_max_bytes` in `.codex/config.toml` (ignore this warning if you already have)."
79094
+ }
79003
79095
  }
79004
79096
  }],
79005
79097
  ["commandcode", {
@@ -79457,9 +79549,61 @@ var RulesProcessor = class extends FeatureProcessor {
79457
79549
  toolRules,
79458
79550
  factory
79459
79551
  });
79552
+ this.warnForInstructionBudget({
79553
+ toolRules,
79554
+ meta
79555
+ });
79460
79556
  return outputFiles;
79461
79557
  }
79462
79558
  /**
79559
+ * Warn when a generated project instruction chain exceeds the tool's byte
79560
+ * budget (`meta.projectInstructionBudget`).
79561
+ *
79562
+ * Codex CLI concatenates the project `AGENTS.md` files from the project root
79563
+ * down to the cwd and stops once their combined size reaches
79564
+ * `project_doc_max_bytes`, truncating the file that crosses it — silently, so
79565
+ * without this warning the rules past the limit simply never reach the model.
79566
+ * Each generated file is measured together with the generated files in its
79567
+ * ancestor directories, since that is the chain Codex loads while working
79568
+ * there. When the root file alone is over the budget every chain is, so only
79569
+ * the root is reported; likewise each over-budget nested chain is reported
79570
+ * once, at its shallowest directory. With several output roots the warning
79571
+ * names the root it refers to. An output root below the git root is measured
79572
+ * without the parent directories' files Codex would also load, so the
79573
+ * estimate can come out low there. The budget checked is the tool's default: a value
79574
+ * raised by hand in the tool's own config is not read. Global scope is not
79575
+ * checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
79576
+ * project budget.
79577
+ */
79578
+ warnForInstructionBudget({ toolRules, meta }) {
79579
+ const budget = meta.projectInstructionBudget;
79580
+ if (budget === void 0 || this.global) return;
79581
+ const files = toolRules.map((rule) => ({
79582
+ dir: normalizeRelativeDir(rule.getRelativeDirPath()),
79583
+ path: toPosixPath(join(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
79584
+ bytes: Buffer.byteLength(addTrailingNewline(rule.getFileContent()), "utf8")
79585
+ }));
79586
+ const isAncestorOrSelf = (ancestor, dir) => ancestor === "." || ancestor === dir || dir.startsWith(`${ancestor}/`);
79587
+ const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
79588
+ const rootFiles = files.filter(({ dir }) => dir === ".");
79589
+ const rootBytes = rootFiles.reduce((sum, { bytes }) => sum + bytes, 0);
79590
+ if (rootBytes > budget.bytes) {
79591
+ this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${budget.bytes}-byte limit. ${budget.remedy}`);
79592
+ return;
79593
+ }
79594
+ const reportedDirs = [];
79595
+ const nestedFiles = files.filter(({ dir }) => dir !== ".").toSorted((a, b) => a.dir.split("/").length - b.dir.split("/").length);
79596
+ for (const file of nestedFiles) {
79597
+ if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
79598
+ const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
79599
+ const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
79600
+ if (chainBytes > budget.bytes) {
79601
+ reportedDirs.push(file.dir);
79602
+ this.logger.warn(`The generated ${this.toolTarget} instruction files${location} loaded while working in '${file.dir}' (${chain.map(({ path }) => path).join(", ")}) total ${chainBytes} bytes, over the ${budget.bytes}-byte limit (as is every directory below it). ${budget.remedy}`);
79603
+ }
79604
+ }
79605
+ }
79606
+ /**
79463
79607
  * Handle localRoot rules (only in non-global mode and when enabled). Mutates
79464
79608
  * `toolRules` in place.
79465
79609
  */
@@ -80131,14 +80275,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
80131
80275
  * decides for all of them: its `pi.contextFile` is copied onto the non-root
80132
80276
  * rules, and the flag set only on a non-root rule is dropped with a warning.
80133
80277
  */
80134
- alignPiContextFile(rules) {
80278
+ alignPiContextFile(rules, { warn = true } = {}) {
80135
80279
  if (this.toolTarget !== "pi") return rules;
80136
80280
  const factory = this.getFactory(this.toolTarget);
80137
80281
  const targeted = rules.filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
80138
80282
  const rootContextFile = targeted.some((rule) => rule.getFrontmatter().root === true && rule.getFrontmatter().pi?.contextFile === "override") ? "override" : void 0;
80139
80283
  const mismatched = targeted.filter((rule) => rule.getFrontmatter().pi?.contextFile !== rootContextFile);
80140
80284
  if (mismatched.length === 0) return rules;
80141
- if (rootContextFile === void 0) this.logger.warn(`pi.contextFile is set on ${mismatched.length} non-root rule(s) but not on the root rule, so it is ignored: Pi folds every rule body into the root context file, and emitting AGENTS.override.md for some of them would hide the rest. Set it on the root rule instead: ${formatRulePaths(mismatched)}`);
80285
+ if (warn && rootContextFile === void 0) this.logger.warn(`pi.contextFile is set on ${mismatched.length} non-root rule(s) but not on the root rule, so it is ignored: Pi folds every rule body into the root context file, and emitting AGENTS.override.md for some of them would hide the rest. Set it on the root rule instead: ${formatRulePaths(mismatched)}`);
80142
80286
  const mismatchedSet = new Set(mismatched);
80143
80287
  return rules.map((rule) => {
80144
80288
  if (!mismatchedSet.has(rule)) return rule;
@@ -80192,14 +80336,72 @@ As this project's AI coding tool, you must follow the additional conventions bel
80192
80336
  * duplication check the same way `loadRulesyncFiles`'s global-mode branch
80193
80337
  * excludes it from `nonRootRules`: `generate` ignores `localRoot` entirely
80194
80338
  * in global mode, so such a rule is never actually folded into the global
80195
- * root output and warning about it here would be inaccurate.
80339
+ * root output and warning about it here would be inaccurate. The same goes
80340
+ * for a tool that writes `localRoot` to a dedicated local file.
80341
+ *
80342
+ * Non-root rules written to a file other than the root (nested
80343
+ * per-directory files, Pi's `APPEND_SYSTEM.md`) get a separate warning when
80344
+ * that file already exists and would import under a different name.
80196
80345
  */
80197
80346
  async warnForFoldImportDuplicationRisk() {
80198
80347
  const factory = this.getFactory(this.toolTarget);
80199
80348
  if (factory.meta.collisionPolicy !== "fold") return;
80200
- const nonRootRules = (await this.loadMergedRulesyncRules()).filter((rule) => !rule.getFrontmatter().root && (!this.global || !rule.getFrontmatter().localRoot) && factory.class.isTargetedByRulesyncRule(rule));
80201
- if (nonRootRules.length === 0) return;
80202
- this.logger.warn(`Importing ${this.toolTarget}'s root file will re-add content already folded from ${formatRulePaths(nonRootRules)}: ${this.toolTarget} concatenates every non-root rule into its single root output file, so the imported copy duplicates them the next time you run \`rulesync generate --targets ${this.toolTarget}\`. Review the imported rule and remove the duplicated content, or remove the original non-root rule files, before generating again.`);
80349
+ const targetedRules = this.alignPiContextFile(await this.loadMergedRulesyncRules(), { warn: false }).filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
80350
+ let includeLocalRoot = !this.global;
80351
+ try {
80352
+ includeLocalRoot &&= resolveIncludeLocalRoot(this.featureOptions);
80353
+ } catch {}
80354
+ const targetedNonRootRules = targetedRules.filter((rule) => !rule.getFrontmatter().root && !(rule.getFrontmatter().localRoot && (!includeLocalRoot || factory.meta.localRootMode === "separate-local-file")));
80355
+ const rootPaths = /* @__PURE__ */ new Set();
80356
+ const { root } = factory.class.getSettablePaths({ global: this.global });
80357
+ if (root) rootPaths.add(posix.join(normalizeRelativeDir(root.relativeDirPath), root.relativeFilePath));
80358
+ for (const rulesyncRule of targetedRules.filter((rule) => rule.getFrontmatter().root)) {
80359
+ const toolRule = this.toToolRuleForImportCheck({
80360
+ factory,
80361
+ rulesyncRule
80362
+ });
80363
+ rootPaths.add(toolOutputPath(toolRule));
80364
+ }
80365
+ const separateRules = [];
80366
+ const foldedRules = [];
80367
+ let localRootCounted = false;
80368
+ for (const rulesyncRule of targetedNonRootRules) {
80369
+ if (rulesyncRule.getFrontmatter().localRoot) {
80370
+ if (!localRootCounted) {
80371
+ foldedRules.push(rulesyncRule);
80372
+ localRootCounted = true;
80373
+ }
80374
+ continue;
80375
+ }
80376
+ const toolRule = this.toToolRuleForImportCheck({
80377
+ factory,
80378
+ rulesyncRule
80379
+ });
80380
+ if (rootPaths.size === 0 || rootPaths.has(toolOutputPath(toolRule))) foldedRules.push(rulesyncRule);
80381
+ else separateRules.push({
80382
+ toolRule,
80383
+ rulesyncRule
80384
+ });
80385
+ }
80386
+ if (foldedRules.length > 0) this.logger.warn(`Importing ${this.toolTarget}'s root file will re-add content already folded from ${formatRulePaths(foldedRules)}: ${this.toolTarget} concatenates every non-root rule into its single root output file, so the imported copy duplicates them the next time you run \`rulesync generate --targets ${this.toolTarget}\`. Review the imported rule and remove the duplicated content, or remove the original non-root rule files, before generating again.`);
80387
+ const duplicated = [];
80388
+ for (const conversion of separateRules) {
80389
+ const { toolRule, rulesyncRule } = conversion;
80390
+ if (toolRule.toRulesyncRule().getRelativeFilePath().toLowerCase() === rulesyncRule.getRelativeFilePath().toLowerCase()) continue;
80391
+ if (await fileExists(toolRule.getFilePath())) duplicated.push(conversion);
80392
+ }
80393
+ if (duplicated.length > 0) {
80394
+ const fileNames = [...new Set(duplicated.map(({ toolRule }) => toolRule.getRelativeFilePath()))].join(", ");
80395
+ this.logger.warn(`Importing ${this.toolTarget}'s ${fileNames} files outside the root file will re-add content already written from ${formatRulePaths(duplicated.map(({ rulesyncRule }) => rulesyncRule))}: each such file is imported as its own rule under a name derived from its path, so the imported copy and the original rule resolve to the same file and are concatenated the next time you run \`rulesync generate --targets ${this.toolTarget}\`. Remove one of the two copies before generating again.`);
80396
+ }
80397
+ }
80398
+ toToolRuleForImportCheck({ factory, rulesyncRule }) {
80399
+ return factory.class.fromRulesyncRule({
80400
+ outputRoot: this.outputRoot,
80401
+ rulesyncRule,
80402
+ validate: false,
80403
+ global: this.global
80404
+ });
80203
80405
  }
80204
80406
  /**
80205
80407
  * Implementation of abstract method from FeatureProcessor
@@ -80493,6 +80695,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
80493
80695
  function isPackagingToolTarget(toolTarget) {
80494
80696
  return PACKAGING_TOOL_TARGETS.includes(toolTarget);
80495
80697
  }
80698
+ /**
80699
+ * Packaging targets write into a plugin directory and support project scope
80700
+ * only. In global mode their output root would be the home directory, so
80701
+ * callers reject or skip them up front instead of checking all of $HOME.
80702
+ */
80703
+ function formatPackagingTargetGlobalMessage(toolTarget) {
80704
+ return `Target '${toolTarget}' is a plugin packaging target and supports only project scope. Re-run without '--global'.`;
80705
+ }
80496
80706
  async function assertPluginRootSafe(params) {
80497
80707
  if (!isPackagingToolTarget(params.toolTarget)) return;
80498
80708
  await assertDirectoryIfExists(params.outputRoot);
@@ -81412,14 +81622,17 @@ function warnUnsupportedTargets(params) {
81412
81622
  } catch {
81413
81623
  oppositeScopeTargets = [];
81414
81624
  }
81415
- for (const target of config.getTargets()) if (!supportedTargets.includes(target) && config.getFeatures(target).includes(featureName)) {
81416
- const simulateOption = SIMULATE_OPTION_MAP[featureName];
81417
- if (simulateOption && simulatedTargets.includes(target)) logger.warn(`Target '${target}' only supports simulated '${featureName}'. Use '${simulateOption}' to enable it. Skipping.`);
81418
- else if (oppositeScopeTargets.includes(target)) {
81419
- const supportedScope = config.getGlobal() ? "project" : "global";
81420
- const retry = config.getGlobal() ? "without '--global'" : "with '--global'";
81421
- logger.warn(`Target '${target}' supports the feature '${featureName}' only in ${supportedScope} scope. Re-run ${retry}. Skipping.`);
81422
- } else logger.warn(`Target '${target}' does not support the feature '${featureName}'. Skipping.`);
81625
+ for (const target of config.getTargets()) {
81626
+ if (config.getGlobal() && isPackagingToolTarget(target)) continue;
81627
+ if (!supportedTargets.includes(target) && config.getFeatures(target).includes(featureName)) {
81628
+ const simulateOption = SIMULATE_OPTION_MAP[featureName];
81629
+ if (simulateOption && simulatedTargets.includes(target)) logger.warn(`Target '${target}' only supports simulated '${featureName}'. Use '${simulateOption}' to enable it. Skipping.`);
81630
+ else if (oppositeScopeTargets.includes(target)) {
81631
+ const supportedScope = config.getGlobal() ? "project" : "global";
81632
+ const retry = config.getGlobal() ? "without '--global'" : "with '--global'";
81633
+ logger.warn(`Target '${target}' supports the feature '${featureName}' only in ${supportedScope} scope. Re-run ${retry}. Skipping.`);
81634
+ } else logger.warn(`Target '${target}' does not support the feature '${featureName}'. Skipping.`);
81635
+ }
81423
81636
  }
81424
81637
  }
81425
81638
  /**
@@ -81672,10 +81885,16 @@ async function generate(params) {
81672
81885
  const { config, logger } = params;
81673
81886
  resetRootShadowingWarnings({ logger });
81674
81887
  resetRunWarningState();
81675
- for (const toolTarget of config.getTargets()) for (const outputRoot of config.getOutputRoots(toolTarget)) await assertPluginRootSafe({
81676
- toolTarget,
81677
- outputRoot
81678
- });
81888
+ for (const toolTarget of config.getTargets()) {
81889
+ if (config.getGlobal() && isPackagingToolTarget(toolTarget)) {
81890
+ logger.warn(`${formatPackagingTargetGlobalMessage(toolTarget)} Skipping.`);
81891
+ continue;
81892
+ }
81893
+ for (const outputRoot of config.getOutputRoots(toolTarget)) await assertPluginRootSafe({
81894
+ toolTarget,
81895
+ outputRoot
81896
+ });
81897
+ }
81679
81898
  await warnSkillSubagentNameCollisions({
81680
81899
  config,
81681
81900
  logger
@@ -82292,6 +82511,7 @@ function getToolOutputRoot({ config, tool }) {
82292
82511
  async function importFromTool(params) {
82293
82512
  const { config, tool, logger } = params;
82294
82513
  resetRunWarningState();
82514
+ if (config.getGlobal() && isPackagingToolTarget(tool)) throw new Error(formatPackagingTargetGlobalMessage(tool));
82295
82515
  await assertPluginRootSafe({
82296
82516
  toolTarget: tool,
82297
82517
  outputRoot: getToolOutputRoot({
@@ -82352,7 +82572,7 @@ async function importRulesCore(params) {
82352
82572
  if (!config.getFeatures(tool).includes("rules")) return 0;
82353
82573
  const global = config.getGlobal();
82354
82574
  if (!RulesProcessor.getToolTargets({ global }).includes(tool)) return 0;
82355
- const rulesProcessor = new RulesProcessor({
82575
+ const baseParams = {
82356
82576
  outputRoot: getToolOutputRoot({
82357
82577
  config,
82358
82578
  tool
@@ -82360,13 +82580,23 @@ async function importRulesCore(params) {
82360
82580
  toolTarget: tool,
82361
82581
  global,
82362
82582
  logger
82363
- });
82583
+ };
82584
+ const rulesProcessor = new RulesProcessor(baseParams);
82364
82585
  const toolFiles = await rulesProcessor.loadToolFiles();
82365
82586
  if (toolFiles.length === 0) {
82366
82587
  logger.warn(`No rule files found for ${tool}. Skipping import.`);
82367
82588
  return 0;
82368
82589
  }
82369
- await rulesProcessor.warnForFoldImportDuplicationRisk();
82590
+ try {
82591
+ await new RulesProcessor({
82592
+ ...baseParams,
82593
+ inputRoots: config.getInputRoots(),
82594
+ deriveSubprojectPathFromGlobs: config.getDeriveSubprojectPathFromGlobs(),
82595
+ featureOptions: config.getFeatureOptions(tool, "rules")
82596
+ }).warnForFoldImportDuplicationRisk();
82597
+ } catch (error) {
82598
+ logger.warn(`Skipped the ${tool} import duplication check: ${formatError(error)}`);
82599
+ }
82370
82600
  const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
82371
82601
  const { count: writtenCount } = await rulesProcessor.writeAiFiles(rulesyncFiles);
82372
82602
  if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} rule files`);
@@ -82616,4 +82846,4 @@ async function importChecksCore(params) {
82616
82846
  //#endregion
82617
82847
  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 };
82618
82848
 
82619
- //# sourceMappingURL=import-CSKYRGa5.js.map
82849
+ //# sourceMappingURL=import-DNDrEvS2.js.map