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.
- package/dist/cli/index.cjs +5 -5
- package/dist/cli/index.js +5 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-CGT8v9bO.cjs → import-1vyJw3q0.cjs} +417 -187
- package/dist/{import-CSKYRGa5.js → import-DNDrEvS2.js} +401 -171
- package/dist/import-DNDrEvS2.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-CSKYRGa5.js.map +0 -1
|
@@ -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
|
|
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(
|
|
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, ...
|
|
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(
|
|
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/
|
|
74697
|
-
|
|
74698
|
-
|
|
74699
|
-
|
|
74700
|
-
|
|
74701
|
-
|
|
74702
|
-
|
|
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:
|
|
74708
|
-
relativeFilePath:
|
|
74837
|
+
relativeDirPath: ".",
|
|
74838
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74709
74839
|
} };
|
|
74710
74840
|
}
|
|
74711
|
-
static
|
|
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
|
-
|
|
74714
|
-
|
|
74715
|
-
|
|
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
|
|
74727
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
74915
|
+
root: isRoot
|
|
74753
74916
|
});
|
|
74754
74917
|
}
|
|
74755
74918
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
74756
74919
|
return this.isTargetedByRulesyncRuleDefault({
|
|
74757
74920
|
rulesyncRule,
|
|
74758
|
-
toolTarget:
|
|
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
|
|
80201
|
-
|
|
80202
|
-
|
|
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())
|
|
81416
|
-
|
|
81417
|
-
if (
|
|
81418
|
-
|
|
81419
|
-
|
|
81420
|
-
|
|
81421
|
-
|
|
81422
|
-
|
|
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())
|
|
81676
|
-
toolTarget
|
|
81677
|
-
|
|
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
|
|
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
|
-
|
|
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-
|
|
82849
|
+
//# sourceMappingURL=import-DNDrEvS2.js.map
|