rulesync 20.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-Cp3q904n.cjs → import-1vyJw3q0.cjs} +442 -208
- package/dist/{import-_UbLpXUZ.js → import-DNDrEvS2.js} +426 -192
- 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-_UbLpXUZ.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);
|
|
@@ -44986,14 +45097,14 @@ const COMMANDCODE_TOOL_TO_CATEGORY = {
|
|
|
44986
45097
|
web_fetch: "webfetch",
|
|
44987
45098
|
web_search: "websearch"
|
|
44988
45099
|
};
|
|
44989
|
-
const ACTION_RANK$
|
|
45100
|
+
const ACTION_RANK$2 = {
|
|
44990
45101
|
allow: 0,
|
|
44991
45102
|
ask: 1,
|
|
44992
45103
|
deny: 2
|
|
44993
45104
|
};
|
|
44994
45105
|
/** Whether `action` outranks `existing` (deny > ask > allow). */
|
|
44995
45106
|
function isStricterAction({ action, existing }) {
|
|
44996
|
-
return existing === void 0 || ACTION_RANK$
|
|
45107
|
+
return existing === void 0 || ACTION_RANK$2[action] > ACTION_RANK$2[existing];
|
|
44997
45108
|
}
|
|
44998
45109
|
/**
|
|
44999
45110
|
* Why a padded `Shell( * )` cannot stand in for the bare `Shell` in `allow`:
|
|
@@ -48341,20 +48452,22 @@ function convertFactorydroidToRulesyncPermissions({ allow, deny }) {
|
|
|
48341
48452
|
const GOOSE_GLOBAL_ONLY_MESSAGE = "Goose permissions are global-only; use --global to sync ~/.config/goose/permission.yaml";
|
|
48342
48453
|
const GOOSE_USER_KEY = "user";
|
|
48343
48454
|
const CATCH_ALL_PATTERN$4 = "*";
|
|
48344
|
-
const RULESYNC_TO_GOOSE_TOOL_NAME = {
|
|
48345
|
-
|
|
48346
|
-
|
|
48347
|
-
|
|
48348
|
-
|
|
48349
|
-
const GOOSE_TO_RULESYNC_TOOL_NAME = {
|
|
48350
|
-
developer__shell: "bash",
|
|
48351
|
-
developer__text_editor: "edit"
|
|
48455
|
+
const RULESYNC_TO_GOOSE_TOOL_NAME = { bash: "shell" };
|
|
48456
|
+
const GOOSE_TO_RULESYNC_TOOL_NAMES = {
|
|
48457
|
+
shell: ["bash"],
|
|
48458
|
+
developer__shell: ["bash"],
|
|
48459
|
+
developer__text_editor: ["edit", "write"]
|
|
48352
48460
|
};
|
|
48353
48461
|
const ACTION_TO_GOOSE_LIST = {
|
|
48354
48462
|
allow: "always_allow",
|
|
48355
48463
|
ask: "ask_before",
|
|
48356
48464
|
deny: "never_allow"
|
|
48357
48465
|
};
|
|
48466
|
+
const ACTION_RANK$1 = {
|
|
48467
|
+
allow: 0,
|
|
48468
|
+
ask: 1,
|
|
48469
|
+
deny: 2
|
|
48470
|
+
};
|
|
48358
48471
|
const GOOSE_LIST_TO_ACTION = {
|
|
48359
48472
|
always_allow: "allow",
|
|
48360
48473
|
ask_before: "ask",
|
|
@@ -48379,10 +48492,11 @@ const GOOSE_PERMISSION_LIST_KEYS = [
|
|
|
48379
48492
|
*
|
|
48380
48493
|
* Mapping (rulesync canonical -> Goose):
|
|
48381
48494
|
* - Action: `allow` -> `always_allow`, `ask` -> `ask_before`, `deny` -> `never_allow`.
|
|
48382
|
-
* - Tool name: `bash` -> `
|
|
48383
|
-
*
|
|
48384
|
-
*
|
|
48385
|
-
*
|
|
48495
|
+
* - Tool name: `bash` -> `shell`; any other category (including `edit` and
|
|
48496
|
+
* `write`) passes through verbatim as the Goose tool name (mirrors the
|
|
48497
|
+
* Gemini CLI adapter). The legacy `developer__shell` /
|
|
48498
|
+
* `developer__text_editor` names are still accepted on import, the latter
|
|
48499
|
+
* as both `edit` and `write`.
|
|
48386
48500
|
* - Granularity: Goose lists hold whole tool names, so only a category's
|
|
48387
48501
|
* catch-all `*` pattern is representable. Non-catch-all patterns cannot be
|
|
48388
48502
|
* expressed per-tool and are reported via `logger.warn` and skipped.
|
|
@@ -48498,8 +48612,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
48498
48612
|
never_allow: []
|
|
48499
48613
|
};
|
|
48500
48614
|
const assigned = /* @__PURE__ */ new Map();
|
|
48501
|
-
const
|
|
48502
|
-
for (const [category, rules] of orderedEntries) {
|
|
48615
|
+
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
48503
48616
|
const toolName = RULESYNC_TO_GOOSE_TOOL_NAME[category] ?? category;
|
|
48504
48617
|
for (const [pattern, action] of Object.entries(rules)) {
|
|
48505
48618
|
if (pattern !== CATCH_ALL_PATTERN$4) {
|
|
@@ -48507,12 +48620,15 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
48507
48620
|
continue;
|
|
48508
48621
|
}
|
|
48509
48622
|
const previous = assigned.get(toolName);
|
|
48510
|
-
if (previous
|
|
48511
|
-
|
|
48623
|
+
if (previous === action) continue;
|
|
48624
|
+
if (previous !== void 0) {
|
|
48625
|
+
const stricter = ACTION_RANK$1[action] > ACTION_RANK$1[previous] ? action : previous;
|
|
48626
|
+
logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). Keeping the stricter "${stricter}" (deny > ask > allow).`);
|
|
48627
|
+
if (stricter === previous) continue;
|
|
48512
48628
|
const previousList = lists[ACTION_TO_GOOSE_LIST[previous]];
|
|
48513
48629
|
const index = previousList.indexOf(toolName);
|
|
48514
48630
|
if (index !== -1) previousList.splice(index, 1);
|
|
48515
|
-
}
|
|
48631
|
+
}
|
|
48516
48632
|
lists[ACTION_TO_GOOSE_LIST[action]].push(toolName);
|
|
48517
48633
|
assigned.set(toolName, action);
|
|
48518
48634
|
}
|
|
@@ -48528,8 +48644,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
|
48528
48644
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
48529
48645
|
const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
|
|
48530
48646
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
48531
|
-
for (const toolName of toolNames) {
|
|
48532
|
-
const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
|
|
48647
|
+
for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
|
|
48533
48648
|
permission[category] ??= {};
|
|
48534
48649
|
permission[category][CATCH_ALL_PATTERN$4] = action;
|
|
48535
48650
|
}
|
|
@@ -59203,7 +59318,7 @@ const ClaudecodeSkillFrontmatterSchema = z.looseObject({
|
|
|
59203
59318
|
"disallowed-tools": z.optional(z.union([z.string(), z.array(z.string())])),
|
|
59204
59319
|
model: z.optional(z.string()),
|
|
59205
59320
|
effort: z.optional(z.string()),
|
|
59206
|
-
"argument-hint": z.optional(
|
|
59321
|
+
"argument-hint": z.optional(ClaudecodeArgumentHintSchema),
|
|
59207
59322
|
arguments: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
59208
59323
|
context: z.optional(z.string()),
|
|
59209
59324
|
agent: z.optional(z.string()),
|
|
@@ -65359,7 +65474,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
65359
65474
|
meta: {
|
|
65360
65475
|
supportsProject: true,
|
|
65361
65476
|
supportsSimulated: false,
|
|
65362
|
-
supportsGlobal: true
|
|
65477
|
+
supportsGlobal: true,
|
|
65478
|
+
lenientImport: true
|
|
65363
65479
|
}
|
|
65364
65480
|
}],
|
|
65365
65481
|
["claudecode-plugin", {
|
|
@@ -72308,7 +72424,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72308
72424
|
static buildToolRuleParamsAgentsmd({ outputRoot = process.cwd(), rulesyncRule, validate = true, rootPath = {
|
|
72309
72425
|
relativeDirPath: ".",
|
|
72310
72426
|
relativeFilePath: "AGENTS.md"
|
|
72311
|
-
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH } }) {
|
|
72427
|
+
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH }, global = false }) {
|
|
72312
72428
|
const params = this.buildToolRuleParamsDefault({
|
|
72313
72429
|
outputRoot,
|
|
72314
72430
|
rulesyncRule,
|
|
@@ -72317,7 +72433,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72317
72433
|
nonRootPath
|
|
72318
72434
|
});
|
|
72319
72435
|
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
72320
|
-
if (!rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72436
|
+
if (!global && !rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72321
72437
|
params.relativeDirPath = join(rulesyncFrontmatter.agentsmd.subprojectPath);
|
|
72322
72438
|
params.relativeFilePath = "AGENTS.md";
|
|
72323
72439
|
}
|
|
@@ -72974,7 +73090,8 @@ var AmpRule = class AmpRule extends ToolRule {
|
|
|
72974
73090
|
rulesyncRule,
|
|
72975
73091
|
validate,
|
|
72976
73092
|
rootPath: paths.root,
|
|
72977
|
-
nonRootPath: paths.nonRoot
|
|
73093
|
+
nonRootPath: paths.nonRoot,
|
|
73094
|
+
global
|
|
72978
73095
|
});
|
|
72979
73096
|
if (!params.root && params.globs !== void 0 && params.globs.length > 0) params.fileContent = stringifyFrontmatter(params.fileContent, { globs: params.globs });
|
|
72980
73097
|
return new AmpRule(params);
|
|
@@ -74269,7 +74386,8 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
74269
74386
|
outputRoot,
|
|
74270
74387
|
rulesyncRule,
|
|
74271
74388
|
validate,
|
|
74272
|
-
rootPath: paths.root
|
|
74389
|
+
rootPath: paths.root,
|
|
74390
|
+
global
|
|
74273
74391
|
}));
|
|
74274
74392
|
}
|
|
74275
74393
|
if (isRoot) return new ClineRule({
|
|
@@ -74689,26 +74807,62 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
74689
74807
|
}
|
|
74690
74808
|
};
|
|
74691
74809
|
//#endregion
|
|
74692
|
-
//#region src/features/rules/
|
|
74693
|
-
|
|
74694
|
-
|
|
74695
|
-
|
|
74696
|
-
|
|
74697
|
-
|
|
74698
|
-
|
|
74699
|
-
|
|
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.");
|
|
74700
74830
|
}
|
|
74701
74831
|
static getSettablePaths({ global = false } = {}) {
|
|
74832
|
+
if (global) return { root: {
|
|
74833
|
+
relativeDirPath: this.getFamily().globalDir,
|
|
74834
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74835
|
+
} };
|
|
74702
74836
|
return { root: {
|
|
74703
|
-
relativeDirPath:
|
|
74704
|
-
relativeFilePath:
|
|
74837
|
+
relativeDirPath: ".",
|
|
74838
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74705
74839
|
} };
|
|
74706
74840
|
}
|
|
74707
|
-
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 }) {
|
|
74708
74852
|
const { root } = this.getSettablePaths({ global });
|
|
74709
|
-
|
|
74710
|
-
|
|
74711
|
-
|
|
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({
|
|
74712
74866
|
outputRoot,
|
|
74713
74867
|
relativeDirPath: root.relativeDirPath,
|
|
74714
74868
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74719,8 +74873,18 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74719
74873
|
}
|
|
74720
74874
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
74721
74875
|
const { root } = this.getSettablePaths({ global });
|
|
74722
|
-
const
|
|
74723
|
-
|
|
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({
|
|
74724
74888
|
outputRoot,
|
|
74725
74889
|
relativeDirPath: root.relativeDirPath,
|
|
74726
74890
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74730,7 +74894,9 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74730
74894
|
});
|
|
74731
74895
|
}
|
|
74732
74896
|
toRulesyncRule() {
|
|
74733
|
-
|
|
74897
|
+
const subprojectPath = this.getSubprojectPath();
|
|
74898
|
+
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
74899
|
+
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
74734
74900
|
}
|
|
74735
74901
|
validate() {
|
|
74736
74902
|
return {
|
|
@@ -74739,23 +74905,54 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74739
74905
|
};
|
|
74740
74906
|
}
|
|
74741
74907
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
74742
|
-
|
|
74908
|
+
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
74909
|
+
return new this({
|
|
74743
74910
|
outputRoot,
|
|
74744
74911
|
relativeDirPath,
|
|
74745
74912
|
relativeFilePath,
|
|
74746
74913
|
fileContent: "",
|
|
74747
74914
|
validate: false,
|
|
74748
|
-
root:
|
|
74915
|
+
root: isRoot
|
|
74749
74916
|
});
|
|
74750
74917
|
}
|
|
74751
74918
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
74752
74919
|
return this.isTargetedByRulesyncRuleDefault({
|
|
74753
74920
|
rulesyncRule,
|
|
74754
|
-
toolTarget:
|
|
74921
|
+
toolTarget: this.getFamily().toolTarget
|
|
74755
74922
|
});
|
|
74756
74923
|
}
|
|
74757
74924
|
};
|
|
74758
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
|
|
74759
74956
|
//#region src/features/rules/commandcode-rule.ts
|
|
74760
74957
|
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
74761
74958
|
constructor({ fileContent, root, ...rest }) {
|
|
@@ -76060,122 +76257,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
76060
76257
|
}
|
|
76061
76258
|
};
|
|
76062
76259
|
//#endregion
|
|
76063
|
-
//#region src/features/rules/nested-agentsmd-rule.ts
|
|
76064
|
-
/**
|
|
76065
|
-
* Shared adapter for the targets that read instructions exactly the way the
|
|
76066
|
-
* AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
|
|
76067
|
-
* project root, nested per-directory `AGENTS.md` files loaded only while working
|
|
76068
|
-
* under that directory, and a single personal root file under a tool-specific
|
|
76069
|
-
* home directory. Such a target has no modular non-root instruction directory,
|
|
76070
|
-
* so every other non-root rule folds into the root file.
|
|
76071
|
-
*
|
|
76072
|
-
* Concrete targets (Pool, Vibe, DeepSeek Harness) only supply the global
|
|
76073
|
-
* directory and their tool target through {@link getFamily}; the vendor docs
|
|
76074
|
-
* describing each one's discovery walk live on the subclass.
|
|
76075
|
-
*
|
|
76076
|
-
* The nested-file scan mirrors the AGENTS.md standard's nested discovery —
|
|
76077
|
-
* same file name, same exclusions, import-only, project scope — because it
|
|
76078
|
-
* discovers literally the same files.
|
|
76079
|
-
*/
|
|
76080
|
-
var NestedAgentsmdRule = class extends ToolRule {
|
|
76081
|
-
static getFamily() {
|
|
76082
|
-
throw new Error("Please implement this method in the subclass.");
|
|
76083
|
-
}
|
|
76084
|
-
static getSettablePaths({ global = false } = {}) {
|
|
76085
|
-
if (global) return { root: {
|
|
76086
|
-
relativeDirPath: this.getFamily().globalDir,
|
|
76087
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76088
|
-
} };
|
|
76089
|
-
return { root: {
|
|
76090
|
-
relativeDirPath: ".",
|
|
76091
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76092
|
-
} };
|
|
76093
|
-
}
|
|
76094
|
-
static getNestedFilePatterns() {
|
|
76095
|
-
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76096
|
-
}
|
|
76097
|
-
/**
|
|
76098
|
-
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
76099
|
-
* (project or global).
|
|
76100
|
-
*/
|
|
76101
|
-
getSubprojectPath() {
|
|
76102
|
-
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76103
|
-
}
|
|
76104
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
76105
|
-
const { root } = this.getSettablePaths({ global });
|
|
76106
|
-
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
76107
|
-
const fileContent = await readFileContent(join(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
76108
|
-
return new this({
|
|
76109
|
-
outputRoot,
|
|
76110
|
-
relativeDirPath: overrideDirPath,
|
|
76111
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76112
|
-
fileContent,
|
|
76113
|
-
validate,
|
|
76114
|
-
root: false
|
|
76115
|
-
});
|
|
76116
|
-
}
|
|
76117
|
-
const fileContent = await readFileContent(join(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
76118
|
-
return new this({
|
|
76119
|
-
outputRoot,
|
|
76120
|
-
relativeDirPath: root.relativeDirPath,
|
|
76121
|
-
relativeFilePath: root.relativeFilePath,
|
|
76122
|
-
fileContent,
|
|
76123
|
-
validate,
|
|
76124
|
-
root: true
|
|
76125
|
-
});
|
|
76126
|
-
}
|
|
76127
|
-
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
76128
|
-
const { root } = this.getSettablePaths({ global });
|
|
76129
|
-
const frontmatter = rulesyncRule.getFrontmatter();
|
|
76130
|
-
const isRoot = frontmatter.root ?? false;
|
|
76131
|
-
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
76132
|
-
if (!global && !isRoot && subprojectPath) return new this({
|
|
76133
|
-
outputRoot,
|
|
76134
|
-
relativeDirPath: join(subprojectPath),
|
|
76135
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76136
|
-
fileContent: rulesyncRule.getBody(),
|
|
76137
|
-
validate,
|
|
76138
|
-
root: false
|
|
76139
|
-
});
|
|
76140
|
-
return new this({
|
|
76141
|
-
outputRoot,
|
|
76142
|
-
relativeDirPath: root.relativeDirPath,
|
|
76143
|
-
relativeFilePath: root.relativeFilePath,
|
|
76144
|
-
fileContent: rulesyncRule.getBody(),
|
|
76145
|
-
validate,
|
|
76146
|
-
root: isRoot
|
|
76147
|
-
});
|
|
76148
|
-
}
|
|
76149
|
-
toRulesyncRule() {
|
|
76150
|
-
const subprojectPath = this.getSubprojectPath();
|
|
76151
|
-
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
76152
|
-
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
76153
|
-
}
|
|
76154
|
-
validate() {
|
|
76155
|
-
return {
|
|
76156
|
-
success: true,
|
|
76157
|
-
error: null
|
|
76158
|
-
};
|
|
76159
|
-
}
|
|
76160
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
76161
|
-
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
76162
|
-
return new this({
|
|
76163
|
-
outputRoot,
|
|
76164
|
-
relativeDirPath,
|
|
76165
|
-
relativeFilePath,
|
|
76166
|
-
fileContent: "",
|
|
76167
|
-
validate: false,
|
|
76168
|
-
root: isRoot
|
|
76169
|
-
});
|
|
76170
|
-
}
|
|
76171
|
-
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
76172
|
-
return this.isTargetedByRulesyncRuleDefault({
|
|
76173
|
-
rulesyncRule,
|
|
76174
|
-
toolTarget: this.getFamily().toolTarget
|
|
76175
|
-
});
|
|
76176
|
-
}
|
|
76177
|
-
};
|
|
76178
|
-
//#endregion
|
|
76179
76260
|
//#region src/features/rules/dsh-rule.ts
|
|
76180
76261
|
/**
|
|
76181
76262
|
* DeepSeek Harness (`dsh`) rules.
|
|
@@ -76387,7 +76468,8 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
76387
76468
|
rulesyncRule,
|
|
76388
76469
|
validate,
|
|
76389
76470
|
rootPath: paths.root,
|
|
76390
|
-
nonRootPath: paths.nonRoot
|
|
76471
|
+
nonRootPath: paths.nonRoot,
|
|
76472
|
+
global
|
|
76391
76473
|
}));
|
|
76392
76474
|
}
|
|
76393
76475
|
toRulesyncRule() {
|
|
@@ -76854,7 +76936,8 @@ var KiloRule = class KiloRule extends ToolRule {
|
|
|
76854
76936
|
rulesyncRule,
|
|
76855
76937
|
validate,
|
|
76856
76938
|
rootPath: paths.root,
|
|
76857
|
-
nonRootPath: paths.nonRoot
|
|
76939
|
+
nonRootPath: paths.nonRoot,
|
|
76940
|
+
global
|
|
76858
76941
|
}));
|
|
76859
76942
|
}
|
|
76860
76943
|
toRulesyncRule() {
|
|
@@ -77358,7 +77441,8 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
|
|
|
77358
77441
|
rulesyncRule,
|
|
77359
77442
|
validate,
|
|
77360
77443
|
rootPath: paths.root,
|
|
77361
|
-
nonRootPath: paths.nonRoot
|
|
77444
|
+
nonRootPath: paths.nonRoot,
|
|
77445
|
+
global
|
|
77362
77446
|
}));
|
|
77363
77447
|
}
|
|
77364
77448
|
toRulesyncRule() {
|
|
@@ -78827,6 +78911,14 @@ var ZoocodeRule = class extends RooRule {
|
|
|
78827
78911
|
//#endregion
|
|
78828
78912
|
//#region src/features/rules/rules-processor.ts
|
|
78829
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());
|
|
78830
78922
|
const formatRulePaths = (rules) => rules.map((r) => join(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
|
|
78831
78923
|
const RulesFeatureOptionsSchema = z.looseObject({
|
|
78832
78924
|
ruleDiscoveryMode: z.optional(z.enum(["none", "explicit"])),
|
|
@@ -78995,7 +79087,11 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
78995
79087
|
extension: "md",
|
|
78996
79088
|
supportsGlobal: true,
|
|
78997
79089
|
ruleDiscoveryMode: "auto",
|
|
78998
|
-
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
|
+
}
|
|
78999
79095
|
}
|
|
79000
79096
|
}],
|
|
79001
79097
|
["commandcode", {
|
|
@@ -79453,9 +79549,61 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
79453
79549
|
toolRules,
|
|
79454
79550
|
factory
|
|
79455
79551
|
});
|
|
79552
|
+
this.warnForInstructionBudget({
|
|
79553
|
+
toolRules,
|
|
79554
|
+
meta
|
|
79555
|
+
});
|
|
79456
79556
|
return outputFiles;
|
|
79457
79557
|
}
|
|
79458
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
|
+
/**
|
|
79459
79607
|
* Handle localRoot rules (only in non-global mode and when enabled). Mutates
|
|
79460
79608
|
* `toolRules` in place.
|
|
79461
79609
|
*/
|
|
@@ -80127,14 +80275,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80127
80275
|
* decides for all of them: its `pi.contextFile` is copied onto the non-root
|
|
80128
80276
|
* rules, and the flag set only on a non-root rule is dropped with a warning.
|
|
80129
80277
|
*/
|
|
80130
|
-
alignPiContextFile(rules) {
|
|
80278
|
+
alignPiContextFile(rules, { warn = true } = {}) {
|
|
80131
80279
|
if (this.toolTarget !== "pi") return rules;
|
|
80132
80280
|
const factory = this.getFactory(this.toolTarget);
|
|
80133
80281
|
const targeted = rules.filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
|
|
80134
80282
|
const rootContextFile = targeted.some((rule) => rule.getFrontmatter().root === true && rule.getFrontmatter().pi?.contextFile === "override") ? "override" : void 0;
|
|
80135
80283
|
const mismatched = targeted.filter((rule) => rule.getFrontmatter().pi?.contextFile !== rootContextFile);
|
|
80136
80284
|
if (mismatched.length === 0) return rules;
|
|
80137
|
-
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)}`);
|
|
80138
80286
|
const mismatchedSet = new Set(mismatched);
|
|
80139
80287
|
return rules.map((rule) => {
|
|
80140
80288
|
if (!mismatchedSet.has(rule)) return rule;
|
|
@@ -80188,14 +80336,72 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80188
80336
|
* duplication check the same way `loadRulesyncFiles`'s global-mode branch
|
|
80189
80337
|
* excludes it from `nonRootRules`: `generate` ignores `localRoot` entirely
|
|
80190
80338
|
* in global mode, so such a rule is never actually folded into the global
|
|
80191
|
-
* 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.
|
|
80192
80345
|
*/
|
|
80193
80346
|
async warnForFoldImportDuplicationRisk() {
|
|
80194
80347
|
const factory = this.getFactory(this.toolTarget);
|
|
80195
80348
|
if (factory.meta.collisionPolicy !== "fold") return;
|
|
80196
|
-
const
|
|
80197
|
-
|
|
80198
|
-
|
|
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
|
+
});
|
|
80199
80405
|
}
|
|
80200
80406
|
/**
|
|
80201
80407
|
* Implementation of abstract method from FeatureProcessor
|
|
@@ -80489,6 +80695,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80489
80695
|
function isPackagingToolTarget(toolTarget) {
|
|
80490
80696
|
return PACKAGING_TOOL_TARGETS.includes(toolTarget);
|
|
80491
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
|
+
}
|
|
80492
80706
|
async function assertPluginRootSafe(params) {
|
|
80493
80707
|
if (!isPackagingToolTarget(params.toolTarget)) return;
|
|
80494
80708
|
await assertDirectoryIfExists(params.outputRoot);
|
|
@@ -81408,14 +81622,17 @@ function warnUnsupportedTargets(params) {
|
|
|
81408
81622
|
} catch {
|
|
81409
81623
|
oppositeScopeTargets = [];
|
|
81410
81624
|
}
|
|
81411
|
-
for (const target of config.getTargets())
|
|
81412
|
-
|
|
81413
|
-
if (
|
|
81414
|
-
|
|
81415
|
-
|
|
81416
|
-
|
|
81417
|
-
|
|
81418
|
-
|
|
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
|
+
}
|
|
81419
81636
|
}
|
|
81420
81637
|
}
|
|
81421
81638
|
/**
|
|
@@ -81668,10 +81885,16 @@ async function generate(params) {
|
|
|
81668
81885
|
const { config, logger } = params;
|
|
81669
81886
|
resetRootShadowingWarnings({ logger });
|
|
81670
81887
|
resetRunWarningState();
|
|
81671
|
-
for (const toolTarget of config.getTargets())
|
|
81672
|
-
toolTarget
|
|
81673
|
-
|
|
81674
|
-
|
|
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
|
+
}
|
|
81675
81898
|
await warnSkillSubagentNameCollisions({
|
|
81676
81899
|
config,
|
|
81677
81900
|
logger
|
|
@@ -82288,6 +82511,7 @@ function getToolOutputRoot({ config, tool }) {
|
|
|
82288
82511
|
async function importFromTool(params) {
|
|
82289
82512
|
const { config, tool, logger } = params;
|
|
82290
82513
|
resetRunWarningState();
|
|
82514
|
+
if (config.getGlobal() && isPackagingToolTarget(tool)) throw new Error(formatPackagingTargetGlobalMessage(tool));
|
|
82291
82515
|
await assertPluginRootSafe({
|
|
82292
82516
|
toolTarget: tool,
|
|
82293
82517
|
outputRoot: getToolOutputRoot({
|
|
@@ -82348,7 +82572,7 @@ async function importRulesCore(params) {
|
|
|
82348
82572
|
if (!config.getFeatures(tool).includes("rules")) return 0;
|
|
82349
82573
|
const global = config.getGlobal();
|
|
82350
82574
|
if (!RulesProcessor.getToolTargets({ global }).includes(tool)) return 0;
|
|
82351
|
-
const
|
|
82575
|
+
const baseParams = {
|
|
82352
82576
|
outputRoot: getToolOutputRoot({
|
|
82353
82577
|
config,
|
|
82354
82578
|
tool
|
|
@@ -82356,13 +82580,23 @@ async function importRulesCore(params) {
|
|
|
82356
82580
|
toolTarget: tool,
|
|
82357
82581
|
global,
|
|
82358
82582
|
logger
|
|
82359
|
-
}
|
|
82583
|
+
};
|
|
82584
|
+
const rulesProcessor = new RulesProcessor(baseParams);
|
|
82360
82585
|
const toolFiles = await rulesProcessor.loadToolFiles();
|
|
82361
82586
|
if (toolFiles.length === 0) {
|
|
82362
82587
|
logger.warn(`No rule files found for ${tool}. Skipping import.`);
|
|
82363
82588
|
return 0;
|
|
82364
82589
|
}
|
|
82365
|
-
|
|
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
|
+
}
|
|
82366
82600
|
const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
|
|
82367
82601
|
const { count: writtenCount } = await rulesProcessor.writeAiFiles(rulesyncFiles);
|
|
82368
82602
|
if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} rule files`);
|
|
@@ -82612,4 +82846,4 @@ async function importChecksCore(params) {
|
|
|
82612
82846
|
//#endregion
|
|
82613
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 };
|
|
82614
82848
|
|
|
82615
|
-
//# sourceMappingURL=import-
|
|
82849
|
+
//# sourceMappingURL=import-DNDrEvS2.js.map
|