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
|
@@ -444,36 +444,36 @@ const isFeatureValueEnabled = (value) => {
|
|
|
444
444
|
const parseCommaSeparatedList = (value) => value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
445
445
|
//#endregion
|
|
446
446
|
//#region src/constants/rulesync-paths.ts
|
|
447
|
-
const { join: join$
|
|
447
|
+
const { join: join$351 } = node_path.posix;
|
|
448
448
|
const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
|
|
449
449
|
const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
|
|
450
450
|
const RULESYNC_USER_CONFIG_DIR_NAME = "rulesync";
|
|
451
451
|
const RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME = ".config";
|
|
452
452
|
const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
|
|
453
453
|
const RULES_FEATURE_SUBDIR = "rules";
|
|
454
|
-
const CURATED_RULES_FEATURE_SUBDIR = join$
|
|
454
|
+
const CURATED_RULES_FEATURE_SUBDIR = join$351(RULES_FEATURE_SUBDIR, ".curated");
|
|
455
455
|
const COMMANDS_FEATURE_SUBDIR = "commands";
|
|
456
456
|
const SUBAGENTS_FEATURE_SUBDIR = "subagents";
|
|
457
457
|
const CHECKS_FEATURE_SUBDIR = "checks";
|
|
458
458
|
const SKILLS_FEATURE_SUBDIR = "skills";
|
|
459
|
-
const CURATED_SKILLS_FEATURE_SUBDIR = join$
|
|
460
|
-
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$
|
|
461
|
-
const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$
|
|
462
|
-
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$
|
|
463
|
-
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$
|
|
464
|
-
const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$
|
|
465
|
-
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$
|
|
466
|
-
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$
|
|
467
|
-
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$
|
|
468
|
-
join$
|
|
469
|
-
const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$
|
|
470
|
-
const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$
|
|
459
|
+
const CURATED_SKILLS_FEATURE_SUBDIR = join$351(SKILLS_FEATURE_SUBDIR, ".curated");
|
|
460
|
+
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, RULES_FEATURE_SUBDIR);
|
|
461
|
+
const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, CURATED_RULES_FEATURE_SUBDIR);
|
|
462
|
+
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, COMMANDS_FEATURE_SUBDIR);
|
|
463
|
+
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, SUBAGENTS_FEATURE_SUBDIR);
|
|
464
|
+
const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, CHECKS_FEATURE_SUBDIR);
|
|
465
|
+
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
|
|
466
|
+
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
|
|
467
|
+
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
|
|
468
|
+
join$351(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
|
|
469
|
+
const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
|
|
470
|
+
const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
|
|
471
471
|
const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
|
|
472
|
-
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$
|
|
472
|
+
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
|
|
473
473
|
const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
|
|
474
474
|
const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
|
|
475
|
-
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$
|
|
476
|
-
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$
|
|
475
|
+
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, SKILLS_FEATURE_SUBDIR);
|
|
476
|
+
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$351(RULESYNC_RELATIVE_DIR_PATH, CURATED_SKILLS_FEATURE_SUBDIR);
|
|
477
477
|
const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
|
|
478
478
|
const RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync-npm.lock.json";
|
|
479
479
|
const RULESYNC_MCP_FILE_NAME = "mcp.jsonc";
|
|
@@ -12295,7 +12295,7 @@ const CODEXCLI_AGENTS_DIR_PATH = (0, node_path.join)(CODEXCLI_DIR, "agents");
|
|
|
12295
12295
|
const CODEXCLI_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
|
|
12296
12296
|
const CODEXCLI_HOOKS_FILE_NAME = "hooks.json";
|
|
12297
12297
|
const CODEXCLI_MCP_FILE_NAME = "config.toml";
|
|
12298
|
-
const
|
|
12298
|
+
const CODEXCLI_PROJECT_DOC_MAX_BYTES = 32768;
|
|
12299
12299
|
const CODEXCLI_BASH_RULES_FILE_NAME = "rulesync.rules";
|
|
12300
12300
|
const CODEXCLI_OPENAI_YAML_RELATIVE_PATH = (0, node_path.join)("agents", "openai.yaml");
|
|
12301
12301
|
const CODEXCLI_OVERRIDE_KEYS = [
|
|
@@ -15480,12 +15480,50 @@ var BobCommand = class BobCommand extends ToolCommand {
|
|
|
15480
15480
|
}
|
|
15481
15481
|
};
|
|
15482
15482
|
//#endregion
|
|
15483
|
+
//#region src/features/shared/claudecode-argument-hint.ts
|
|
15484
|
+
/**
|
|
15485
|
+
* Turns a YAML-list `argument-hint` back into the bracketed string form Claude
|
|
15486
|
+
* Code documents (`[issue-number]`, `[filename] [format]`), one bracketed
|
|
15487
|
+
* placeholder per list entry. The per-entry form is chosen over re-joining with
|
|
15488
|
+
* commas because it is the shape every Claude Code example uses. An empty list
|
|
15489
|
+
* carries no hint, so it yields `undefined` and the key is dropped.
|
|
15490
|
+
*/
|
|
15491
|
+
function joinArgumentHint(value) {
|
|
15492
|
+
if (typeof value === "string") return value;
|
|
15493
|
+
if (value.length === 0) return;
|
|
15494
|
+
return value.map((entry) => `[${String(entry)}]`).join(" ");
|
|
15495
|
+
}
|
|
15496
|
+
/**
|
|
15497
|
+
* `argument-hint` as Claude Code reads it from skill and command frontmatter.
|
|
15498
|
+
*
|
|
15499
|
+
* The documented examples (`[issue-number]`) are written unquoted, and YAML
|
|
15500
|
+
* parses an unquoted `[issue-number]` as a one-element list. Claude Code
|
|
15501
|
+
* accepts that form, so it is accepted here too and normalized to the
|
|
15502
|
+
* bracketed string, keeping the parsed value (and everything downstream of it)
|
|
15503
|
+
* a plain string. A scalar entry YAML did not read as a string (`[1]`,
|
|
15504
|
+
* `[true]`, `[~]`) is stringified rather than rejected.
|
|
15505
|
+
* https://code.claude.com/docs/en/skills
|
|
15506
|
+
*
|
|
15507
|
+
* A list entry is stringified from the value YAML produced, so its original
|
|
15508
|
+
* spelling is not kept (`[1.0]` becomes `[1]`), and an entry YAML reads as a
|
|
15509
|
+
* date (`[2026-01-01]`) is rejected.
|
|
15510
|
+
*
|
|
15511
|
+
* The unquoted multi-placeholder form (`argument-hint: [filename] [format]`)
|
|
15512
|
+
* is not valid YAML at all, so it fails before this schema is reached.
|
|
15513
|
+
*/
|
|
15514
|
+
const ClaudecodeArgumentHintSchema = zod_mini.z.pipe(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.union([
|
|
15515
|
+
zod_mini.z.string(),
|
|
15516
|
+
zod_mini.z.number(),
|
|
15517
|
+
zod_mini.z.boolean(),
|
|
15518
|
+
zod_mini.z.null()
|
|
15519
|
+
]))]), zod_mini.z.transform(joinArgumentHint));
|
|
15520
|
+
//#endregion
|
|
15483
15521
|
//#region src/features/commands/claudecode-command.ts
|
|
15484
15522
|
const ClaudecodeCommandFrontmatterSchema = zod_mini.z.looseObject({
|
|
15485
15523
|
description: zod_mini.z.optional(zod_mini.z.string()),
|
|
15486
15524
|
"allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
15487
15525
|
"disallowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
15488
|
-
"argument-hint": zod_mini.z.optional(
|
|
15526
|
+
"argument-hint": zod_mini.z.optional(ClaudecodeArgumentHintSchema),
|
|
15489
15527
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
15490
15528
|
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean())
|
|
15491
15529
|
});
|
|
@@ -15514,7 +15552,8 @@ var ClaudecodeCommand = class ClaudecodeCommand extends ToolCommand {
|
|
|
15514
15552
|
return this.frontmatter;
|
|
15515
15553
|
}
|
|
15516
15554
|
toRulesyncCommand() {
|
|
15517
|
-
const { description, ...
|
|
15555
|
+
const { description, ...extraFields } = this.frontmatter;
|
|
15556
|
+
const restFields = Object.fromEntries(Object.entries(extraFields).filter(([, value]) => value !== void 0));
|
|
15518
15557
|
const rulesyncFrontmatter = {
|
|
15519
15558
|
targets: ["*"],
|
|
15520
15559
|
description,
|
|
@@ -33147,6 +33186,74 @@ function translateCanonicalKeyToCodex({ key, value, isStdio, serverName }) {
|
|
|
33147
33186
|
}
|
|
33148
33187
|
}
|
|
33149
33188
|
/**
|
|
33189
|
+
* A header value that is exactly one canonical `${VAR}` reference. Anchored and
|
|
33190
|
+
* without the `g` flag, so `.exec()` carries no `lastIndex` state between
|
|
33191
|
+
* calls. `${VAR:-default}` does not match (the name excludes `:`), because
|
|
33192
|
+
* Codex has no way to express the fallback.
|
|
33193
|
+
*/
|
|
33194
|
+
const WHOLE_ENV_VAR_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
33195
|
+
/**
|
|
33196
|
+
* An `Authorization` value that is exactly `Bearer ${VAR}`. The scheme is
|
|
33197
|
+
* matched case-insensitively, as HTTP authentication schemes are.
|
|
33198
|
+
*/
|
|
33199
|
+
const BEARER_ENV_VAR_REF_PATTERN = /^Bearer \$\{([A-Za-z_][A-Za-z0-9_]*)\}$/i;
|
|
33200
|
+
const AUTHORIZATION_HEADER_NAME = "authorization";
|
|
33201
|
+
/**
|
|
33202
|
+
* Whether a string still contains an unexpanded `${…}` reference. Codex writes
|
|
33203
|
+
* `url` and `http_headers` values as-is and never expands them, so such a value
|
|
33204
|
+
* reaches the server literally.
|
|
33205
|
+
*/
|
|
33206
|
+
function hasUnexpandedEnvVarRef(value) {
|
|
33207
|
+
return value.includes("${");
|
|
33208
|
+
}
|
|
33209
|
+
/**
|
|
33210
|
+
* Rewrite canonical `${VAR}` header references into the keys Codex reads them
|
|
33211
|
+
* from, since Codex sends `http_headers` values verbatim:
|
|
33212
|
+
* - `Authorization: Bearer ${VAR}` → `bearer_token_env_var = "VAR"`
|
|
33213
|
+
* - a header whose whole value is `${VAR}` → `env_http_headers.<name> = "VAR"`
|
|
33214
|
+
*
|
|
33215
|
+
* Runs after every key is translated, so a `bearer_token_env_var` or
|
|
33216
|
+
* `env_http_headers` the source already carries (they pass through under their
|
|
33217
|
+
* own names) is seen regardless of key order. Those explicit values win: a
|
|
33218
|
+
* derived bearer is not written over an existing one, and an existing
|
|
33219
|
+
* `env_http_headers` entry for the same header is kept. Any value the rewrite
|
|
33220
|
+
* cannot express — a partial reference, a `${VAR:-default}` fallback — stays in
|
|
33221
|
+
* `http_headers` verbatim with a warning, as does a `${…}` in `url`, for which
|
|
33222
|
+
* Codex has no environment-sourced counterpart at all.
|
|
33223
|
+
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
33224
|
+
*/
|
|
33225
|
+
function convertEnvVarRefsForCodex({ converted, serverName }) {
|
|
33226
|
+
const url = converted["url"];
|
|
33227
|
+
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.`);
|
|
33228
|
+
const httpHeaders = converted["http_headers"];
|
|
33229
|
+
if (!isHeadersRecord(httpHeaders)) return;
|
|
33230
|
+
const existingEnvHeaders = converted["env_http_headers"];
|
|
33231
|
+
const canWriteEnvHeaders = existingEnvHeaders === void 0 || isHeadersRecord(existingEnvHeaders);
|
|
33232
|
+
const envHeaders = {};
|
|
33233
|
+
const staticHeaders = {};
|
|
33234
|
+
let bearerTokenEnvVar;
|
|
33235
|
+
for (const [name, value] of Object.entries(httpHeaders)) {
|
|
33236
|
+
const bearerMatch = BEARER_ENV_VAR_REF_PATTERN.exec(value);
|
|
33237
|
+
if (bearerMatch?.[1] && name.toLowerCase() === AUTHORIZATION_HEADER_NAME) {
|
|
33238
|
+
if (converted["bearer_token_env_var"] === void 0 && bearerTokenEnvVar === void 0) bearerTokenEnvVar = bearerMatch[1];
|
|
33239
|
+
continue;
|
|
33240
|
+
}
|
|
33241
|
+
const wholeMatch = WHOLE_ENV_VAR_REF_PATTERN.exec(value);
|
|
33242
|
+
if (wholeMatch?.[1] && canWriteEnvHeaders) {
|
|
33243
|
+
envHeaders[name] = wholeMatch[1];
|
|
33244
|
+
continue;
|
|
33245
|
+
}
|
|
33246
|
+
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.`);
|
|
33247
|
+
staticHeaders[name] = value;
|
|
33248
|
+
}
|
|
33249
|
+
converted["http_headers"] = staticHeaders;
|
|
33250
|
+
if (bearerTokenEnvVar !== void 0) converted["bearer_token_env_var"] = bearerTokenEnvVar;
|
|
33251
|
+
if (Object.keys(envHeaders).length > 0) converted["env_http_headers"] = {
|
|
33252
|
+
...envHeaders,
|
|
33253
|
+
...isHeadersRecord(existingEnvHeaders) ? existingEnvHeaders : {}
|
|
33254
|
+
};
|
|
33255
|
+
}
|
|
33256
|
+
/**
|
|
33150
33257
|
* Codex states no transport of its own — it infers one from `command` versus
|
|
33151
33258
|
* `url`, which is why generate drops the canonical `type`. Restate it for a url
|
|
33152
33259
|
* server on the way back, so a config imported from Codex reaches the adapters
|
|
@@ -33233,6 +33340,10 @@ function convertToCodexFormat(mcpServers) {
|
|
|
33233
33340
|
}
|
|
33234
33341
|
} else converted[key] = value;
|
|
33235
33342
|
}
|
|
33343
|
+
convertEnvVarRefsForCodex({
|
|
33344
|
+
converted,
|
|
33345
|
+
serverName: name
|
|
33346
|
+
});
|
|
33236
33347
|
const previousName = originalNames.get(codexName);
|
|
33237
33348
|
if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}"; "${name}" (processed last) overwrites "${previousName}".`);
|
|
33238
33349
|
originalNames.set(codexName, name);
|
|
@@ -45011,14 +45122,14 @@ const COMMANDCODE_TOOL_TO_CATEGORY = {
|
|
|
45011
45122
|
web_fetch: "webfetch",
|
|
45012
45123
|
web_search: "websearch"
|
|
45013
45124
|
};
|
|
45014
|
-
const ACTION_RANK$
|
|
45125
|
+
const ACTION_RANK$2 = {
|
|
45015
45126
|
allow: 0,
|
|
45016
45127
|
ask: 1,
|
|
45017
45128
|
deny: 2
|
|
45018
45129
|
};
|
|
45019
45130
|
/** Whether `action` outranks `existing` (deny > ask > allow). */
|
|
45020
45131
|
function isStricterAction({ action, existing }) {
|
|
45021
|
-
return existing === void 0 || ACTION_RANK$
|
|
45132
|
+
return existing === void 0 || ACTION_RANK$2[action] > ACTION_RANK$2[existing];
|
|
45022
45133
|
}
|
|
45023
45134
|
/**
|
|
45024
45135
|
* Why a padded `Shell( * )` cannot stand in for the bare `Shell` in `allow`:
|
|
@@ -48366,20 +48477,22 @@ function convertFactorydroidToRulesyncPermissions({ allow, deny }) {
|
|
|
48366
48477
|
const GOOSE_GLOBAL_ONLY_MESSAGE = "Goose permissions are global-only; use --global to sync ~/.config/goose/permission.yaml";
|
|
48367
48478
|
const GOOSE_USER_KEY = "user";
|
|
48368
48479
|
const CATCH_ALL_PATTERN$4 = "*";
|
|
48369
|
-
const RULESYNC_TO_GOOSE_TOOL_NAME = {
|
|
48370
|
-
|
|
48371
|
-
|
|
48372
|
-
|
|
48373
|
-
|
|
48374
|
-
const GOOSE_TO_RULESYNC_TOOL_NAME = {
|
|
48375
|
-
developer__shell: "bash",
|
|
48376
|
-
developer__text_editor: "edit"
|
|
48480
|
+
const RULESYNC_TO_GOOSE_TOOL_NAME = { bash: "shell" };
|
|
48481
|
+
const GOOSE_TO_RULESYNC_TOOL_NAMES = {
|
|
48482
|
+
shell: ["bash"],
|
|
48483
|
+
developer__shell: ["bash"],
|
|
48484
|
+
developer__text_editor: ["edit", "write"]
|
|
48377
48485
|
};
|
|
48378
48486
|
const ACTION_TO_GOOSE_LIST = {
|
|
48379
48487
|
allow: "always_allow",
|
|
48380
48488
|
ask: "ask_before",
|
|
48381
48489
|
deny: "never_allow"
|
|
48382
48490
|
};
|
|
48491
|
+
const ACTION_RANK$1 = {
|
|
48492
|
+
allow: 0,
|
|
48493
|
+
ask: 1,
|
|
48494
|
+
deny: 2
|
|
48495
|
+
};
|
|
48383
48496
|
const GOOSE_LIST_TO_ACTION = {
|
|
48384
48497
|
always_allow: "allow",
|
|
48385
48498
|
ask_before: "ask",
|
|
@@ -48404,10 +48517,11 @@ const GOOSE_PERMISSION_LIST_KEYS = [
|
|
|
48404
48517
|
*
|
|
48405
48518
|
* Mapping (rulesync canonical -> Goose):
|
|
48406
48519
|
* - Action: `allow` -> `always_allow`, `ask` -> `ask_before`, `deny` -> `never_allow`.
|
|
48407
|
-
* - Tool name: `bash` -> `
|
|
48408
|
-
*
|
|
48409
|
-
*
|
|
48410
|
-
*
|
|
48520
|
+
* - Tool name: `bash` -> `shell`; any other category (including `edit` and
|
|
48521
|
+
* `write`) passes through verbatim as the Goose tool name (mirrors the
|
|
48522
|
+
* Gemini CLI adapter). The legacy `developer__shell` /
|
|
48523
|
+
* `developer__text_editor` names are still accepted on import, the latter
|
|
48524
|
+
* as both `edit` and `write`.
|
|
48411
48525
|
* - Granularity: Goose lists hold whole tool names, so only a category's
|
|
48412
48526
|
* catch-all `*` pattern is representable. Non-catch-all patterns cannot be
|
|
48413
48527
|
* expressed per-tool and are reported via `logger.warn` and skipped.
|
|
@@ -48523,8 +48637,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
48523
48637
|
never_allow: []
|
|
48524
48638
|
};
|
|
48525
48639
|
const assigned = /* @__PURE__ */ new Map();
|
|
48526
|
-
const
|
|
48527
|
-
for (const [category, rules] of orderedEntries) {
|
|
48640
|
+
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
48528
48641
|
const toolName = RULESYNC_TO_GOOSE_TOOL_NAME[category] ?? category;
|
|
48529
48642
|
for (const [pattern, action] of Object.entries(rules)) {
|
|
48530
48643
|
if (pattern !== CATCH_ALL_PATTERN$4) {
|
|
@@ -48532,12 +48645,15 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
48532
48645
|
continue;
|
|
48533
48646
|
}
|
|
48534
48647
|
const previous = assigned.get(toolName);
|
|
48535
|
-
if (previous
|
|
48536
|
-
|
|
48648
|
+
if (previous === action) continue;
|
|
48649
|
+
if (previous !== void 0) {
|
|
48650
|
+
const stricter = ACTION_RANK$1[action] > ACTION_RANK$1[previous] ? action : previous;
|
|
48651
|
+
logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). Keeping the stricter "${stricter}" (deny > ask > allow).`);
|
|
48652
|
+
if (stricter === previous) continue;
|
|
48537
48653
|
const previousList = lists[ACTION_TO_GOOSE_LIST[previous]];
|
|
48538
48654
|
const index = previousList.indexOf(toolName);
|
|
48539
48655
|
if (index !== -1) previousList.splice(index, 1);
|
|
48540
|
-
}
|
|
48656
|
+
}
|
|
48541
48657
|
lists[ACTION_TO_GOOSE_LIST[action]].push(toolName);
|
|
48542
48658
|
assigned.set(toolName, action);
|
|
48543
48659
|
}
|
|
@@ -48553,8 +48669,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
|
48553
48669
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
48554
48670
|
const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
|
|
48555
48671
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
48556
|
-
for (const toolName of toolNames) {
|
|
48557
|
-
const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
|
|
48672
|
+
for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
|
|
48558
48673
|
permission[category] ??= {};
|
|
48559
48674
|
permission[category][CATCH_ALL_PATTERN$4] = action;
|
|
48560
48675
|
}
|
|
@@ -59228,7 +59343,7 @@ const ClaudecodeSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
59228
59343
|
"disallowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
59229
59344
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
59230
59345
|
effort: zod_mini.z.optional(zod_mini.z.string()),
|
|
59231
|
-
"argument-hint": zod_mini.z.optional(
|
|
59346
|
+
"argument-hint": zod_mini.z.optional(ClaudecodeArgumentHintSchema),
|
|
59232
59347
|
arguments: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
59233
59348
|
context: zod_mini.z.optional(zod_mini.z.string()),
|
|
59234
59349
|
agent: zod_mini.z.optional(zod_mini.z.string()),
|
|
@@ -65384,7 +65499,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
65384
65499
|
meta: {
|
|
65385
65500
|
supportsProject: true,
|
|
65386
65501
|
supportsSimulated: false,
|
|
65387
|
-
supportsGlobal: true
|
|
65502
|
+
supportsGlobal: true,
|
|
65503
|
+
lenientImport: true
|
|
65388
65504
|
}
|
|
65389
65505
|
}],
|
|
65390
65506
|
["claudecode-plugin", {
|
|
@@ -72333,7 +72449,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72333
72449
|
static buildToolRuleParamsAgentsmd({ outputRoot = process.cwd(), rulesyncRule, validate = true, rootPath = {
|
|
72334
72450
|
relativeDirPath: ".",
|
|
72335
72451
|
relativeFilePath: "AGENTS.md"
|
|
72336
|
-
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH } }) {
|
|
72452
|
+
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH }, global = false }) {
|
|
72337
72453
|
const params = this.buildToolRuleParamsDefault({
|
|
72338
72454
|
outputRoot,
|
|
72339
72455
|
rulesyncRule,
|
|
@@ -72342,7 +72458,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72342
72458
|
nonRootPath
|
|
72343
72459
|
});
|
|
72344
72460
|
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
72345
|
-
if (!rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72461
|
+
if (!global && !rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72346
72462
|
params.relativeDirPath = (0, node_path.join)(rulesyncFrontmatter.agentsmd.subprojectPath);
|
|
72347
72463
|
params.relativeFilePath = "AGENTS.md";
|
|
72348
72464
|
}
|
|
@@ -72999,7 +73115,8 @@ var AmpRule = class AmpRule extends ToolRule {
|
|
|
72999
73115
|
rulesyncRule,
|
|
73000
73116
|
validate,
|
|
73001
73117
|
rootPath: paths.root,
|
|
73002
|
-
nonRootPath: paths.nonRoot
|
|
73118
|
+
nonRootPath: paths.nonRoot,
|
|
73119
|
+
global
|
|
73003
73120
|
});
|
|
73004
73121
|
if (!params.root && params.globs !== void 0 && params.globs.length > 0) params.fileContent = stringifyFrontmatter(params.fileContent, { globs: params.globs });
|
|
73005
73122
|
return new AmpRule(params);
|
|
@@ -74294,7 +74411,8 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
74294
74411
|
outputRoot,
|
|
74295
74412
|
rulesyncRule,
|
|
74296
74413
|
validate,
|
|
74297
|
-
rootPath: paths.root
|
|
74414
|
+
rootPath: paths.root,
|
|
74415
|
+
global
|
|
74298
74416
|
}));
|
|
74299
74417
|
}
|
|
74300
74418
|
if (isRoot) return new ClineRule({
|
|
@@ -74714,26 +74832,62 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
74714
74832
|
}
|
|
74715
74833
|
};
|
|
74716
74834
|
//#endregion
|
|
74717
|
-
//#region src/features/rules/
|
|
74718
|
-
|
|
74719
|
-
|
|
74720
|
-
|
|
74721
|
-
|
|
74722
|
-
|
|
74723
|
-
|
|
74724
|
-
|
|
74835
|
+
//#region src/features/rules/nested-agentsmd-rule.ts
|
|
74836
|
+
/**
|
|
74837
|
+
* Shared adapter for the targets that read instructions exactly the way the
|
|
74838
|
+
* AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
|
|
74839
|
+
* project root, nested per-directory `AGENTS.md` files loaded only while working
|
|
74840
|
+
* under that directory, and a single personal root file under a tool-specific
|
|
74841
|
+
* home directory. Such a target has no modular non-root instruction directory,
|
|
74842
|
+
* so every other non-root rule folds into the root file.
|
|
74843
|
+
*
|
|
74844
|
+
* Concrete targets (Codex CLI, Pool, Vibe, DeepSeek Harness) only supply the global
|
|
74845
|
+
* directory and their tool target through {@link getFamily}; the vendor docs
|
|
74846
|
+
* describing each one's discovery walk live on the subclass.
|
|
74847
|
+
*
|
|
74848
|
+
* The nested-file scan mirrors the AGENTS.md standard's nested discovery —
|
|
74849
|
+
* same file name, same exclusions, import-only, project scope — because it
|
|
74850
|
+
* discovers literally the same files.
|
|
74851
|
+
*/
|
|
74852
|
+
var NestedAgentsmdRule = class extends ToolRule {
|
|
74853
|
+
static getFamily() {
|
|
74854
|
+
throw new Error("Please implement this method in the subclass.");
|
|
74725
74855
|
}
|
|
74726
74856
|
static getSettablePaths({ global = false } = {}) {
|
|
74857
|
+
if (global) return { root: {
|
|
74858
|
+
relativeDirPath: this.getFamily().globalDir,
|
|
74859
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74860
|
+
} };
|
|
74727
74861
|
return { root: {
|
|
74728
|
-
relativeDirPath:
|
|
74729
|
-
relativeFilePath:
|
|
74862
|
+
relativeDirPath: ".",
|
|
74863
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74730
74864
|
} };
|
|
74731
74865
|
}
|
|
74732
|
-
static
|
|
74866
|
+
static getNestedFilePatterns() {
|
|
74867
|
+
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
74868
|
+
}
|
|
74869
|
+
/**
|
|
74870
|
+
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
74871
|
+
* (project or global).
|
|
74872
|
+
*/
|
|
74873
|
+
getSubprojectPath() {
|
|
74874
|
+
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
74875
|
+
}
|
|
74876
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
74733
74877
|
const { root } = this.getSettablePaths({ global });
|
|
74734
|
-
|
|
74735
|
-
|
|
74736
|
-
|
|
74878
|
+
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
74879
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
74880
|
+
return new this({
|
|
74881
|
+
outputRoot,
|
|
74882
|
+
relativeDirPath: overrideDirPath,
|
|
74883
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
74884
|
+
fileContent,
|
|
74885
|
+
validate,
|
|
74886
|
+
root: false
|
|
74887
|
+
});
|
|
74888
|
+
}
|
|
74889
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
74890
|
+
return new this({
|
|
74737
74891
|
outputRoot,
|
|
74738
74892
|
relativeDirPath: root.relativeDirPath,
|
|
74739
74893
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74744,8 +74898,18 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74744
74898
|
}
|
|
74745
74899
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
74746
74900
|
const { root } = this.getSettablePaths({ global });
|
|
74747
|
-
const
|
|
74748
|
-
|
|
74901
|
+
const frontmatter = rulesyncRule.getFrontmatter();
|
|
74902
|
+
const isRoot = frontmatter.root ?? false;
|
|
74903
|
+
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
74904
|
+
if (!global && !isRoot && subprojectPath) return new this({
|
|
74905
|
+
outputRoot,
|
|
74906
|
+
relativeDirPath: (0, node_path.join)(subprojectPath),
|
|
74907
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
74908
|
+
fileContent: rulesyncRule.getBody(),
|
|
74909
|
+
validate,
|
|
74910
|
+
root: false
|
|
74911
|
+
});
|
|
74912
|
+
return new this({
|
|
74749
74913
|
outputRoot,
|
|
74750
74914
|
relativeDirPath: root.relativeDirPath,
|
|
74751
74915
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74755,7 +74919,9 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74755
74919
|
});
|
|
74756
74920
|
}
|
|
74757
74921
|
toRulesyncRule() {
|
|
74758
|
-
|
|
74922
|
+
const subprojectPath = this.getSubprojectPath();
|
|
74923
|
+
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
74924
|
+
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
74759
74925
|
}
|
|
74760
74926
|
validate() {
|
|
74761
74927
|
return {
|
|
@@ -74764,23 +74930,54 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74764
74930
|
};
|
|
74765
74931
|
}
|
|
74766
74932
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
74767
|
-
|
|
74933
|
+
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
74934
|
+
return new this({
|
|
74768
74935
|
outputRoot,
|
|
74769
74936
|
relativeDirPath,
|
|
74770
74937
|
relativeFilePath,
|
|
74771
74938
|
fileContent: "",
|
|
74772
74939
|
validate: false,
|
|
74773
|
-
root:
|
|
74940
|
+
root: isRoot
|
|
74774
74941
|
});
|
|
74775
74942
|
}
|
|
74776
74943
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
74777
74944
|
return this.isTargetedByRulesyncRuleDefault({
|
|
74778
74945
|
rulesyncRule,
|
|
74779
|
-
toolTarget:
|
|
74946
|
+
toolTarget: this.getFamily().toolTarget
|
|
74780
74947
|
});
|
|
74781
74948
|
}
|
|
74782
74949
|
};
|
|
74783
74950
|
//#endregion
|
|
74951
|
+
//#region src/features/rules/codexcli-rule.ts
|
|
74952
|
+
/**
|
|
74953
|
+
* Rule generator for OpenAI Codex CLI.
|
|
74954
|
+
*
|
|
74955
|
+
* Codex CLI loads instructions only from the `AGENTS.md` family: the global
|
|
74956
|
+
* `~/.codex/AGENTS.md` (or `AGENTS.override.md`), then one file per directory
|
|
74957
|
+
* level from the project root down to the current working directory, joined
|
|
74958
|
+
* root-first so deeper files take precedence. Nested per-directory files are
|
|
74959
|
+
* therefore a real scoping surface, and Codex's docs recommend splitting large
|
|
74960
|
+
* instruction sets across nested directories. It does NOT scan a
|
|
74961
|
+
* `.codex/memories/` directory for instruction files — that directory belongs
|
|
74962
|
+
* to Codex's separate SQLite-backed auto-memory system.
|
|
74963
|
+
*
|
|
74964
|
+
* A non-root rule carrying `agentsmd.subprojectPath` is emitted as a nested
|
|
74965
|
+
* `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
|
|
74966
|
+
* folds into the root `AGENTS.md`, since Codex has no modular rules directory.
|
|
74967
|
+
* The project files on the root-to-cwd chain share one `project_doc_max_bytes`
|
|
74968
|
+
* budget (32 KiB by default), which the RulesProcessor warns about.
|
|
74969
|
+
*
|
|
74970
|
+
* @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
|
|
74971
|
+
*/
|
|
74972
|
+
var CodexcliRule = class extends NestedAgentsmdRule {
|
|
74973
|
+
static getFamily() {
|
|
74974
|
+
return {
|
|
74975
|
+
globalDir: CODEXCLI_DIR,
|
|
74976
|
+
toolTarget: "codexcli"
|
|
74977
|
+
};
|
|
74978
|
+
}
|
|
74979
|
+
};
|
|
74980
|
+
//#endregion
|
|
74784
74981
|
//#region src/features/rules/commandcode-rule.ts
|
|
74785
74982
|
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
74786
74983
|
constructor({ fileContent, root, ...rest }) {
|
|
@@ -76085,122 +76282,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
76085
76282
|
}
|
|
76086
76283
|
};
|
|
76087
76284
|
//#endregion
|
|
76088
|
-
//#region src/features/rules/nested-agentsmd-rule.ts
|
|
76089
|
-
/**
|
|
76090
|
-
* Shared adapter for the targets that read instructions exactly the way the
|
|
76091
|
-
* AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
|
|
76092
|
-
* project root, nested per-directory `AGENTS.md` files loaded only while working
|
|
76093
|
-
* under that directory, and a single personal root file under a tool-specific
|
|
76094
|
-
* home directory. Such a target has no modular non-root instruction directory,
|
|
76095
|
-
* so every other non-root rule folds into the root file.
|
|
76096
|
-
*
|
|
76097
|
-
* Concrete targets (Pool, Vibe, DeepSeek Harness) only supply the global
|
|
76098
|
-
* directory and their tool target through {@link getFamily}; the vendor docs
|
|
76099
|
-
* describing each one's discovery walk live on the subclass.
|
|
76100
|
-
*
|
|
76101
|
-
* The nested-file scan mirrors the AGENTS.md standard's nested discovery —
|
|
76102
|
-
* same file name, same exclusions, import-only, project scope — because it
|
|
76103
|
-
* discovers literally the same files.
|
|
76104
|
-
*/
|
|
76105
|
-
var NestedAgentsmdRule = class extends ToolRule {
|
|
76106
|
-
static getFamily() {
|
|
76107
|
-
throw new Error("Please implement this method in the subclass.");
|
|
76108
|
-
}
|
|
76109
|
-
static getSettablePaths({ global = false } = {}) {
|
|
76110
|
-
if (global) return { root: {
|
|
76111
|
-
relativeDirPath: this.getFamily().globalDir,
|
|
76112
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76113
|
-
} };
|
|
76114
|
-
return { root: {
|
|
76115
|
-
relativeDirPath: ".",
|
|
76116
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76117
|
-
} };
|
|
76118
|
-
}
|
|
76119
|
-
static getNestedFilePatterns() {
|
|
76120
|
-
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76121
|
-
}
|
|
76122
|
-
/**
|
|
76123
|
-
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
76124
|
-
* (project or global).
|
|
76125
|
-
*/
|
|
76126
|
-
getSubprojectPath() {
|
|
76127
|
-
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76128
|
-
}
|
|
76129
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
76130
|
-
const { root } = this.getSettablePaths({ global });
|
|
76131
|
-
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
76132
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
76133
|
-
return new this({
|
|
76134
|
-
outputRoot,
|
|
76135
|
-
relativeDirPath: overrideDirPath,
|
|
76136
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76137
|
-
fileContent,
|
|
76138
|
-
validate,
|
|
76139
|
-
root: false
|
|
76140
|
-
});
|
|
76141
|
-
}
|
|
76142
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
76143
|
-
return new this({
|
|
76144
|
-
outputRoot,
|
|
76145
|
-
relativeDirPath: root.relativeDirPath,
|
|
76146
|
-
relativeFilePath: root.relativeFilePath,
|
|
76147
|
-
fileContent,
|
|
76148
|
-
validate,
|
|
76149
|
-
root: true
|
|
76150
|
-
});
|
|
76151
|
-
}
|
|
76152
|
-
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
76153
|
-
const { root } = this.getSettablePaths({ global });
|
|
76154
|
-
const frontmatter = rulesyncRule.getFrontmatter();
|
|
76155
|
-
const isRoot = frontmatter.root ?? false;
|
|
76156
|
-
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
76157
|
-
if (!global && !isRoot && subprojectPath) return new this({
|
|
76158
|
-
outputRoot,
|
|
76159
|
-
relativeDirPath: (0, node_path.join)(subprojectPath),
|
|
76160
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76161
|
-
fileContent: rulesyncRule.getBody(),
|
|
76162
|
-
validate,
|
|
76163
|
-
root: false
|
|
76164
|
-
});
|
|
76165
|
-
return new this({
|
|
76166
|
-
outputRoot,
|
|
76167
|
-
relativeDirPath: root.relativeDirPath,
|
|
76168
|
-
relativeFilePath: root.relativeFilePath,
|
|
76169
|
-
fileContent: rulesyncRule.getBody(),
|
|
76170
|
-
validate,
|
|
76171
|
-
root: isRoot
|
|
76172
|
-
});
|
|
76173
|
-
}
|
|
76174
|
-
toRulesyncRule() {
|
|
76175
|
-
const subprojectPath = this.getSubprojectPath();
|
|
76176
|
-
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
76177
|
-
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
76178
|
-
}
|
|
76179
|
-
validate() {
|
|
76180
|
-
return {
|
|
76181
|
-
success: true,
|
|
76182
|
-
error: null
|
|
76183
|
-
};
|
|
76184
|
-
}
|
|
76185
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
76186
|
-
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
76187
|
-
return new this({
|
|
76188
|
-
outputRoot,
|
|
76189
|
-
relativeDirPath,
|
|
76190
|
-
relativeFilePath,
|
|
76191
|
-
fileContent: "",
|
|
76192
|
-
validate: false,
|
|
76193
|
-
root: isRoot
|
|
76194
|
-
});
|
|
76195
|
-
}
|
|
76196
|
-
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
76197
|
-
return this.isTargetedByRulesyncRuleDefault({
|
|
76198
|
-
rulesyncRule,
|
|
76199
|
-
toolTarget: this.getFamily().toolTarget
|
|
76200
|
-
});
|
|
76201
|
-
}
|
|
76202
|
-
};
|
|
76203
|
-
//#endregion
|
|
76204
76285
|
//#region src/features/rules/dsh-rule.ts
|
|
76205
76286
|
/**
|
|
76206
76287
|
* DeepSeek Harness (`dsh`) rules.
|
|
@@ -76412,7 +76493,8 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
76412
76493
|
rulesyncRule,
|
|
76413
76494
|
validate,
|
|
76414
76495
|
rootPath: paths.root,
|
|
76415
|
-
nonRootPath: paths.nonRoot
|
|
76496
|
+
nonRootPath: paths.nonRoot,
|
|
76497
|
+
global
|
|
76416
76498
|
}));
|
|
76417
76499
|
}
|
|
76418
76500
|
toRulesyncRule() {
|
|
@@ -76879,7 +76961,8 @@ var KiloRule = class KiloRule extends ToolRule {
|
|
|
76879
76961
|
rulesyncRule,
|
|
76880
76962
|
validate,
|
|
76881
76963
|
rootPath: paths.root,
|
|
76882
|
-
nonRootPath: paths.nonRoot
|
|
76964
|
+
nonRootPath: paths.nonRoot,
|
|
76965
|
+
global
|
|
76883
76966
|
}));
|
|
76884
76967
|
}
|
|
76885
76968
|
toRulesyncRule() {
|
|
@@ -77383,7 +77466,8 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
|
|
|
77383
77466
|
rulesyncRule,
|
|
77384
77467
|
validate,
|
|
77385
77468
|
rootPath: paths.root,
|
|
77386
|
-
nonRootPath: paths.nonRoot
|
|
77469
|
+
nonRootPath: paths.nonRoot,
|
|
77470
|
+
global
|
|
77387
77471
|
}));
|
|
77388
77472
|
}
|
|
77389
77473
|
toRulesyncRule() {
|
|
@@ -78852,6 +78936,14 @@ var ZoocodeRule = class extends RooRule {
|
|
|
78852
78936
|
//#endregion
|
|
78853
78937
|
//#region src/features/rules/rules-processor.ts
|
|
78854
78938
|
const RulesProcessorToolTargetSchema = zod_mini.z.enum(rulesProcessorToolTargetTuple);
|
|
78939
|
+
/**
|
|
78940
|
+
* Normalizes a relative directory path for comparison: POSIX separators, no
|
|
78941
|
+
* `./` segments and no trailing slash, with the current directory as `"."`
|
|
78942
|
+
* (so `"./"`, `"a/"` and `"a"` compare the way `join()` groups them).
|
|
78943
|
+
*/
|
|
78944
|
+
const normalizeRelativeDir = (dir) => node_path.posix.normalize(toPosixPath(dir)).replace(/\/+$/, "") || ".";
|
|
78945
|
+
/** A tool rule's output path relative to its output root, for comparison. */
|
|
78946
|
+
const toolOutputPath = (toolRule) => node_path.posix.join(normalizeRelativeDir(toolRule.getRelativeDirPath()), toolRule.getRelativeFilePath());
|
|
78855
78947
|
const formatRulePaths = (rules) => rules.map((r) => (0, node_path.join)(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
|
|
78856
78948
|
const RulesFeatureOptionsSchema = zod_mini.z.looseObject({
|
|
78857
78949
|
ruleDiscoveryMode: zod_mini.z.optional(zod_mini.z.enum(["none", "explicit"])),
|
|
@@ -79020,7 +79112,11 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
79020
79112
|
extension: "md",
|
|
79021
79113
|
supportsGlobal: true,
|
|
79022
79114
|
ruleDiscoveryMode: "auto",
|
|
79023
|
-
collisionPolicy: "fold"
|
|
79115
|
+
collisionPolicy: "fold",
|
|
79116
|
+
projectInstructionBudget: {
|
|
79117
|
+
bytes: CODEXCLI_PROJECT_DOC_MAX_BYTES,
|
|
79118
|
+
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)."
|
|
79119
|
+
}
|
|
79024
79120
|
}
|
|
79025
79121
|
}],
|
|
79026
79122
|
["commandcode", {
|
|
@@ -79478,9 +79574,61 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
79478
79574
|
toolRules,
|
|
79479
79575
|
factory
|
|
79480
79576
|
});
|
|
79577
|
+
this.warnForInstructionBudget({
|
|
79578
|
+
toolRules,
|
|
79579
|
+
meta
|
|
79580
|
+
});
|
|
79481
79581
|
return outputFiles;
|
|
79482
79582
|
}
|
|
79483
79583
|
/**
|
|
79584
|
+
* Warn when a generated project instruction chain exceeds the tool's byte
|
|
79585
|
+
* budget (`meta.projectInstructionBudget`).
|
|
79586
|
+
*
|
|
79587
|
+
* Codex CLI concatenates the project `AGENTS.md` files from the project root
|
|
79588
|
+
* down to the cwd and stops once their combined size reaches
|
|
79589
|
+
* `project_doc_max_bytes`, truncating the file that crosses it — silently, so
|
|
79590
|
+
* without this warning the rules past the limit simply never reach the model.
|
|
79591
|
+
* Each generated file is measured together with the generated files in its
|
|
79592
|
+
* ancestor directories, since that is the chain Codex loads while working
|
|
79593
|
+
* there. When the root file alone is over the budget every chain is, so only
|
|
79594
|
+
* the root is reported; likewise each over-budget nested chain is reported
|
|
79595
|
+
* once, at its shallowest directory. With several output roots the warning
|
|
79596
|
+
* names the root it refers to. An output root below the git root is measured
|
|
79597
|
+
* without the parent directories' files Codex would also load, so the
|
|
79598
|
+
* estimate can come out low there. The budget checked is the tool's default: a value
|
|
79599
|
+
* raised by hand in the tool's own config is not read. Global scope is not
|
|
79600
|
+
* checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
|
|
79601
|
+
* project budget.
|
|
79602
|
+
*/
|
|
79603
|
+
warnForInstructionBudget({ toolRules, meta }) {
|
|
79604
|
+
const budget = meta.projectInstructionBudget;
|
|
79605
|
+
if (budget === void 0 || this.global) return;
|
|
79606
|
+
const files = toolRules.map((rule) => ({
|
|
79607
|
+
dir: normalizeRelativeDir(rule.getRelativeDirPath()),
|
|
79608
|
+
path: toPosixPath((0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
|
|
79609
|
+
bytes: Buffer.byteLength(addTrailingNewline(rule.getFileContent()), "utf8")
|
|
79610
|
+
}));
|
|
79611
|
+
const isAncestorOrSelf = (ancestor, dir) => ancestor === "." || ancestor === dir || dir.startsWith(`${ancestor}/`);
|
|
79612
|
+
const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
|
|
79613
|
+
const rootFiles = files.filter(({ dir }) => dir === ".");
|
|
79614
|
+
const rootBytes = rootFiles.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
79615
|
+
if (rootBytes > budget.bytes) {
|
|
79616
|
+
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}`);
|
|
79617
|
+
return;
|
|
79618
|
+
}
|
|
79619
|
+
const reportedDirs = [];
|
|
79620
|
+
const nestedFiles = files.filter(({ dir }) => dir !== ".").toSorted((a, b) => a.dir.split("/").length - b.dir.split("/").length);
|
|
79621
|
+
for (const file of nestedFiles) {
|
|
79622
|
+
if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
|
|
79623
|
+
const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
|
|
79624
|
+
const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
79625
|
+
if (chainBytes > budget.bytes) {
|
|
79626
|
+
reportedDirs.push(file.dir);
|
|
79627
|
+
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}`);
|
|
79628
|
+
}
|
|
79629
|
+
}
|
|
79630
|
+
}
|
|
79631
|
+
/**
|
|
79484
79632
|
* Handle localRoot rules (only in non-global mode and when enabled). Mutates
|
|
79485
79633
|
* `toolRules` in place.
|
|
79486
79634
|
*/
|
|
@@ -80152,14 +80300,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80152
80300
|
* decides for all of them: its `pi.contextFile` is copied onto the non-root
|
|
80153
80301
|
* rules, and the flag set only on a non-root rule is dropped with a warning.
|
|
80154
80302
|
*/
|
|
80155
|
-
alignPiContextFile(rules) {
|
|
80303
|
+
alignPiContextFile(rules, { warn = true } = {}) {
|
|
80156
80304
|
if (this.toolTarget !== "pi") return rules;
|
|
80157
80305
|
const factory = this.getFactory(this.toolTarget);
|
|
80158
80306
|
const targeted = rules.filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
|
|
80159
80307
|
const rootContextFile = targeted.some((rule) => rule.getFrontmatter().root === true && rule.getFrontmatter().pi?.contextFile === "override") ? "override" : void 0;
|
|
80160
80308
|
const mismatched = targeted.filter((rule) => rule.getFrontmatter().pi?.contextFile !== rootContextFile);
|
|
80161
80309
|
if (mismatched.length === 0) return rules;
|
|
80162
|
-
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)}`);
|
|
80310
|
+
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)}`);
|
|
80163
80311
|
const mismatchedSet = new Set(mismatched);
|
|
80164
80312
|
return rules.map((rule) => {
|
|
80165
80313
|
if (!mismatchedSet.has(rule)) return rule;
|
|
@@ -80213,14 +80361,72 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80213
80361
|
* duplication check the same way `loadRulesyncFiles`'s global-mode branch
|
|
80214
80362
|
* excludes it from `nonRootRules`: `generate` ignores `localRoot` entirely
|
|
80215
80363
|
* in global mode, so such a rule is never actually folded into the global
|
|
80216
|
-
* root output and warning about it here would be inaccurate.
|
|
80364
|
+
* root output and warning about it here would be inaccurate. The same goes
|
|
80365
|
+
* for a tool that writes `localRoot` to a dedicated local file.
|
|
80366
|
+
*
|
|
80367
|
+
* Non-root rules written to a file other than the root (nested
|
|
80368
|
+
* per-directory files, Pi's `APPEND_SYSTEM.md`) get a separate warning when
|
|
80369
|
+
* that file already exists and would import under a different name.
|
|
80217
80370
|
*/
|
|
80218
80371
|
async warnForFoldImportDuplicationRisk() {
|
|
80219
80372
|
const factory = this.getFactory(this.toolTarget);
|
|
80220
80373
|
if (factory.meta.collisionPolicy !== "fold") return;
|
|
80221
|
-
const
|
|
80222
|
-
|
|
80223
|
-
|
|
80374
|
+
const targetedRules = this.alignPiContextFile(await this.loadMergedRulesyncRules(), { warn: false }).filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
|
|
80375
|
+
let includeLocalRoot = !this.global;
|
|
80376
|
+
try {
|
|
80377
|
+
includeLocalRoot &&= resolveIncludeLocalRoot(this.featureOptions);
|
|
80378
|
+
} catch {}
|
|
80379
|
+
const targetedNonRootRules = targetedRules.filter((rule) => !rule.getFrontmatter().root && !(rule.getFrontmatter().localRoot && (!includeLocalRoot || factory.meta.localRootMode === "separate-local-file")));
|
|
80380
|
+
const rootPaths = /* @__PURE__ */ new Set();
|
|
80381
|
+
const { root } = factory.class.getSettablePaths({ global: this.global });
|
|
80382
|
+
if (root) rootPaths.add(node_path.posix.join(normalizeRelativeDir(root.relativeDirPath), root.relativeFilePath));
|
|
80383
|
+
for (const rulesyncRule of targetedRules.filter((rule) => rule.getFrontmatter().root)) {
|
|
80384
|
+
const toolRule = this.toToolRuleForImportCheck({
|
|
80385
|
+
factory,
|
|
80386
|
+
rulesyncRule
|
|
80387
|
+
});
|
|
80388
|
+
rootPaths.add(toolOutputPath(toolRule));
|
|
80389
|
+
}
|
|
80390
|
+
const separateRules = [];
|
|
80391
|
+
const foldedRules = [];
|
|
80392
|
+
let localRootCounted = false;
|
|
80393
|
+
for (const rulesyncRule of targetedNonRootRules) {
|
|
80394
|
+
if (rulesyncRule.getFrontmatter().localRoot) {
|
|
80395
|
+
if (!localRootCounted) {
|
|
80396
|
+
foldedRules.push(rulesyncRule);
|
|
80397
|
+
localRootCounted = true;
|
|
80398
|
+
}
|
|
80399
|
+
continue;
|
|
80400
|
+
}
|
|
80401
|
+
const toolRule = this.toToolRuleForImportCheck({
|
|
80402
|
+
factory,
|
|
80403
|
+
rulesyncRule
|
|
80404
|
+
});
|
|
80405
|
+
if (rootPaths.size === 0 || rootPaths.has(toolOutputPath(toolRule))) foldedRules.push(rulesyncRule);
|
|
80406
|
+
else separateRules.push({
|
|
80407
|
+
toolRule,
|
|
80408
|
+
rulesyncRule
|
|
80409
|
+
});
|
|
80410
|
+
}
|
|
80411
|
+
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.`);
|
|
80412
|
+
const duplicated = [];
|
|
80413
|
+
for (const conversion of separateRules) {
|
|
80414
|
+
const { toolRule, rulesyncRule } = conversion;
|
|
80415
|
+
if (toolRule.toRulesyncRule().getRelativeFilePath().toLowerCase() === rulesyncRule.getRelativeFilePath().toLowerCase()) continue;
|
|
80416
|
+
if (await fileExists(toolRule.getFilePath())) duplicated.push(conversion);
|
|
80417
|
+
}
|
|
80418
|
+
if (duplicated.length > 0) {
|
|
80419
|
+
const fileNames = [...new Set(duplicated.map(({ toolRule }) => toolRule.getRelativeFilePath()))].join(", ");
|
|
80420
|
+
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.`);
|
|
80421
|
+
}
|
|
80422
|
+
}
|
|
80423
|
+
toToolRuleForImportCheck({ factory, rulesyncRule }) {
|
|
80424
|
+
return factory.class.fromRulesyncRule({
|
|
80425
|
+
outputRoot: this.outputRoot,
|
|
80426
|
+
rulesyncRule,
|
|
80427
|
+
validate: false,
|
|
80428
|
+
global: this.global
|
|
80429
|
+
});
|
|
80224
80430
|
}
|
|
80225
80431
|
/**
|
|
80226
80432
|
* Implementation of abstract method from FeatureProcessor
|
|
@@ -80514,6 +80720,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80514
80720
|
function isPackagingToolTarget(toolTarget) {
|
|
80515
80721
|
return PACKAGING_TOOL_TARGETS.includes(toolTarget);
|
|
80516
80722
|
}
|
|
80723
|
+
/**
|
|
80724
|
+
* Packaging targets write into a plugin directory and support project scope
|
|
80725
|
+
* only. In global mode their output root would be the home directory, so
|
|
80726
|
+
* callers reject or skip them up front instead of checking all of $HOME.
|
|
80727
|
+
*/
|
|
80728
|
+
function formatPackagingTargetGlobalMessage(toolTarget) {
|
|
80729
|
+
return `Target '${toolTarget}' is a plugin packaging target and supports only project scope. Re-run without '--global'.`;
|
|
80730
|
+
}
|
|
80517
80731
|
async function assertPluginRootSafe(params) {
|
|
80518
80732
|
if (!isPackagingToolTarget(params.toolTarget)) return;
|
|
80519
80733
|
await assertDirectoryIfExists(params.outputRoot);
|
|
@@ -81433,14 +81647,17 @@ function warnUnsupportedTargets(params) {
|
|
|
81433
81647
|
} catch {
|
|
81434
81648
|
oppositeScopeTargets = [];
|
|
81435
81649
|
}
|
|
81436
|
-
for (const target of config.getTargets())
|
|
81437
|
-
|
|
81438
|
-
if (
|
|
81439
|
-
|
|
81440
|
-
|
|
81441
|
-
|
|
81442
|
-
|
|
81443
|
-
|
|
81650
|
+
for (const target of config.getTargets()) {
|
|
81651
|
+
if (config.getGlobal() && isPackagingToolTarget(target)) continue;
|
|
81652
|
+
if (!supportedTargets.includes(target) && config.getFeatures(target).includes(featureName)) {
|
|
81653
|
+
const simulateOption = SIMULATE_OPTION_MAP[featureName];
|
|
81654
|
+
if (simulateOption && simulatedTargets.includes(target)) logger.warn(`Target '${target}' only supports simulated '${featureName}'. Use '${simulateOption}' to enable it. Skipping.`);
|
|
81655
|
+
else if (oppositeScopeTargets.includes(target)) {
|
|
81656
|
+
const supportedScope = config.getGlobal() ? "project" : "global";
|
|
81657
|
+
const retry = config.getGlobal() ? "without '--global'" : "with '--global'";
|
|
81658
|
+
logger.warn(`Target '${target}' supports the feature '${featureName}' only in ${supportedScope} scope. Re-run ${retry}. Skipping.`);
|
|
81659
|
+
} else logger.warn(`Target '${target}' does not support the feature '${featureName}'. Skipping.`);
|
|
81660
|
+
}
|
|
81444
81661
|
}
|
|
81445
81662
|
}
|
|
81446
81663
|
/**
|
|
@@ -81693,10 +81910,16 @@ async function generate(params) {
|
|
|
81693
81910
|
const { config, logger } = params;
|
|
81694
81911
|
resetRootShadowingWarnings({ logger });
|
|
81695
81912
|
resetRunWarningState();
|
|
81696
|
-
for (const toolTarget of config.getTargets())
|
|
81697
|
-
toolTarget
|
|
81698
|
-
|
|
81699
|
-
|
|
81913
|
+
for (const toolTarget of config.getTargets()) {
|
|
81914
|
+
if (config.getGlobal() && isPackagingToolTarget(toolTarget)) {
|
|
81915
|
+
logger.warn(`${formatPackagingTargetGlobalMessage(toolTarget)} Skipping.`);
|
|
81916
|
+
continue;
|
|
81917
|
+
}
|
|
81918
|
+
for (const outputRoot of config.getOutputRoots(toolTarget)) await assertPluginRootSafe({
|
|
81919
|
+
toolTarget,
|
|
81920
|
+
outputRoot
|
|
81921
|
+
});
|
|
81922
|
+
}
|
|
81700
81923
|
await warnSkillSubagentNameCollisions({
|
|
81701
81924
|
config,
|
|
81702
81925
|
logger
|
|
@@ -82313,6 +82536,7 @@ function getToolOutputRoot({ config, tool }) {
|
|
|
82313
82536
|
async function importFromTool(params) {
|
|
82314
82537
|
const { config, tool, logger } = params;
|
|
82315
82538
|
resetRunWarningState();
|
|
82539
|
+
if (config.getGlobal() && isPackagingToolTarget(tool)) throw new Error(formatPackagingTargetGlobalMessage(tool));
|
|
82316
82540
|
await assertPluginRootSafe({
|
|
82317
82541
|
toolTarget: tool,
|
|
82318
82542
|
outputRoot: getToolOutputRoot({
|
|
@@ -82373,7 +82597,7 @@ async function importRulesCore(params) {
|
|
|
82373
82597
|
if (!config.getFeatures(tool).includes("rules")) return 0;
|
|
82374
82598
|
const global = config.getGlobal();
|
|
82375
82599
|
if (!RulesProcessor.getToolTargets({ global }).includes(tool)) return 0;
|
|
82376
|
-
const
|
|
82600
|
+
const baseParams = {
|
|
82377
82601
|
outputRoot: getToolOutputRoot({
|
|
82378
82602
|
config,
|
|
82379
82603
|
tool
|
|
@@ -82381,13 +82605,23 @@ async function importRulesCore(params) {
|
|
|
82381
82605
|
toolTarget: tool,
|
|
82382
82606
|
global,
|
|
82383
82607
|
logger
|
|
82384
|
-
}
|
|
82608
|
+
};
|
|
82609
|
+
const rulesProcessor = new RulesProcessor(baseParams);
|
|
82385
82610
|
const toolFiles = await rulesProcessor.loadToolFiles();
|
|
82386
82611
|
if (toolFiles.length === 0) {
|
|
82387
82612
|
logger.warn(`No rule files found for ${tool}. Skipping import.`);
|
|
82388
82613
|
return 0;
|
|
82389
82614
|
}
|
|
82390
|
-
|
|
82615
|
+
try {
|
|
82616
|
+
await new RulesProcessor({
|
|
82617
|
+
...baseParams,
|
|
82618
|
+
inputRoots: config.getInputRoots(),
|
|
82619
|
+
deriveSubprojectPathFromGlobs: config.getDeriveSubprojectPathFromGlobs(),
|
|
82620
|
+
featureOptions: config.getFeatureOptions(tool, "rules")
|
|
82621
|
+
}).warnForFoldImportDuplicationRisk();
|
|
82622
|
+
} catch (error) {
|
|
82623
|
+
logger.warn(`Skipped the ${tool} import duplication check: ${formatError(error)}`);
|
|
82624
|
+
}
|
|
82391
82625
|
const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
|
|
82392
82626
|
const { count: writtenCount } = await rulesProcessor.writeAiFiles(rulesyncFiles);
|
|
82393
82627
|
if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} rule files`);
|