rulesync 21.0.0 → 23.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/README.md +1 -1
- package/dist/cli/index.cjs +7 -7
- package/dist/cli/index.js +7 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-CSKYRGa5.js → import-CGIOcUpR.js} +836 -307
- package/dist/import-CGIOcUpR.js.map +1 -0
- package/dist/{import-CGT8v9bO.cjs → import-CeSvBhiv.cjs} +835 -306
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-CSKYRGa5.js.map +0 -1
|
@@ -2728,6 +2728,7 @@ var Config = class Config {
|
|
|
2728
2728
|
*/
|
|
2729
2729
|
objectFormTargetKeys;
|
|
2730
2730
|
configFileTargets;
|
|
2731
|
+
configFileSelection;
|
|
2731
2732
|
verbose;
|
|
2732
2733
|
delete;
|
|
2733
2734
|
global;
|
|
@@ -2762,7 +2763,7 @@ var Config = class Config {
|
|
|
2762
2763
|
inputRoots;
|
|
2763
2764
|
configFilePath;
|
|
2764
2765
|
sources;
|
|
2765
|
-
constructor({ outputRoots, targets, features, verbose, delete: isDelete, global, silent, simulateCommands, simulateSubagents, simulateSkills, preserveUnownedHooks, deriveSubprojectPathFromGlobs, flattenedCommandNaming, language, gitignoreTargetsOnly, gitignoreDestination, dryRun, check, inputRoot, inputRoots, configFilePath, sources, configFileTargets }) {
|
|
2766
|
+
constructor({ outputRoots, targets, features, verbose, delete: isDelete, global, silent, simulateCommands, simulateSubagents, simulateSkills, preserveUnownedHooks, deriveSubprojectPathFromGlobs, flattenedCommandNaming, language, gitignoreTargetsOnly, gitignoreDestination, dryRun, check, inputRoot, inputRoots, configFilePath, sources, configFileTargets, configFileSelection }) {
|
|
2766
2767
|
assertTargetsFeaturesExclusive({
|
|
2767
2768
|
targets,
|
|
2768
2769
|
features
|
|
@@ -2787,6 +2788,7 @@ var Config = class Config {
|
|
|
2787
2788
|
this.features = resolvedFeatures;
|
|
2788
2789
|
this.objectFormTargetKeys = isRulesyncConfigTargetsObject(resolvedTargets) ? Config.filterValidToolTargets(Object.keys(resolvedTargets)) : void 0;
|
|
2789
2790
|
this.configFileTargets = configFileTargets;
|
|
2791
|
+
this.configFileSelection = configFileSelection;
|
|
2790
2792
|
this.verbose = verbose;
|
|
2791
2793
|
this.delete = isDelete;
|
|
2792
2794
|
this.global = global ?? false;
|
|
@@ -2877,17 +2879,54 @@ var Config = class Config {
|
|
|
2877
2879
|
getConfigFileTargets() {
|
|
2878
2880
|
return this.configFileTargets ?? this.getTargets();
|
|
2879
2881
|
}
|
|
2882
|
+
/**
|
|
2883
|
+
* The features `target` has for a run over the configuration file's
|
|
2884
|
+
* targets, i.e. what `rulesync generate` without `--targets` would produce
|
|
2885
|
+
* for it (a CLI `--features` still applies to array-form targets). Falls
|
|
2886
|
+
* back to {@link getFeatures} when no configuration file declared `targets`.
|
|
2887
|
+
*/
|
|
2888
|
+
getConfigFileFeatures(target) {
|
|
2889
|
+
const selection = this.configFileSelection;
|
|
2890
|
+
if (selection === void 0) return this.getFeatures(target);
|
|
2891
|
+
return Config.resolveTargetFeatures({
|
|
2892
|
+
targets: selection.targets,
|
|
2893
|
+
features: selection.features ?? [],
|
|
2894
|
+
target
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* The options `target` sets for `feature` in the configuration file, for
|
|
2899
|
+
* the same run as {@link getConfigFileFeatures}. Falls back to
|
|
2900
|
+
* {@link getFeatureOptions} when no configuration file declared `targets`.
|
|
2901
|
+
*/
|
|
2902
|
+
getConfigFileFeatureOptions(target, feature) {
|
|
2903
|
+
const selection = this.configFileSelection;
|
|
2904
|
+
if (selection === void 0) return this.getFeatureOptions(target, feature);
|
|
2905
|
+
return Config.resolveFeatureOptions({
|
|
2906
|
+
targets: selection.targets,
|
|
2907
|
+
target,
|
|
2908
|
+
feature
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2880
2911
|
getFeatures(target) {
|
|
2881
|
-
if (
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2912
|
+
if (target) return Config.resolveTargetFeatures({
|
|
2913
|
+
targets: this.targets,
|
|
2914
|
+
features: this.features,
|
|
2915
|
+
target
|
|
2916
|
+
});
|
|
2917
|
+
if (isRulesyncConfigTargetsObject(this.targets)) return Config.collectAllFeatures(Object.values(this.targets));
|
|
2918
|
+
return Config.normalizeFeatureList(this.features);
|
|
2919
|
+
}
|
|
2920
|
+
static resolveTargetFeatures({ targets, features, target }) {
|
|
2921
|
+
if (isRulesyncConfigTargetsObject(targets)) {
|
|
2922
|
+
const value = targets[target];
|
|
2923
|
+
return value ? Config.normalizeTargetFeatures(value) : [];
|
|
2888
2924
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2925
|
+
return Config.normalizeFeatureList(features);
|
|
2926
|
+
}
|
|
2927
|
+
static normalizeFeatureList(features) {
|
|
2928
|
+
if (features.includes("*")) return [...ALL_FEATURES];
|
|
2929
|
+
return features.filter((feature) => feature !== "*");
|
|
2891
2930
|
}
|
|
2892
2931
|
/**
|
|
2893
2932
|
* Normalize a per-target features value (array or per-feature object) into
|
|
@@ -2928,7 +2967,14 @@ var Config = class Config {
|
|
|
2928
2967
|
* feature is not enabled for the given target.
|
|
2929
2968
|
*/
|
|
2930
2969
|
getFeatureOptions(target, feature) {
|
|
2931
|
-
|
|
2970
|
+
return Config.resolveFeatureOptions({
|
|
2971
|
+
targets: this.targets,
|
|
2972
|
+
target,
|
|
2973
|
+
feature
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
static resolveFeatureOptions({ targets, target, feature }) {
|
|
2977
|
+
const value = isRulesyncConfigTargetsObject(targets) ? targets[target] : void 0;
|
|
2932
2978
|
if (!value || Array.isArray(value)) return;
|
|
2933
2979
|
const featureValue = value[feature];
|
|
2934
2980
|
if (featureValue && typeof featureValue === "object" && isFeatureValueEnabled(featureValue)) return featureValue;
|
|
@@ -3363,7 +3409,15 @@ var ConfigResolver = class {
|
|
|
3363
3409
|
sources: configByFile.sources ?? getDefaults().sources,
|
|
3364
3410
|
flattenedCommandNaming: configByFile.flattenedCommandNaming ?? getDefaults().flattenedCommandNaming,
|
|
3365
3411
|
language: configByFile.language,
|
|
3366
|
-
configFileTargets: extractConfigFileTargets(configByFile.targets)
|
|
3412
|
+
configFileTargets: extractConfigFileTargets(configByFile.targets),
|
|
3413
|
+
configFileSelection: configByFile.targets === void 0 ? void 0 : {
|
|
3414
|
+
targets: configByFile.targets,
|
|
3415
|
+
features: resolveFeaturesAndTargets({
|
|
3416
|
+
features,
|
|
3417
|
+
targets: void 0,
|
|
3418
|
+
configByFile
|
|
3419
|
+
}).resolvedFeatures
|
|
3420
|
+
}
|
|
3367
3421
|
});
|
|
3368
3422
|
}
|
|
3369
3423
|
};
|
|
@@ -12295,7 +12349,7 @@ const CODEXCLI_AGENTS_DIR_PATH = (0, node_path.join)(CODEXCLI_DIR, "agents");
|
|
|
12295
12349
|
const CODEXCLI_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
|
|
12296
12350
|
const CODEXCLI_HOOKS_FILE_NAME = "hooks.json";
|
|
12297
12351
|
const CODEXCLI_MCP_FILE_NAME = "config.toml";
|
|
12298
|
-
const
|
|
12352
|
+
const CODEXCLI_PROJECT_DOC_MAX_BYTES = 32768;
|
|
12299
12353
|
const CODEXCLI_BASH_RULES_FILE_NAME = "rulesync.rules";
|
|
12300
12354
|
const CODEXCLI_OPENAI_YAML_RELATIVE_PATH = (0, node_path.join)("agents", "openai.yaml");
|
|
12301
12355
|
const CODEXCLI_OVERRIDE_KEYS = [
|
|
@@ -14965,6 +15019,7 @@ const ANTIGRAVITY_GLOBAL_AGENTS_DIR_PATH = (0, node_path.join)(ANTIGRAVITY_GLOBA
|
|
|
14965
15019
|
const ANTIGRAVITY_AGENTS_DIR = ANTIGRAVITY_DIR;
|
|
14966
15020
|
const ANTIGRAVITY_RULE_FILE_NAME = "AGENTS.md";
|
|
14967
15021
|
const ANTIGRAVITY_GLOBAL_RULE_FILE_NAME = "GEMINI.md";
|
|
15022
|
+
const ANTIGRAVITY_GLOBAL_RULES_SUBDIR = (0, node_path.join)(ANTIGRAVITY_GLOBAL_CONFIG_SUBDIR, "rules");
|
|
14968
15023
|
//#endregion
|
|
14969
15024
|
//#region src/features/commands/antigravity-command.ts
|
|
14970
15025
|
const AntigravityWorkflowFrontmatterSchema = zod_mini.z.looseObject({
|
|
@@ -15480,12 +15535,50 @@ var BobCommand = class BobCommand extends ToolCommand {
|
|
|
15480
15535
|
}
|
|
15481
15536
|
};
|
|
15482
15537
|
//#endregion
|
|
15538
|
+
//#region src/features/shared/claudecode-argument-hint.ts
|
|
15539
|
+
/**
|
|
15540
|
+
* Turns a YAML-list `argument-hint` back into the bracketed string form Claude
|
|
15541
|
+
* Code documents (`[issue-number]`, `[filename] [format]`), one bracketed
|
|
15542
|
+
* placeholder per list entry. The per-entry form is chosen over re-joining with
|
|
15543
|
+
* commas because it is the shape every Claude Code example uses. An empty list
|
|
15544
|
+
* carries no hint, so it yields `undefined` and the key is dropped.
|
|
15545
|
+
*/
|
|
15546
|
+
function joinArgumentHint(value) {
|
|
15547
|
+
if (typeof value === "string") return value;
|
|
15548
|
+
if (value.length === 0) return;
|
|
15549
|
+
return value.map((entry) => `[${String(entry)}]`).join(" ");
|
|
15550
|
+
}
|
|
15551
|
+
/**
|
|
15552
|
+
* `argument-hint` as Claude Code reads it from skill and command frontmatter.
|
|
15553
|
+
*
|
|
15554
|
+
* The documented examples (`[issue-number]`) are written unquoted, and YAML
|
|
15555
|
+
* parses an unquoted `[issue-number]` as a one-element list. Claude Code
|
|
15556
|
+
* accepts that form, so it is accepted here too and normalized to the
|
|
15557
|
+
* bracketed string, keeping the parsed value (and everything downstream of it)
|
|
15558
|
+
* a plain string. A scalar entry YAML did not read as a string (`[1]`,
|
|
15559
|
+
* `[true]`, `[~]`) is stringified rather than rejected.
|
|
15560
|
+
* https://code.claude.com/docs/en/skills
|
|
15561
|
+
*
|
|
15562
|
+
* A list entry is stringified from the value YAML produced, so its original
|
|
15563
|
+
* spelling is not kept (`[1.0]` becomes `[1]`), and an entry YAML reads as a
|
|
15564
|
+
* date (`[2026-01-01]`) is rejected.
|
|
15565
|
+
*
|
|
15566
|
+
* The unquoted multi-placeholder form (`argument-hint: [filename] [format]`)
|
|
15567
|
+
* is not valid YAML at all, so it fails before this schema is reached.
|
|
15568
|
+
*/
|
|
15569
|
+
const ClaudecodeArgumentHintSchema = zod_mini.z.pipe(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.union([
|
|
15570
|
+
zod_mini.z.string(),
|
|
15571
|
+
zod_mini.z.number(),
|
|
15572
|
+
zod_mini.z.boolean(),
|
|
15573
|
+
zod_mini.z.null()
|
|
15574
|
+
]))]), zod_mini.z.transform(joinArgumentHint));
|
|
15575
|
+
//#endregion
|
|
15483
15576
|
//#region src/features/commands/claudecode-command.ts
|
|
15484
15577
|
const ClaudecodeCommandFrontmatterSchema = zod_mini.z.looseObject({
|
|
15485
15578
|
description: zod_mini.z.optional(zod_mini.z.string()),
|
|
15486
15579
|
"allowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
15487
15580
|
"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(
|
|
15581
|
+
"argument-hint": zod_mini.z.optional(ClaudecodeArgumentHintSchema),
|
|
15489
15582
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
15490
15583
|
"disable-model-invocation": zod_mini.z.optional(zod_mini.z.boolean())
|
|
15491
15584
|
});
|
|
@@ -15514,7 +15607,8 @@ var ClaudecodeCommand = class ClaudecodeCommand extends ToolCommand {
|
|
|
15514
15607
|
return this.frontmatter;
|
|
15515
15608
|
}
|
|
15516
15609
|
toRulesyncCommand() {
|
|
15517
|
-
const { description, ...
|
|
15610
|
+
const { description, ...extraFields } = this.frontmatter;
|
|
15611
|
+
const restFields = Object.fromEntries(Object.entries(extraFields).filter(([, value]) => value !== void 0));
|
|
15518
15612
|
const rulesyncFrontmatter = {
|
|
15519
15613
|
targets: ["*"],
|
|
15520
15614
|
description,
|
|
@@ -33147,6 +33241,74 @@ function translateCanonicalKeyToCodex({ key, value, isStdio, serverName }) {
|
|
|
33147
33241
|
}
|
|
33148
33242
|
}
|
|
33149
33243
|
/**
|
|
33244
|
+
* A header value that is exactly one canonical `${VAR}` reference. Anchored and
|
|
33245
|
+
* without the `g` flag, so `.exec()` carries no `lastIndex` state between
|
|
33246
|
+
* calls. `${VAR:-default}` does not match (the name excludes `:`), because
|
|
33247
|
+
* Codex has no way to express the fallback.
|
|
33248
|
+
*/
|
|
33249
|
+
const WHOLE_ENV_VAR_REF_PATTERN = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
33250
|
+
/**
|
|
33251
|
+
* An `Authorization` value that is exactly `Bearer ${VAR}`. The scheme is
|
|
33252
|
+
* matched case-insensitively, as HTTP authentication schemes are.
|
|
33253
|
+
*/
|
|
33254
|
+
const BEARER_ENV_VAR_REF_PATTERN = /^Bearer \$\{([A-Za-z_][A-Za-z0-9_]*)\}$/i;
|
|
33255
|
+
const AUTHORIZATION_HEADER_NAME = "authorization";
|
|
33256
|
+
/**
|
|
33257
|
+
* Whether a string still contains an unexpanded `${…}` reference. Codex writes
|
|
33258
|
+
* `url` and `http_headers` values as-is and never expands them, so such a value
|
|
33259
|
+
* reaches the server literally.
|
|
33260
|
+
*/
|
|
33261
|
+
function hasUnexpandedEnvVarRef(value) {
|
|
33262
|
+
return value.includes("${");
|
|
33263
|
+
}
|
|
33264
|
+
/**
|
|
33265
|
+
* Rewrite canonical `${VAR}` header references into the keys Codex reads them
|
|
33266
|
+
* from, since Codex sends `http_headers` values verbatim:
|
|
33267
|
+
* - `Authorization: Bearer ${VAR}` → `bearer_token_env_var = "VAR"`
|
|
33268
|
+
* - a header whose whole value is `${VAR}` → `env_http_headers.<name> = "VAR"`
|
|
33269
|
+
*
|
|
33270
|
+
* Runs after every key is translated, so a `bearer_token_env_var` or
|
|
33271
|
+
* `env_http_headers` the source already carries (they pass through under their
|
|
33272
|
+
* own names) is seen regardless of key order. Those explicit values win: a
|
|
33273
|
+
* derived bearer is not written over an existing one, and an existing
|
|
33274
|
+
* `env_http_headers` entry for the same header is kept. Any value the rewrite
|
|
33275
|
+
* cannot express — a partial reference, a `${VAR:-default}` fallback — stays in
|
|
33276
|
+
* `http_headers` verbatim with a warning, as does a `${…}` in `url`, for which
|
|
33277
|
+
* Codex has no environment-sourced counterpart at all.
|
|
33278
|
+
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
33279
|
+
*/
|
|
33280
|
+
function convertEnvVarRefsForCodex({ converted, serverName }) {
|
|
33281
|
+
const url = converted["url"];
|
|
33282
|
+
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.`);
|
|
33283
|
+
const httpHeaders = converted["http_headers"];
|
|
33284
|
+
if (!isHeadersRecord(httpHeaders)) return;
|
|
33285
|
+
const existingEnvHeaders = converted["env_http_headers"];
|
|
33286
|
+
const canWriteEnvHeaders = existingEnvHeaders === void 0 || isHeadersRecord(existingEnvHeaders);
|
|
33287
|
+
const envHeaders = {};
|
|
33288
|
+
const staticHeaders = {};
|
|
33289
|
+
let bearerTokenEnvVar;
|
|
33290
|
+
for (const [name, value] of Object.entries(httpHeaders)) {
|
|
33291
|
+
const bearerMatch = BEARER_ENV_VAR_REF_PATTERN.exec(value);
|
|
33292
|
+
if (bearerMatch?.[1] && name.toLowerCase() === AUTHORIZATION_HEADER_NAME) {
|
|
33293
|
+
if (converted["bearer_token_env_var"] === void 0 && bearerTokenEnvVar === void 0) bearerTokenEnvVar = bearerMatch[1];
|
|
33294
|
+
continue;
|
|
33295
|
+
}
|
|
33296
|
+
const wholeMatch = WHOLE_ENV_VAR_REF_PATTERN.exec(value);
|
|
33297
|
+
if (wholeMatch?.[1] && canWriteEnvHeaders) {
|
|
33298
|
+
envHeaders[name] = wholeMatch[1];
|
|
33299
|
+
continue;
|
|
33300
|
+
}
|
|
33301
|
+
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.`);
|
|
33302
|
+
staticHeaders[name] = value;
|
|
33303
|
+
}
|
|
33304
|
+
converted["http_headers"] = staticHeaders;
|
|
33305
|
+
if (bearerTokenEnvVar !== void 0) converted["bearer_token_env_var"] = bearerTokenEnvVar;
|
|
33306
|
+
if (Object.keys(envHeaders).length > 0) converted["env_http_headers"] = {
|
|
33307
|
+
...envHeaders,
|
|
33308
|
+
...isHeadersRecord(existingEnvHeaders) ? existingEnvHeaders : {}
|
|
33309
|
+
};
|
|
33310
|
+
}
|
|
33311
|
+
/**
|
|
33150
33312
|
* Codex states no transport of its own — it infers one from `command` versus
|
|
33151
33313
|
* `url`, which is why generate drops the canonical `type`. Restate it for a url
|
|
33152
33314
|
* server on the way back, so a config imported from Codex reaches the adapters
|
|
@@ -33233,6 +33395,10 @@ function convertToCodexFormat(mcpServers) {
|
|
|
33233
33395
|
}
|
|
33234
33396
|
} else converted[key] = value;
|
|
33235
33397
|
}
|
|
33398
|
+
convertEnvVarRefsForCodex({
|
|
33399
|
+
converted,
|
|
33400
|
+
serverName: name
|
|
33401
|
+
});
|
|
33236
33402
|
const previousName = originalNames.get(codexName);
|
|
33237
33403
|
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
33404
|
originalNames.set(codexName, name);
|
|
@@ -59232,7 +59398,7 @@ const ClaudecodeSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
|
59232
59398
|
"disallowed-tools": zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
59233
59399
|
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
59234
59400
|
effort: zod_mini.z.optional(zod_mini.z.string()),
|
|
59235
|
-
"argument-hint": zod_mini.z.optional(
|
|
59401
|
+
"argument-hint": zod_mini.z.optional(ClaudecodeArgumentHintSchema),
|
|
59236
59402
|
arguments: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())])),
|
|
59237
59403
|
context: zod_mini.z.optional(zod_mini.z.string()),
|
|
59238
59404
|
agent: zod_mini.z.optional(zod_mini.z.string()),
|
|
@@ -65388,7 +65554,8 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
65388
65554
|
meta: {
|
|
65389
65555
|
supportsProject: true,
|
|
65390
65556
|
supportsSimulated: false,
|
|
65391
|
-
supportsGlobal: true
|
|
65557
|
+
supportsGlobal: true,
|
|
65558
|
+
lenientImport: true
|
|
65392
65559
|
}
|
|
65393
65560
|
}],
|
|
65394
65561
|
["claudecode-plugin", {
|
|
@@ -72337,7 +72504,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72337
72504
|
static buildToolRuleParamsAgentsmd({ outputRoot = process.cwd(), rulesyncRule, validate = true, rootPath = {
|
|
72338
72505
|
relativeDirPath: ".",
|
|
72339
72506
|
relativeFilePath: "AGENTS.md"
|
|
72340
|
-
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH } }) {
|
|
72507
|
+
}, nonRootPath = { relativeDirPath: AGENTSMD_MEMORIES_DIR_PATH }, global = false }) {
|
|
72341
72508
|
const params = this.buildToolRuleParamsDefault({
|
|
72342
72509
|
outputRoot,
|
|
72343
72510
|
rulesyncRule,
|
|
@@ -72346,7 +72513,7 @@ var ToolRule = class extends ToolFile {
|
|
|
72346
72513
|
nonRootPath
|
|
72347
72514
|
});
|
|
72348
72515
|
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
72349
|
-
if (!rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72516
|
+
if (!global && !rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
|
|
72350
72517
|
params.relativeDirPath = (0, node_path.join)(rulesyncFrontmatter.agentsmd.subprojectPath);
|
|
72351
72518
|
params.relativeFilePath = "AGENTS.md";
|
|
72352
72519
|
}
|
|
@@ -73003,7 +73170,8 @@ var AmpRule = class AmpRule extends ToolRule {
|
|
|
73003
73170
|
rulesyncRule,
|
|
73004
73171
|
validate,
|
|
73005
73172
|
rootPath: paths.root,
|
|
73006
|
-
nonRootPath: paths.nonRoot
|
|
73173
|
+
nonRootPath: paths.nonRoot,
|
|
73174
|
+
global
|
|
73007
73175
|
});
|
|
73008
73176
|
if (!params.root && params.globs !== void 0 && params.globs.length > 0) params.fileContent = stringifyFrontmatter(params.fileContent, { globs: params.globs });
|
|
73009
73177
|
return new AmpRule(params);
|
|
@@ -73049,97 +73217,6 @@ var AmpRule = class AmpRule extends ToolRule {
|
|
|
73049
73217
|
}
|
|
73050
73218
|
};
|
|
73051
73219
|
//#endregion
|
|
73052
|
-
//#region src/features/rules/antigravity-cli-rule.ts
|
|
73053
|
-
/**
|
|
73054
|
-
* Rule generator for the Google Antigravity CLI (`agy`, the Gemini-CLI
|
|
73055
|
-
* successor in Antigravity 2.0).
|
|
73056
|
-
*
|
|
73057
|
-
* The CLI reads the same plain-markdown context files as Gemini CLI — a root
|
|
73058
|
-
* context file plus non-root memory files in `.agents/rules/` — so this class
|
|
73059
|
-
* follows that same plain-markdown approach but points at the new `.agents/` tree.
|
|
73060
|
-
*
|
|
73061
|
-
* - Project scope: root `AGENTS.md` (the cross-tool standard, matching
|
|
73062
|
-
* `antigravity-ide`); non-root `.agents/rules/*.md`.
|
|
73063
|
-
* - Global scope: a single plain `~/.gemini/GEMINI.md` (shared with the IDE).
|
|
73064
|
-
*/
|
|
73065
|
-
var AntigravityCliRule = class AntigravityCliRule extends ToolRule {
|
|
73066
|
-
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
73067
|
-
if (global) return { root: {
|
|
73068
|
-
relativeDirPath: buildToolPath(ANTIGRAVITY_GEMINI_DIR, ".", excludeToolDir),
|
|
73069
|
-
relativeFilePath: ANTIGRAVITY_GLOBAL_RULE_FILE_NAME
|
|
73070
|
-
} };
|
|
73071
|
-
return {
|
|
73072
|
-
root: {
|
|
73073
|
-
relativeDirPath: ".",
|
|
73074
|
-
relativeFilePath: ANTIGRAVITY_RULE_FILE_NAME
|
|
73075
|
-
},
|
|
73076
|
-
nonRoot: { relativeDirPath: buildToolPath(ANTIGRAVITY_AGENTS_DIR, "rules", excludeToolDir) }
|
|
73077
|
-
};
|
|
73078
|
-
}
|
|
73079
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
73080
|
-
const paths = this.getSettablePaths({ global });
|
|
73081
|
-
if (relativeFilePath === paths.root.relativeFilePath) {
|
|
73082
|
-
const relativePath = paths.root.relativeFilePath;
|
|
73083
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, paths.root.relativeDirPath, relativePath));
|
|
73084
|
-
return new AntigravityCliRule({
|
|
73085
|
-
outputRoot,
|
|
73086
|
-
relativeDirPath: paths.root.relativeDirPath,
|
|
73087
|
-
relativeFilePath: paths.root.relativeFilePath,
|
|
73088
|
-
fileContent,
|
|
73089
|
-
validate,
|
|
73090
|
-
root: true
|
|
73091
|
-
});
|
|
73092
|
-
}
|
|
73093
|
-
if (!paths.nonRoot) throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
|
|
73094
|
-
const relativePath = (0, node_path.join)(paths.nonRoot.relativeDirPath, relativeFilePath);
|
|
73095
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, relativePath));
|
|
73096
|
-
return new AntigravityCliRule({
|
|
73097
|
-
outputRoot,
|
|
73098
|
-
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
73099
|
-
relativeFilePath,
|
|
73100
|
-
fileContent,
|
|
73101
|
-
validate,
|
|
73102
|
-
root: false
|
|
73103
|
-
});
|
|
73104
|
-
}
|
|
73105
|
-
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
73106
|
-
const paths = this.getSettablePaths({ global });
|
|
73107
|
-
return new AntigravityCliRule(this.buildToolRuleParamsDefault({
|
|
73108
|
-
outputRoot,
|
|
73109
|
-
rulesyncRule,
|
|
73110
|
-
validate,
|
|
73111
|
-
rootPath: paths.root,
|
|
73112
|
-
nonRootPath: paths.nonRoot
|
|
73113
|
-
}));
|
|
73114
|
-
}
|
|
73115
|
-
toRulesyncRule() {
|
|
73116
|
-
return this.toRulesyncRuleDefault();
|
|
73117
|
-
}
|
|
73118
|
-
validate() {
|
|
73119
|
-
return {
|
|
73120
|
-
success: true,
|
|
73121
|
-
error: null
|
|
73122
|
-
};
|
|
73123
|
-
}
|
|
73124
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
73125
|
-
const isRoot = relativeFilePath === this.getSettablePaths({ global }).root.relativeFilePath;
|
|
73126
|
-
return new AntigravityCliRule({
|
|
73127
|
-
outputRoot,
|
|
73128
|
-
relativeDirPath,
|
|
73129
|
-
relativeFilePath,
|
|
73130
|
-
fileContent: "",
|
|
73131
|
-
validate: false,
|
|
73132
|
-
root: isRoot
|
|
73133
|
-
});
|
|
73134
|
-
}
|
|
73135
|
-
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
73136
|
-
return this.isTargetedByRulesyncRuleDefault({
|
|
73137
|
-
rulesyncRule,
|
|
73138
|
-
toolTarget: "antigravity-cli"
|
|
73139
|
-
});
|
|
73140
|
-
}
|
|
73141
|
-
};
|
|
73142
|
-
//#endregion
|
|
73143
73220
|
//#region src/features/rules/antigravity-rule.ts
|
|
73144
73221
|
const AntigravityRuleFrontmatterSchema = zod_mini.z.looseObject({
|
|
73145
73222
|
trigger: zod_mini.z.optional(zod_mini.z.union([
|
|
@@ -73286,6 +73363,185 @@ const STRATEGIES$1 = [
|
|
|
73286
73363
|
}
|
|
73287
73364
|
];
|
|
73288
73365
|
//#endregion
|
|
73366
|
+
//#region src/features/rules/antigravity-cli-rule.ts
|
|
73367
|
+
/**
|
|
73368
|
+
* Rule generator for the Google Antigravity CLI (`agy`, the Gemini-CLI
|
|
73369
|
+
* successor in Antigravity 2.0).
|
|
73370
|
+
*
|
|
73371
|
+
* The CLI reads the same plain-markdown context files as Gemini CLI — a root
|
|
73372
|
+
* context file plus non-root memory files in `.agents/rules/` — so this class
|
|
73373
|
+
* follows that same plain-markdown approach but points at the new `.agents/` tree.
|
|
73374
|
+
*
|
|
73375
|
+
* - Project scope: root `AGENTS.md` (the cross-tool standard, matching
|
|
73376
|
+
* `antigravity-ide`); non-root `.agents/rules/*.md`.
|
|
73377
|
+
* - Global scope: root plain `~/.gemini/GEMINI.md` (shared with the IDE);
|
|
73378
|
+
* non-root `~/.gemini/config/rules/*.md`. The CLI truncates each rule file at
|
|
73379
|
+
* 24,000 bytes, so keeping non-root rules as separate files stops a large rule
|
|
73380
|
+
* set from being cut off. Every file in that directory must carry a valid
|
|
73381
|
+
* `trigger` frontmatter or the CLI discards it, so these files get the same
|
|
73382
|
+
* trigger-strategy frontmatter as the IDE's non-root rules.
|
|
73383
|
+
*
|
|
73384
|
+
* @see https://antigravity.google/docs/rules
|
|
73385
|
+
*/
|
|
73386
|
+
var AntigravityCliRule = class AntigravityCliRule extends ToolRule {
|
|
73387
|
+
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
73388
|
+
if (global) return {
|
|
73389
|
+
root: {
|
|
73390
|
+
relativeDirPath: buildToolPath(ANTIGRAVITY_GEMINI_DIR, ".", excludeToolDir),
|
|
73391
|
+
relativeFilePath: ANTIGRAVITY_GLOBAL_RULE_FILE_NAME
|
|
73392
|
+
},
|
|
73393
|
+
nonRoot: { relativeDirPath: buildToolPath(ANTIGRAVITY_GEMINI_DIR, ANTIGRAVITY_GLOBAL_RULES_SUBDIR, excludeToolDir) }
|
|
73394
|
+
};
|
|
73395
|
+
return {
|
|
73396
|
+
root: {
|
|
73397
|
+
relativeDirPath: ".",
|
|
73398
|
+
relativeFilePath: ANTIGRAVITY_RULE_FILE_NAME
|
|
73399
|
+
},
|
|
73400
|
+
nonRoot: { relativeDirPath: buildToolPath(ANTIGRAVITY_AGENTS_DIR, "rules", excludeToolDir) }
|
|
73401
|
+
};
|
|
73402
|
+
}
|
|
73403
|
+
static async fromFile({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, validate = true, global = false }) {
|
|
73404
|
+
const paths = this.getSettablePaths({ global });
|
|
73405
|
+
if (relativeFilePath === paths.root.relativeFilePath && (relativeDirPath === void 0 || relativeDirPath === paths.root.relativeDirPath)) {
|
|
73406
|
+
const relativePath = paths.root.relativeFilePath;
|
|
73407
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, paths.root.relativeDirPath, relativePath));
|
|
73408
|
+
return new AntigravityCliRule({
|
|
73409
|
+
outputRoot,
|
|
73410
|
+
relativeDirPath: paths.root.relativeDirPath,
|
|
73411
|
+
relativeFilePath: paths.root.relativeFilePath,
|
|
73412
|
+
fileContent,
|
|
73413
|
+
validate,
|
|
73414
|
+
root: true,
|
|
73415
|
+
global
|
|
73416
|
+
});
|
|
73417
|
+
}
|
|
73418
|
+
if (!paths.nonRoot) throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
|
|
73419
|
+
const relativePath = (0, node_path.join)(paths.nonRoot.relativeDirPath, relativeFilePath);
|
|
73420
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, relativePath));
|
|
73421
|
+
const rule = new AntigravityCliRule({
|
|
73422
|
+
outputRoot,
|
|
73423
|
+
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
73424
|
+
relativeFilePath,
|
|
73425
|
+
fileContent,
|
|
73426
|
+
validate,
|
|
73427
|
+
root: false,
|
|
73428
|
+
global
|
|
73429
|
+
});
|
|
73430
|
+
if (validate) {
|
|
73431
|
+
const result = rule.validate();
|
|
73432
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${relativePath}: ${result.error.message}`);
|
|
73433
|
+
}
|
|
73434
|
+
return rule;
|
|
73435
|
+
}
|
|
73436
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
73437
|
+
const paths = this.getSettablePaths({ global });
|
|
73438
|
+
const params = this.buildToolRuleParamsDefault({
|
|
73439
|
+
outputRoot,
|
|
73440
|
+
rulesyncRule,
|
|
73441
|
+
validate,
|
|
73442
|
+
rootPath: paths.root,
|
|
73443
|
+
nonRootPath: paths.nonRoot
|
|
73444
|
+
});
|
|
73445
|
+
if (!global || params.root) return new AntigravityCliRule({
|
|
73446
|
+
...params,
|
|
73447
|
+
global
|
|
73448
|
+
});
|
|
73449
|
+
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
73450
|
+
const storedAntigravity = rulesyncFrontmatter.antigravity;
|
|
73451
|
+
const storedTrigger = storedAntigravity?.trigger;
|
|
73452
|
+
const strategy = STRATEGIES$1.find((s) => s.canHandle(storedTrigger));
|
|
73453
|
+
if (!strategy) throw new Error(`No strategy found for trigger: ${storedTrigger}`);
|
|
73454
|
+
const frontmatter = strategy.generateFrontmatter(normalizeStoredAntigravity(storedAntigravity), rulesyncFrontmatter);
|
|
73455
|
+
return new AntigravityCliRule({
|
|
73456
|
+
...params,
|
|
73457
|
+
relativeFilePath: params.relativeFilePath.split(/[\\/]/).join("-"),
|
|
73458
|
+
fileContent: stringifyFrontmatter(rulesyncRule.getBody(), frontmatter),
|
|
73459
|
+
global
|
|
73460
|
+
});
|
|
73461
|
+
}
|
|
73462
|
+
toRulesyncRule() {
|
|
73463
|
+
if (!this.global || this.root) return this.toRulesyncRuleDefault();
|
|
73464
|
+
const { frontmatter, body } = this.parseGlobalNonRootContent();
|
|
73465
|
+
const strategy = STRATEGIES$1.find((s) => s.canHandle(frontmatter.trigger));
|
|
73466
|
+
const rulesyncData = strategy ? strategy.exportRulesyncData(frontmatter) : {
|
|
73467
|
+
globs: [],
|
|
73468
|
+
antigravity: frontmatter
|
|
73469
|
+
};
|
|
73470
|
+
return new RulesyncRule({
|
|
73471
|
+
outputRoot: process.cwd(),
|
|
73472
|
+
relativeDirPath: RulesyncRule.getSettablePaths().recommended.relativeDirPath,
|
|
73473
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
73474
|
+
frontmatter: {
|
|
73475
|
+
root: false,
|
|
73476
|
+
targets: ["*"],
|
|
73477
|
+
...rulesyncData,
|
|
73478
|
+
antigravity: {
|
|
73479
|
+
...rulesyncData.antigravity,
|
|
73480
|
+
globs: frontmatter.globs ? parseGlobsString$1(frontmatter.globs) : void 0
|
|
73481
|
+
}
|
|
73482
|
+
},
|
|
73483
|
+
body
|
|
73484
|
+
});
|
|
73485
|
+
}
|
|
73486
|
+
validate() {
|
|
73487
|
+
if (!this.global || this.root) return {
|
|
73488
|
+
success: true,
|
|
73489
|
+
error: null
|
|
73490
|
+
};
|
|
73491
|
+
try {
|
|
73492
|
+
const { frontmatter } = this.parseGlobalNonRootContent();
|
|
73493
|
+
const result = AntigravityRuleFrontmatterSchema.safeParse(frontmatter);
|
|
73494
|
+
if (!result.success) return {
|
|
73495
|
+
success: false,
|
|
73496
|
+
error: new Error(formatError(result.error))
|
|
73497
|
+
};
|
|
73498
|
+
} catch (error) {
|
|
73499
|
+
return {
|
|
73500
|
+
success: false,
|
|
73501
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
73502
|
+
};
|
|
73503
|
+
}
|
|
73504
|
+
return {
|
|
73505
|
+
success: true,
|
|
73506
|
+
error: null
|
|
73507
|
+
};
|
|
73508
|
+
}
|
|
73509
|
+
/**
|
|
73510
|
+
* `~/.gemini/config/rules/` is shared with the Antigravity IDE and holds
|
|
73511
|
+
* global rules the user created outside rulesync, which cannot be told apart
|
|
73512
|
+
* from generated ones, so the orphan sweep never removes files there.
|
|
73513
|
+
*/
|
|
73514
|
+
isDeletable() {
|
|
73515
|
+
return !this.global || this.root;
|
|
73516
|
+
}
|
|
73517
|
+
parseGlobalNonRootContent() {
|
|
73518
|
+
const { frontmatter, body } = parseFrontmatter(this.getFileContent(), this.getFilePath());
|
|
73519
|
+
return {
|
|
73520
|
+
frontmatter,
|
|
73521
|
+
body
|
|
73522
|
+
};
|
|
73523
|
+
}
|
|
73524
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
73525
|
+
const paths = this.getSettablePaths({ global });
|
|
73526
|
+
const isRoot = relativeFilePath === paths.root.relativeFilePath && relativeDirPath === paths.root.relativeDirPath;
|
|
73527
|
+
return new AntigravityCliRule({
|
|
73528
|
+
outputRoot,
|
|
73529
|
+
relativeDirPath,
|
|
73530
|
+
relativeFilePath,
|
|
73531
|
+
fileContent: "",
|
|
73532
|
+
validate: false,
|
|
73533
|
+
root: isRoot,
|
|
73534
|
+
global
|
|
73535
|
+
});
|
|
73536
|
+
}
|
|
73537
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
73538
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
73539
|
+
rulesyncRule,
|
|
73540
|
+
toolTarget: "antigravity-cli"
|
|
73541
|
+
});
|
|
73542
|
+
}
|
|
73543
|
+
};
|
|
73544
|
+
//#endregion
|
|
73289
73545
|
//#region src/features/rules/antigravity-ide-rule.ts
|
|
73290
73546
|
/**
|
|
73291
73547
|
* Rule generator for the Google Antigravity IDE (Antigravity 2.0).
|
|
@@ -74298,7 +74554,8 @@ var ClineRule = class ClineRule extends ToolRule {
|
|
|
74298
74554
|
outputRoot,
|
|
74299
74555
|
rulesyncRule,
|
|
74300
74556
|
validate,
|
|
74301
|
-
rootPath: paths.root
|
|
74557
|
+
rootPath: paths.root,
|
|
74558
|
+
global
|
|
74302
74559
|
}));
|
|
74303
74560
|
}
|
|
74304
74561
|
if (isRoot) return new ClineRule({
|
|
@@ -74718,26 +74975,62 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
|
|
|
74718
74975
|
}
|
|
74719
74976
|
};
|
|
74720
74977
|
//#endregion
|
|
74721
|
-
//#region src/features/rules/
|
|
74722
|
-
|
|
74723
|
-
|
|
74724
|
-
|
|
74725
|
-
|
|
74726
|
-
|
|
74727
|
-
|
|
74728
|
-
|
|
74978
|
+
//#region src/features/rules/nested-agentsmd-rule.ts
|
|
74979
|
+
/**
|
|
74980
|
+
* Shared adapter for the targets that read instructions exactly the way the
|
|
74981
|
+
* AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
|
|
74982
|
+
* project root, nested per-directory `AGENTS.md` files loaded only while working
|
|
74983
|
+
* under that directory, and a single personal root file under a tool-specific
|
|
74984
|
+
* home directory. Such a target has no modular non-root instruction directory,
|
|
74985
|
+
* so every other non-root rule folds into the root file.
|
|
74986
|
+
*
|
|
74987
|
+
* Concrete targets (Codex CLI, Pool, Vibe, DeepSeek Harness) only supply the global
|
|
74988
|
+
* directory and their tool target through {@link getFamily}; the vendor docs
|
|
74989
|
+
* describing each one's discovery walk live on the subclass.
|
|
74990
|
+
*
|
|
74991
|
+
* The nested-file scan mirrors the AGENTS.md standard's nested discovery —
|
|
74992
|
+
* same file name, same exclusions, import-only, project scope — because it
|
|
74993
|
+
* discovers literally the same files.
|
|
74994
|
+
*/
|
|
74995
|
+
var NestedAgentsmdRule = class extends ToolRule {
|
|
74996
|
+
static getFamily() {
|
|
74997
|
+
throw new Error("Please implement this method in the subclass.");
|
|
74729
74998
|
}
|
|
74730
74999
|
static getSettablePaths({ global = false } = {}) {
|
|
75000
|
+
if (global) return { root: {
|
|
75001
|
+
relativeDirPath: this.getFamily().globalDir,
|
|
75002
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
75003
|
+
} };
|
|
74731
75004
|
return { root: {
|
|
74732
|
-
relativeDirPath:
|
|
74733
|
-
relativeFilePath:
|
|
75005
|
+
relativeDirPath: ".",
|
|
75006
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
74734
75007
|
} };
|
|
74735
75008
|
}
|
|
74736
|
-
static
|
|
75009
|
+
static getNestedFilePatterns() {
|
|
75010
|
+
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
75011
|
+
}
|
|
75012
|
+
/**
|
|
75013
|
+
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
75014
|
+
* (project or global).
|
|
75015
|
+
*/
|
|
75016
|
+
getSubprojectPath() {
|
|
75017
|
+
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
75018
|
+
}
|
|
75019
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
74737
75020
|
const { root } = this.getSettablePaths({ global });
|
|
74738
|
-
|
|
74739
|
-
|
|
74740
|
-
|
|
75021
|
+
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
75022
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
75023
|
+
return new this({
|
|
75024
|
+
outputRoot,
|
|
75025
|
+
relativeDirPath: overrideDirPath,
|
|
75026
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
75027
|
+
fileContent,
|
|
75028
|
+
validate,
|
|
75029
|
+
root: false
|
|
75030
|
+
});
|
|
75031
|
+
}
|
|
75032
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
75033
|
+
return new this({
|
|
74741
75034
|
outputRoot,
|
|
74742
75035
|
relativeDirPath: root.relativeDirPath,
|
|
74743
75036
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74748,8 +75041,18 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74748
75041
|
}
|
|
74749
75042
|
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
74750
75043
|
const { root } = this.getSettablePaths({ global });
|
|
74751
|
-
const
|
|
74752
|
-
|
|
75044
|
+
const frontmatter = rulesyncRule.getFrontmatter();
|
|
75045
|
+
const isRoot = frontmatter.root ?? false;
|
|
75046
|
+
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
75047
|
+
if (!global && !isRoot && subprojectPath) return new this({
|
|
75048
|
+
outputRoot,
|
|
75049
|
+
relativeDirPath: (0, node_path.join)(subprojectPath),
|
|
75050
|
+
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
75051
|
+
fileContent: rulesyncRule.getBody(),
|
|
75052
|
+
validate,
|
|
75053
|
+
root: false
|
|
75054
|
+
});
|
|
75055
|
+
return new this({
|
|
74753
75056
|
outputRoot,
|
|
74754
75057
|
relativeDirPath: root.relativeDirPath,
|
|
74755
75058
|
relativeFilePath: root.relativeFilePath,
|
|
@@ -74759,7 +75062,9 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74759
75062
|
});
|
|
74760
75063
|
}
|
|
74761
75064
|
toRulesyncRule() {
|
|
74762
|
-
|
|
75065
|
+
const subprojectPath = this.getSubprojectPath();
|
|
75066
|
+
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
75067
|
+
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
74763
75068
|
}
|
|
74764
75069
|
validate() {
|
|
74765
75070
|
return {
|
|
@@ -74768,23 +75073,54 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
74768
75073
|
};
|
|
74769
75074
|
}
|
|
74770
75075
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
74771
|
-
|
|
75076
|
+
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
75077
|
+
return new this({
|
|
74772
75078
|
outputRoot,
|
|
74773
75079
|
relativeDirPath,
|
|
74774
75080
|
relativeFilePath,
|
|
74775
75081
|
fileContent: "",
|
|
74776
75082
|
validate: false,
|
|
74777
|
-
root:
|
|
75083
|
+
root: isRoot
|
|
74778
75084
|
});
|
|
74779
75085
|
}
|
|
74780
75086
|
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
74781
75087
|
return this.isTargetedByRulesyncRuleDefault({
|
|
74782
75088
|
rulesyncRule,
|
|
74783
|
-
toolTarget:
|
|
75089
|
+
toolTarget: this.getFamily().toolTarget
|
|
74784
75090
|
});
|
|
74785
75091
|
}
|
|
74786
75092
|
};
|
|
74787
75093
|
//#endregion
|
|
75094
|
+
//#region src/features/rules/codexcli-rule.ts
|
|
75095
|
+
/**
|
|
75096
|
+
* Rule generator for OpenAI Codex CLI.
|
|
75097
|
+
*
|
|
75098
|
+
* Codex CLI loads instructions only from the `AGENTS.md` family: the global
|
|
75099
|
+
* `~/.codex/AGENTS.md` (or `AGENTS.override.md`), then one file per directory
|
|
75100
|
+
* level from the project root down to the current working directory, joined
|
|
75101
|
+
* root-first so deeper files take precedence. Nested per-directory files are
|
|
75102
|
+
* therefore a real scoping surface, and Codex's docs recommend splitting large
|
|
75103
|
+
* instruction sets across nested directories. It does NOT scan a
|
|
75104
|
+
* `.codex/memories/` directory for instruction files — that directory belongs
|
|
75105
|
+
* to Codex's separate SQLite-backed auto-memory system.
|
|
75106
|
+
*
|
|
75107
|
+
* A non-root rule carrying `agentsmd.subprojectPath` is emitted as a nested
|
|
75108
|
+
* `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
|
|
75109
|
+
* folds into the root `AGENTS.md`, since Codex has no modular rules directory.
|
|
75110
|
+
* The project files on the root-to-cwd chain share one `project_doc_max_bytes`
|
|
75111
|
+
* budget (32 KiB by default), which the RulesProcessor warns about.
|
|
75112
|
+
*
|
|
75113
|
+
* @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
|
|
75114
|
+
*/
|
|
75115
|
+
var CodexcliRule = class extends NestedAgentsmdRule {
|
|
75116
|
+
static getFamily() {
|
|
75117
|
+
return {
|
|
75118
|
+
globalDir: CODEXCLI_DIR,
|
|
75119
|
+
toolTarget: "codexcli"
|
|
75120
|
+
};
|
|
75121
|
+
}
|
|
75122
|
+
};
|
|
75123
|
+
//#endregion
|
|
74788
75124
|
//#region src/features/rules/commandcode-rule.ts
|
|
74789
75125
|
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
74790
75126
|
constructor({ fileContent, root, ...rest }) {
|
|
@@ -76089,122 +76425,6 @@ var DevinRule = class DevinRule extends ToolRule {
|
|
|
76089
76425
|
}
|
|
76090
76426
|
};
|
|
76091
76427
|
//#endregion
|
|
76092
|
-
//#region src/features/rules/nested-agentsmd-rule.ts
|
|
76093
|
-
/**
|
|
76094
|
-
* Shared adapter for the targets that read instructions exactly the way the
|
|
76095
|
-
* AGENTS.md standard describes them and nothing more: a root `AGENTS.md` at the
|
|
76096
|
-
* project root, nested per-directory `AGENTS.md` files loaded only while working
|
|
76097
|
-
* under that directory, and a single personal root file under a tool-specific
|
|
76098
|
-
* home directory. Such a target has no modular non-root instruction directory,
|
|
76099
|
-
* so every other non-root rule folds into the root file.
|
|
76100
|
-
*
|
|
76101
|
-
* Concrete targets (Pool, Vibe, DeepSeek Harness) only supply the global
|
|
76102
|
-
* directory and their tool target through {@link getFamily}; the vendor docs
|
|
76103
|
-
* describing each one's discovery walk live on the subclass.
|
|
76104
|
-
*
|
|
76105
|
-
* The nested-file scan mirrors the AGENTS.md standard's nested discovery —
|
|
76106
|
-
* same file name, same exclusions, import-only, project scope — because it
|
|
76107
|
-
* discovers literally the same files.
|
|
76108
|
-
*/
|
|
76109
|
-
var NestedAgentsmdRule = class extends ToolRule {
|
|
76110
|
-
static getFamily() {
|
|
76111
|
-
throw new Error("Please implement this method in the subclass.");
|
|
76112
|
-
}
|
|
76113
|
-
static getSettablePaths({ global = false } = {}) {
|
|
76114
|
-
if (global) return { root: {
|
|
76115
|
-
relativeDirPath: this.getFamily().globalDir,
|
|
76116
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76117
|
-
} };
|
|
76118
|
-
return { root: {
|
|
76119
|
-
relativeDirPath: ".",
|
|
76120
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME
|
|
76121
|
-
} };
|
|
76122
|
-
}
|
|
76123
|
-
static getNestedFilePatterns() {
|
|
76124
|
-
return this.buildNestedFilePatterns({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76125
|
-
}
|
|
76126
|
-
/**
|
|
76127
|
-
* The subproject directory this rule scopes, or `undefined` for the root file
|
|
76128
|
-
* (project or global).
|
|
76129
|
-
*/
|
|
76130
|
-
getSubprojectPath() {
|
|
76131
|
-
return this.getNestedSubprojectPath({ fileName: AGENTSMD_RULE_FILE_NAME });
|
|
76132
|
-
}
|
|
76133
|
-
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, relativeDirPath: overrideDirPath, validate = true, global = false }) {
|
|
76134
|
-
const { root } = this.getSettablePaths({ global });
|
|
76135
|
-
if (overrideDirPath !== void 0 && overrideDirPath !== root.relativeDirPath && overrideDirPath !== ".") {
|
|
76136
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, overrideDirPath, AGENTSMD_RULE_FILE_NAME));
|
|
76137
|
-
return new this({
|
|
76138
|
-
outputRoot,
|
|
76139
|
-
relativeDirPath: overrideDirPath,
|
|
76140
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76141
|
-
fileContent,
|
|
76142
|
-
validate,
|
|
76143
|
-
root: false
|
|
76144
|
-
});
|
|
76145
|
-
}
|
|
76146
|
-
const fileContent = await readFileContent((0, node_path.join)(outputRoot, root.relativeDirPath, root.relativeFilePath));
|
|
76147
|
-
return new this({
|
|
76148
|
-
outputRoot,
|
|
76149
|
-
relativeDirPath: root.relativeDirPath,
|
|
76150
|
-
relativeFilePath: root.relativeFilePath,
|
|
76151
|
-
fileContent,
|
|
76152
|
-
validate,
|
|
76153
|
-
root: true
|
|
76154
|
-
});
|
|
76155
|
-
}
|
|
76156
|
-
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
76157
|
-
const { root } = this.getSettablePaths({ global });
|
|
76158
|
-
const frontmatter = rulesyncRule.getFrontmatter();
|
|
76159
|
-
const isRoot = frontmatter.root ?? false;
|
|
76160
|
-
const subprojectPath = frontmatter.agentsmd?.subprojectPath;
|
|
76161
|
-
if (!global && !isRoot && subprojectPath) return new this({
|
|
76162
|
-
outputRoot,
|
|
76163
|
-
relativeDirPath: (0, node_path.join)(subprojectPath),
|
|
76164
|
-
relativeFilePath: AGENTSMD_RULE_FILE_NAME,
|
|
76165
|
-
fileContent: rulesyncRule.getBody(),
|
|
76166
|
-
validate,
|
|
76167
|
-
root: false
|
|
76168
|
-
});
|
|
76169
|
-
return new this({
|
|
76170
|
-
outputRoot,
|
|
76171
|
-
relativeDirPath: root.relativeDirPath,
|
|
76172
|
-
relativeFilePath: root.relativeFilePath,
|
|
76173
|
-
fileContent: rulesyncRule.getBody(),
|
|
76174
|
-
validate,
|
|
76175
|
-
root: isRoot
|
|
76176
|
-
});
|
|
76177
|
-
}
|
|
76178
|
-
toRulesyncRule() {
|
|
76179
|
-
const subprojectPath = this.getSubprojectPath();
|
|
76180
|
-
if (subprojectPath === void 0) return this.toRulesyncRuleDefault();
|
|
76181
|
-
return this.toRulesyncRuleNestedAgentsmd({ subprojectPath });
|
|
76182
|
-
}
|
|
76183
|
-
validate() {
|
|
76184
|
-
return {
|
|
76185
|
-
success: true,
|
|
76186
|
-
error: null
|
|
76187
|
-
};
|
|
76188
|
-
}
|
|
76189
|
-
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
76190
|
-
const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === this.getFamily().globalDir);
|
|
76191
|
-
return new this({
|
|
76192
|
-
outputRoot,
|
|
76193
|
-
relativeDirPath,
|
|
76194
|
-
relativeFilePath,
|
|
76195
|
-
fileContent: "",
|
|
76196
|
-
validate: false,
|
|
76197
|
-
root: isRoot
|
|
76198
|
-
});
|
|
76199
|
-
}
|
|
76200
|
-
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
76201
|
-
return this.isTargetedByRulesyncRuleDefault({
|
|
76202
|
-
rulesyncRule,
|
|
76203
|
-
toolTarget: this.getFamily().toolTarget
|
|
76204
|
-
});
|
|
76205
|
-
}
|
|
76206
|
-
};
|
|
76207
|
-
//#endregion
|
|
76208
76428
|
//#region src/features/rules/dsh-rule.ts
|
|
76209
76429
|
/**
|
|
76210
76430
|
* DeepSeek Harness (`dsh`) rules.
|
|
@@ -76416,7 +76636,8 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
|
|
|
76416
76636
|
rulesyncRule,
|
|
76417
76637
|
validate,
|
|
76418
76638
|
rootPath: paths.root,
|
|
76419
|
-
nonRootPath: paths.nonRoot
|
|
76639
|
+
nonRootPath: paths.nonRoot,
|
|
76640
|
+
global
|
|
76420
76641
|
}));
|
|
76421
76642
|
}
|
|
76422
76643
|
toRulesyncRule() {
|
|
@@ -76883,7 +77104,8 @@ var KiloRule = class KiloRule extends ToolRule {
|
|
|
76883
77104
|
rulesyncRule,
|
|
76884
77105
|
validate,
|
|
76885
77106
|
rootPath: paths.root,
|
|
76886
|
-
nonRootPath: paths.nonRoot
|
|
77107
|
+
nonRootPath: paths.nonRoot,
|
|
77108
|
+
global
|
|
76887
77109
|
}));
|
|
76888
77110
|
}
|
|
76889
77111
|
toRulesyncRule() {
|
|
@@ -77387,7 +77609,8 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
|
|
|
77387
77609
|
rulesyncRule,
|
|
77388
77610
|
validate,
|
|
77389
77611
|
rootPath: paths.root,
|
|
77390
|
-
nonRootPath: paths.nonRoot
|
|
77612
|
+
nonRootPath: paths.nonRoot,
|
|
77613
|
+
global
|
|
77391
77614
|
}));
|
|
77392
77615
|
}
|
|
77393
77616
|
toRulesyncRule() {
|
|
@@ -78856,6 +79079,14 @@ var ZoocodeRule = class extends RooRule {
|
|
|
78856
79079
|
//#endregion
|
|
78857
79080
|
//#region src/features/rules/rules-processor.ts
|
|
78858
79081
|
const RulesProcessorToolTargetSchema = zod_mini.z.enum(rulesProcessorToolTargetTuple);
|
|
79082
|
+
/**
|
|
79083
|
+
* Normalizes a relative directory path for comparison: POSIX separators, no
|
|
79084
|
+
* `./` segments and no trailing slash, with the current directory as `"."`
|
|
79085
|
+
* (so `"./"`, `"a/"` and `"a"` compare the way `join()` groups them).
|
|
79086
|
+
*/
|
|
79087
|
+
const normalizeRelativeDir = (dir) => node_path.posix.normalize(toPosixPath(dir)).replace(/\/+$/, "") || ".";
|
|
79088
|
+
/** A tool rule's output path relative to its output root, for comparison. */
|
|
79089
|
+
const toolOutputPath = (toolRule) => node_path.posix.join(normalizeRelativeDir(toolRule.getRelativeDirPath()), toolRule.getRelativeFilePath());
|
|
78859
79090
|
const formatRulePaths = (rules) => rules.map((r) => (0, node_path.join)(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
|
|
78860
79091
|
const RulesFeatureOptionsSchema = zod_mini.z.looseObject({
|
|
78861
79092
|
ruleDiscoveryMode: zod_mini.z.optional(zod_mini.z.enum(["none", "explicit"])),
|
|
@@ -78937,7 +79168,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
78937
79168
|
meta: {
|
|
78938
79169
|
extension: "md",
|
|
78939
79170
|
supportsGlobal: true,
|
|
78940
|
-
ruleDiscoveryMode: "toon"
|
|
79171
|
+
ruleDiscoveryMode: "toon",
|
|
79172
|
+
ruleDiscoveryModeGlobal: "auto",
|
|
79173
|
+
sharedGlobalNonRootDir: true
|
|
78941
79174
|
}
|
|
78942
79175
|
}],
|
|
78943
79176
|
["antigravity-ide", {
|
|
@@ -79024,7 +79257,11 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
79024
79257
|
extension: "md",
|
|
79025
79258
|
supportsGlobal: true,
|
|
79026
79259
|
ruleDiscoveryMode: "auto",
|
|
79027
|
-
collisionPolicy: "fold"
|
|
79260
|
+
collisionPolicy: "fold",
|
|
79261
|
+
projectInstructionBudget: {
|
|
79262
|
+
bytes: CODEXCLI_PROJECT_DOC_MAX_BYTES,
|
|
79263
|
+
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)."
|
|
79264
|
+
}
|
|
79028
79265
|
}
|
|
79029
79266
|
}],
|
|
79030
79267
|
["commandcode", {
|
|
@@ -79482,9 +79719,61 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
79482
79719
|
toolRules,
|
|
79483
79720
|
factory
|
|
79484
79721
|
});
|
|
79722
|
+
this.warnForInstructionBudget({
|
|
79723
|
+
toolRules,
|
|
79724
|
+
meta
|
|
79725
|
+
});
|
|
79485
79726
|
return outputFiles;
|
|
79486
79727
|
}
|
|
79487
79728
|
/**
|
|
79729
|
+
* Warn when a generated project instruction chain exceeds the tool's byte
|
|
79730
|
+
* budget (`meta.projectInstructionBudget`).
|
|
79731
|
+
*
|
|
79732
|
+
* Codex CLI concatenates the project `AGENTS.md` files from the project root
|
|
79733
|
+
* down to the cwd and stops once their combined size reaches
|
|
79734
|
+
* `project_doc_max_bytes`, truncating the file that crosses it — silently, so
|
|
79735
|
+
* without this warning the rules past the limit simply never reach the model.
|
|
79736
|
+
* Each generated file is measured together with the generated files in its
|
|
79737
|
+
* ancestor directories, since that is the chain Codex loads while working
|
|
79738
|
+
* there. When the root file alone is over the budget every chain is, so only
|
|
79739
|
+
* the root is reported; likewise each over-budget nested chain is reported
|
|
79740
|
+
* once, at its shallowest directory. With several output roots the warning
|
|
79741
|
+
* names the root it refers to. An output root below the git root is measured
|
|
79742
|
+
* without the parent directories' files Codex would also load, so the
|
|
79743
|
+
* estimate can come out low there. The budget checked is the tool's default: a value
|
|
79744
|
+
* raised by hand in the tool's own config is not read. Global scope is not
|
|
79745
|
+
* checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
|
|
79746
|
+
* project budget.
|
|
79747
|
+
*/
|
|
79748
|
+
warnForInstructionBudget({ toolRules, meta }) {
|
|
79749
|
+
const budget = meta.projectInstructionBudget;
|
|
79750
|
+
if (budget === void 0 || this.global) return;
|
|
79751
|
+
const files = toolRules.map((rule) => ({
|
|
79752
|
+
dir: normalizeRelativeDir(rule.getRelativeDirPath()),
|
|
79753
|
+
path: toPosixPath((0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
|
|
79754
|
+
bytes: Buffer.byteLength(addTrailingNewline(rule.getFileContent()), "utf8")
|
|
79755
|
+
}));
|
|
79756
|
+
const isAncestorOrSelf = (ancestor, dir) => ancestor === "." || ancestor === dir || dir.startsWith(`${ancestor}/`);
|
|
79757
|
+
const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
|
|
79758
|
+
const rootFiles = files.filter(({ dir }) => dir === ".");
|
|
79759
|
+
const rootBytes = rootFiles.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
79760
|
+
if (rootBytes > budget.bytes) {
|
|
79761
|
+
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}`);
|
|
79762
|
+
return;
|
|
79763
|
+
}
|
|
79764
|
+
const reportedDirs = [];
|
|
79765
|
+
const nestedFiles = files.filter(({ dir }) => dir !== ".").toSorted((a, b) => a.dir.split("/").length - b.dir.split("/").length);
|
|
79766
|
+
for (const file of nestedFiles) {
|
|
79767
|
+
if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
|
|
79768
|
+
const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
|
|
79769
|
+
const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
|
|
79770
|
+
if (chainBytes > budget.bytes) {
|
|
79771
|
+
reportedDirs.push(file.dir);
|
|
79772
|
+
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}`);
|
|
79773
|
+
}
|
|
79774
|
+
}
|
|
79775
|
+
}
|
|
79776
|
+
/**
|
|
79488
79777
|
* Handle localRoot rules (only in non-global mode and when enabled). Mutates
|
|
79489
79778
|
* `toolRules` in place.
|
|
79490
79779
|
*/
|
|
@@ -79960,7 +80249,7 @@ var RulesProcessor = class extends FeatureProcessor {
|
|
|
79960
80249
|
*/
|
|
79961
80250
|
generateReferenceSectionFromMeta(meta, toolRules) {
|
|
79962
80251
|
switch (resolveRuleDiscoveryMode({
|
|
79963
|
-
defaultMode: meta.ruleDiscoveryMode,
|
|
80252
|
+
defaultMode: this.global && meta.ruleDiscoveryModeGlobal ? meta.ruleDiscoveryModeGlobal : meta.ruleDiscoveryMode,
|
|
79964
80253
|
options: this.featureOptions
|
|
79965
80254
|
})) {
|
|
79966
80255
|
case "toon": return this.generateToonReferencesSection(toolRules);
|
|
@@ -80156,14 +80445,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80156
80445
|
* decides for all of them: its `pi.contextFile` is copied onto the non-root
|
|
80157
80446
|
* rules, and the flag set only on a non-root rule is dropped with a warning.
|
|
80158
80447
|
*/
|
|
80159
|
-
alignPiContextFile(rules) {
|
|
80448
|
+
alignPiContextFile(rules, { warn = true } = {}) {
|
|
80160
80449
|
if (this.toolTarget !== "pi") return rules;
|
|
80161
80450
|
const factory = this.getFactory(this.toolTarget);
|
|
80162
80451
|
const targeted = rules.filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
|
|
80163
80452
|
const rootContextFile = targeted.some((rule) => rule.getFrontmatter().root === true && rule.getFrontmatter().pi?.contextFile === "override") ? "override" : void 0;
|
|
80164
80453
|
const mismatched = targeted.filter((rule) => rule.getFrontmatter().pi?.contextFile !== rootContextFile);
|
|
80165
80454
|
if (mismatched.length === 0) return rules;
|
|
80166
|
-
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)}`);
|
|
80455
|
+
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)}`);
|
|
80167
80456
|
const mismatchedSet = new Set(mismatched);
|
|
80168
80457
|
return rules.map((rule) => {
|
|
80169
80458
|
if (!mismatchedSet.has(rule)) return rule;
|
|
@@ -80217,14 +80506,72 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80217
80506
|
* duplication check the same way `loadRulesyncFiles`'s global-mode branch
|
|
80218
80507
|
* excludes it from `nonRootRules`: `generate` ignores `localRoot` entirely
|
|
80219
80508
|
* in global mode, so such a rule is never actually folded into the global
|
|
80220
|
-
* root output and warning about it here would be inaccurate.
|
|
80509
|
+
* root output and warning about it here would be inaccurate. The same goes
|
|
80510
|
+
* for a tool that writes `localRoot` to a dedicated local file.
|
|
80511
|
+
*
|
|
80512
|
+
* Non-root rules written to a file other than the root (nested
|
|
80513
|
+
* per-directory files, Pi's `APPEND_SYSTEM.md`) get a separate warning when
|
|
80514
|
+
* that file already exists and would import under a different name.
|
|
80221
80515
|
*/
|
|
80222
80516
|
async warnForFoldImportDuplicationRisk() {
|
|
80223
80517
|
const factory = this.getFactory(this.toolTarget);
|
|
80224
80518
|
if (factory.meta.collisionPolicy !== "fold") return;
|
|
80225
|
-
const
|
|
80226
|
-
|
|
80227
|
-
|
|
80519
|
+
const targetedRules = this.alignPiContextFile(await this.loadMergedRulesyncRules(), { warn: false }).filter((rule) => factory.class.isTargetedByRulesyncRule(rule));
|
|
80520
|
+
let includeLocalRoot = !this.global;
|
|
80521
|
+
try {
|
|
80522
|
+
includeLocalRoot &&= resolveIncludeLocalRoot(this.featureOptions);
|
|
80523
|
+
} catch {}
|
|
80524
|
+
const targetedNonRootRules = targetedRules.filter((rule) => !rule.getFrontmatter().root && !(rule.getFrontmatter().localRoot && (!includeLocalRoot || factory.meta.localRootMode === "separate-local-file")));
|
|
80525
|
+
const rootPaths = /* @__PURE__ */ new Set();
|
|
80526
|
+
const { root } = factory.class.getSettablePaths({ global: this.global });
|
|
80527
|
+
if (root) rootPaths.add(node_path.posix.join(normalizeRelativeDir(root.relativeDirPath), root.relativeFilePath));
|
|
80528
|
+
for (const rulesyncRule of targetedRules.filter((rule) => rule.getFrontmatter().root)) {
|
|
80529
|
+
const toolRule = this.toToolRuleForImportCheck({
|
|
80530
|
+
factory,
|
|
80531
|
+
rulesyncRule
|
|
80532
|
+
});
|
|
80533
|
+
rootPaths.add(toolOutputPath(toolRule));
|
|
80534
|
+
}
|
|
80535
|
+
const separateRules = [];
|
|
80536
|
+
const foldedRules = [];
|
|
80537
|
+
let localRootCounted = false;
|
|
80538
|
+
for (const rulesyncRule of targetedNonRootRules) {
|
|
80539
|
+
if (rulesyncRule.getFrontmatter().localRoot) {
|
|
80540
|
+
if (!localRootCounted) {
|
|
80541
|
+
foldedRules.push(rulesyncRule);
|
|
80542
|
+
localRootCounted = true;
|
|
80543
|
+
}
|
|
80544
|
+
continue;
|
|
80545
|
+
}
|
|
80546
|
+
const toolRule = this.toToolRuleForImportCheck({
|
|
80547
|
+
factory,
|
|
80548
|
+
rulesyncRule
|
|
80549
|
+
});
|
|
80550
|
+
if (rootPaths.size === 0 || rootPaths.has(toolOutputPath(toolRule))) foldedRules.push(rulesyncRule);
|
|
80551
|
+
else separateRules.push({
|
|
80552
|
+
toolRule,
|
|
80553
|
+
rulesyncRule
|
|
80554
|
+
});
|
|
80555
|
+
}
|
|
80556
|
+
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.`);
|
|
80557
|
+
const duplicated = [];
|
|
80558
|
+
for (const conversion of separateRules) {
|
|
80559
|
+
const { toolRule, rulesyncRule } = conversion;
|
|
80560
|
+
if (toolRule.toRulesyncRule().getRelativeFilePath().toLowerCase() === rulesyncRule.getRelativeFilePath().toLowerCase()) continue;
|
|
80561
|
+
if (await fileExists(toolRule.getFilePath())) duplicated.push(conversion);
|
|
80562
|
+
}
|
|
80563
|
+
if (duplicated.length > 0) {
|
|
80564
|
+
const fileNames = [...new Set(duplicated.map(({ toolRule }) => toolRule.getRelativeFilePath()))].join(", ");
|
|
80565
|
+
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.`);
|
|
80566
|
+
}
|
|
80567
|
+
}
|
|
80568
|
+
toToolRuleForImportCheck({ factory, rulesyncRule }) {
|
|
80569
|
+
return factory.class.fromRulesyncRule({
|
|
80570
|
+
outputRoot: this.outputRoot,
|
|
80571
|
+
rulesyncRule,
|
|
80572
|
+
validate: false,
|
|
80573
|
+
global: this.global
|
|
80574
|
+
});
|
|
80228
80575
|
}
|
|
80229
80576
|
/**
|
|
80230
80577
|
* Implementation of abstract method from FeatureProcessor
|
|
@@ -80421,7 +80768,8 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80421
80768
|
const nonRootToolRules = await (async () => {
|
|
80422
80769
|
if (!settablePaths.nonRoot) return [];
|
|
80423
80770
|
const nonRootOutputRoot = (0, node_path.join)(this.outputRoot, settablePaths.nonRoot.relativeDirPath);
|
|
80424
|
-
const
|
|
80771
|
+
const isSharedGlobalNonRootDir = this.global && factory.meta.sharedGlobalNonRootDir === true;
|
|
80772
|
+
const nonRootFilePaths = await findFilesByGlobs(`${isSharedGlobalNonRootDir ? "" : "**/"}*.${factory.meta.extension}`, { cwd: nonRootOutputRoot });
|
|
80425
80773
|
if (forDeletion) return buildDeletionRulesFromPaths(nonRootFilePaths, {
|
|
80426
80774
|
outputRootOverride: nonRootOutputRoot,
|
|
80427
80775
|
relativeDirPathOverride: settablePaths.nonRoot.relativeDirPath
|
|
@@ -80434,19 +80782,25 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80434
80782
|
if (!ok) this.logger.warn(`Skipping reserved Rovodev path under modular-rules (import): ${(0, node_path.join)(modularRootRelative, relativeFilePath)}`);
|
|
80435
80783
|
return ok;
|
|
80436
80784
|
}) : nonRootFilePaths).filter((filePath) => rootFileNameInSameDir === void 0 || (0, node_path.relative)(nonRootOutputRoot, filePath) !== rootFileNameInSameDir);
|
|
80437
|
-
return await Promise.all(nonRootPathsForImport.map((filePath) => {
|
|
80785
|
+
return (await Promise.all(nonRootPathsForImport.map(async (filePath) => {
|
|
80438
80786
|
const relativeFilePath = (0, node_path.relative)(nonRootOutputRoot, filePath);
|
|
80439
80787
|
checkPathTraversal({
|
|
80440
80788
|
relativePath: relativeFilePath,
|
|
80441
80789
|
intendedRootDir: nonRootOutputRoot
|
|
80442
80790
|
});
|
|
80443
|
-
|
|
80444
|
-
|
|
80445
|
-
|
|
80446
|
-
|
|
80447
|
-
|
|
80448
|
-
|
|
80449
|
-
|
|
80791
|
+
try {
|
|
80792
|
+
return await factory.class.fromFile({
|
|
80793
|
+
outputRoot: this.outputRoot,
|
|
80794
|
+
relativeDirPath: modularRootRelative,
|
|
80795
|
+
relativeFilePath,
|
|
80796
|
+
global: this.global
|
|
80797
|
+
});
|
|
80798
|
+
} catch (error) {
|
|
80799
|
+
if (!isSharedGlobalNonRootDir) throw error;
|
|
80800
|
+
this.logger.warn(`Skipping ${stripControlCharacters((0, node_path.join)(modularRootRelative, relativeFilePath))} for ${this.toolTarget}: ${formatError(error)}`);
|
|
80801
|
+
return;
|
|
80802
|
+
}
|
|
80803
|
+
}))).filter((rule) => rule !== void 0);
|
|
80450
80804
|
})();
|
|
80451
80805
|
this.logger.debug(`Found ${nonRootToolRules.length} non-root tool rule files`);
|
|
80452
80806
|
return [
|
|
@@ -80518,6 +80872,14 @@ As this project's AI coding tool, you must follow the additional conventions bel
|
|
|
80518
80872
|
function isPackagingToolTarget(toolTarget) {
|
|
80519
80873
|
return PACKAGING_TOOL_TARGETS.includes(toolTarget);
|
|
80520
80874
|
}
|
|
80875
|
+
/**
|
|
80876
|
+
* Packaging targets write into a plugin directory and support project scope
|
|
80877
|
+
* only. In global mode their output root would be the home directory, so
|
|
80878
|
+
* callers reject or skip them up front instead of checking all of $HOME.
|
|
80879
|
+
*/
|
|
80880
|
+
function formatPackagingTargetGlobalMessage(toolTarget) {
|
|
80881
|
+
return `Target '${toolTarget}' is a plugin packaging target and supports only project scope. Re-run without '--global'.`;
|
|
80882
|
+
}
|
|
80521
80883
|
async function assertPluginRootSafe(params) {
|
|
80522
80884
|
if (!isPackagingToolTarget(params.toolTarget)) return;
|
|
80523
80885
|
await assertDirectoryIfExists(params.outputRoot);
|
|
@@ -81437,14 +81799,17 @@ function warnUnsupportedTargets(params) {
|
|
|
81437
81799
|
} catch {
|
|
81438
81800
|
oppositeScopeTargets = [];
|
|
81439
81801
|
}
|
|
81440
|
-
for (const target of config.getTargets())
|
|
81441
|
-
|
|
81442
|
-
if (
|
|
81443
|
-
|
|
81444
|
-
|
|
81445
|
-
|
|
81446
|
-
|
|
81447
|
-
|
|
81802
|
+
for (const target of config.getTargets()) {
|
|
81803
|
+
if (config.getGlobal() && isPackagingToolTarget(target)) continue;
|
|
81804
|
+
if (!supportedTargets.includes(target) && config.getFeatures(target).includes(featureName)) {
|
|
81805
|
+
const simulateOption = SIMULATE_OPTION_MAP[featureName];
|
|
81806
|
+
if (simulateOption && simulatedTargets.includes(target)) logger.warn(`Target '${target}' only supports simulated '${featureName}'. Use '${simulateOption}' to enable it. Skipping.`);
|
|
81807
|
+
else if (oppositeScopeTargets.includes(target)) {
|
|
81808
|
+
const supportedScope = config.getGlobal() ? "project" : "global";
|
|
81809
|
+
const retry = config.getGlobal() ? "without '--global'" : "with '--global'";
|
|
81810
|
+
logger.warn(`Target '${target}' supports the feature '${featureName}' only in ${supportedScope} scope. Re-run ${retry}. Skipping.`);
|
|
81811
|
+
} else logger.warn(`Target '${target}' does not support the feature '${featureName}'. Skipping.`);
|
|
81812
|
+
}
|
|
81448
81813
|
}
|
|
81449
81814
|
}
|
|
81450
81815
|
/**
|
|
@@ -81697,10 +82062,16 @@ async function generate(params) {
|
|
|
81697
82062
|
const { config, logger } = params;
|
|
81698
82063
|
resetRootShadowingWarnings({ logger });
|
|
81699
82064
|
resetRunWarningState();
|
|
81700
|
-
for (const toolTarget of config.getTargets())
|
|
81701
|
-
toolTarget
|
|
81702
|
-
|
|
81703
|
-
|
|
82065
|
+
for (const toolTarget of config.getTargets()) {
|
|
82066
|
+
if (config.getGlobal() && isPackagingToolTarget(toolTarget)) {
|
|
82067
|
+
logger.warn(`${formatPackagingTargetGlobalMessage(toolTarget)} Skipping.`);
|
|
82068
|
+
continue;
|
|
82069
|
+
}
|
|
82070
|
+
for (const outputRoot of config.getOutputRoots(toolTarget)) await assertPluginRootSafe({
|
|
82071
|
+
toolTarget,
|
|
82072
|
+
outputRoot
|
|
82073
|
+
});
|
|
82074
|
+
}
|
|
81704
82075
|
await warnSkillSubagentNameCollisions({
|
|
81705
82076
|
config,
|
|
81706
82077
|
logger
|
|
@@ -81823,6 +82194,115 @@ function computeRootFileOwnership(params) {
|
|
|
81823
82194
|
}
|
|
81824
82195
|
return ownerByPath;
|
|
81825
82196
|
}
|
|
82197
|
+
function createSkippedRootFileCheck({ rootFileOwner }) {
|
|
82198
|
+
const skippedByRoot = /* @__PURE__ */ new Map();
|
|
82199
|
+
const emittedByRoot = /* @__PURE__ */ new Map();
|
|
82200
|
+
const emittedKey = (toolTarget, relativePath) => `${toolTarget}\0${relativePath}`;
|
|
82201
|
+
const loadOwnerToolFiles = async (processor) => {
|
|
82202
|
+
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
82203
|
+
return {
|
|
82204
|
+
toolFiles: rulesyncFiles.length > 0 ? await processor.convertRulesyncFilesToToolFiles(rulesyncFiles) : [],
|
|
82205
|
+
sourceLoadFailed: processor.hasRulesyncSourceLoadFailure()
|
|
82206
|
+
};
|
|
82207
|
+
};
|
|
82208
|
+
return {
|
|
82209
|
+
observe: ({ toolTarget, outputRoot, processor, toolFiles, skipFilePaths }) => {
|
|
82210
|
+
if (rootFileOwner.size === 0) return;
|
|
82211
|
+
for (const toolFile of toolFiles) {
|
|
82212
|
+
const relativePath = toolFile.getRelativePathFromCwd();
|
|
82213
|
+
if (!rootFileOwner.has(relativePath)) continue;
|
|
82214
|
+
const emitted = emittedByRoot.get(outputRoot) ?? /* @__PURE__ */ new Set();
|
|
82215
|
+
emittedByRoot.set(outputRoot, emitted);
|
|
82216
|
+
emitted.add(emittedKey(toolTarget, relativePath));
|
|
82217
|
+
if (!skipFilePaths.has(relativePath)) continue;
|
|
82218
|
+
const skipped = skippedByRoot.get(outputRoot) ?? /* @__PURE__ */ new Map();
|
|
82219
|
+
skippedByRoot.set(outputRoot, skipped);
|
|
82220
|
+
skipped.set(relativePath, {
|
|
82221
|
+
processor,
|
|
82222
|
+
toolFile
|
|
82223
|
+
});
|
|
82224
|
+
}
|
|
82225
|
+
},
|
|
82226
|
+
verify: async ({ isOwnerProcessed, createOwnerProcessor }) => {
|
|
82227
|
+
let count = 0;
|
|
82228
|
+
const paths = [];
|
|
82229
|
+
let sourceLoadFailed = false;
|
|
82230
|
+
for (const [outputRoot, skipped] of skippedByRoot) {
|
|
82231
|
+
const ownerOutputs = /* @__PURE__ */ new Map();
|
|
82232
|
+
for (const [relativePath, fallback] of skipped) {
|
|
82233
|
+
const owner = rootFileOwner.get(relativePath);
|
|
82234
|
+
if (owner === void 0) continue;
|
|
82235
|
+
if (emittedByRoot.get(outputRoot)?.has(emittedKey(owner, relativePath))) continue;
|
|
82236
|
+
let expected = fallback;
|
|
82237
|
+
if (!isOwnerProcessed(owner)) {
|
|
82238
|
+
if (!ownerOutputs.has(owner)) {
|
|
82239
|
+
const processor = await createOwnerProcessor({
|
|
82240
|
+
toolTarget: owner,
|
|
82241
|
+
outputRoot
|
|
82242
|
+
});
|
|
82243
|
+
if (processor === void 0) ownerOutputs.set(owner, void 0);
|
|
82244
|
+
else {
|
|
82245
|
+
const loaded = await loadOwnerToolFiles(processor);
|
|
82246
|
+
if (loaded.sourceLoadFailed) sourceLoadFailed = true;
|
|
82247
|
+
ownerOutputs.set(owner, {
|
|
82248
|
+
processor,
|
|
82249
|
+
toolFiles: loaded.toolFiles
|
|
82250
|
+
});
|
|
82251
|
+
}
|
|
82252
|
+
}
|
|
82253
|
+
const ownerOutput = ownerOutputs.get(owner);
|
|
82254
|
+
const ownerFile = ownerOutput?.toolFiles.find((file) => file.getRelativePathFromCwd() === relativePath && file.getFilePath() === fallback.toolFile.getFilePath());
|
|
82255
|
+
if (ownerOutput !== void 0 && ownerFile !== void 0) expected = {
|
|
82256
|
+
processor: ownerOutput.processor,
|
|
82257
|
+
toolFile: ownerFile
|
|
82258
|
+
};
|
|
82259
|
+
}
|
|
82260
|
+
const result = await expected.processor.writeAiFiles([expected.toolFile]);
|
|
82261
|
+
count += result.count;
|
|
82262
|
+
paths.push(...result.paths);
|
|
82263
|
+
}
|
|
82264
|
+
}
|
|
82265
|
+
return {
|
|
82266
|
+
count,
|
|
82267
|
+
paths,
|
|
82268
|
+
hasDiff: count > 0,
|
|
82269
|
+
sourceLoadFailed
|
|
82270
|
+
};
|
|
82271
|
+
}
|
|
82272
|
+
};
|
|
82273
|
+
}
|
|
82274
|
+
/**
|
|
82275
|
+
* The skills a full `rulesync generate` hands to the rules step: what the
|
|
82276
|
+
* skills step collects for every config-file target that generates skills, in
|
|
82277
|
+
* the same order. Simulated skill sections in a root file list these, so a
|
|
82278
|
+
* narrowed check needs them to rebuild another target's root file.
|
|
82279
|
+
*/
|
|
82280
|
+
async function loadConfigFileSkills({ config, logger }) {
|
|
82281
|
+
const supportedSkillsTargets = SkillsProcessor.getToolTargets({
|
|
82282
|
+
global: config.getGlobal(),
|
|
82283
|
+
includeSimulated: config.getSimulateSkills()
|
|
82284
|
+
});
|
|
82285
|
+
const skills = [];
|
|
82286
|
+
for (const toolTarget of (0, es_toolkit.intersection)(config.getConfigFileTargets(), supportedSkillsTargets)) {
|
|
82287
|
+
if (!config.getConfigFileFeatures(toolTarget).includes("skills")) continue;
|
|
82288
|
+
for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
82289
|
+
const processor = new SkillsProcessor({
|
|
82290
|
+
outputRoot: resolveToolOutputRoot({
|
|
82291
|
+
outputRoot,
|
|
82292
|
+
toolTarget,
|
|
82293
|
+
global: config.getGlobal()
|
|
82294
|
+
}),
|
|
82295
|
+
inputRoots: config.getInputRoots(),
|
|
82296
|
+
toolTarget,
|
|
82297
|
+
global: config.getGlobal(),
|
|
82298
|
+
dryRun: true,
|
|
82299
|
+
logger
|
|
82300
|
+
});
|
|
82301
|
+
for (const rulesyncDir of await processor.loadRulesyncDirs()) if (rulesyncDir instanceof RulesyncSkill) skills.push(rulesyncDir);
|
|
82302
|
+
}
|
|
82303
|
+
}
|
|
82304
|
+
return skills;
|
|
82305
|
+
}
|
|
81826
82306
|
async function generateRulesCore(params) {
|
|
81827
82307
|
const { config, logger, sweepPlan, skills } = params;
|
|
81828
82308
|
let totalCount = 0;
|
|
@@ -81838,32 +82318,47 @@ async function generateRulesCore(params) {
|
|
|
81838
82318
|
logger
|
|
81839
82319
|
});
|
|
81840
82320
|
const foldRootOverwriteWatch = createFoldRootOverwriteWatch({ logger });
|
|
82321
|
+
let fullRunSkills;
|
|
82322
|
+
const loadFullRunSkills = () => {
|
|
82323
|
+
fullRunSkills ??= loadConfigFileSkills({
|
|
82324
|
+
config,
|
|
82325
|
+
logger
|
|
82326
|
+
});
|
|
82327
|
+
return fullRunSkills;
|
|
82328
|
+
};
|
|
82329
|
+
const createProcessor = ({ toolTarget, outputRoot, featureOptions, skills: processorSkills }) => new RulesProcessor({
|
|
82330
|
+
outputRoot: resolveToolOutputRoot({
|
|
82331
|
+
outputRoot,
|
|
82332
|
+
toolTarget,
|
|
82333
|
+
global: config.getGlobal()
|
|
82334
|
+
}),
|
|
82335
|
+
inputRoots: config.getInputRoots(),
|
|
82336
|
+
toolTarget,
|
|
82337
|
+
global: config.getGlobal(),
|
|
82338
|
+
simulateCommands: config.getSimulateCommands(),
|
|
82339
|
+
simulateSubagents: config.getSimulateSubagents(),
|
|
82340
|
+
simulateSkills: config.getSimulateSkills(),
|
|
82341
|
+
language: config.getLanguage(),
|
|
82342
|
+
deriveSubprojectPathFromGlobs: config.getDeriveSubprojectPathFromGlobs(),
|
|
82343
|
+
skills: processorSkills,
|
|
82344
|
+
featureOptions,
|
|
82345
|
+
delete: config.getDelete(),
|
|
82346
|
+
dryRun: config.isPreviewMode(),
|
|
82347
|
+
logger
|
|
82348
|
+
});
|
|
81841
82349
|
const isCheck = config.getCheck();
|
|
81842
82350
|
const rootFileOwner = isCheck ? computeRootFileOwnership({
|
|
81843
|
-
targets: config.getConfigFileTargets(),
|
|
82351
|
+
targets: config.getConfigFileTargets().filter((target) => (toolTargets.includes(target) ? config.getFeatures(target) : config.getConfigFileFeatures(target)).includes("rules")),
|
|
81844
82352
|
global: config.getGlobal()
|
|
81845
82353
|
}) : /* @__PURE__ */ new Map();
|
|
82354
|
+
const skippedRootFileCheck = createSkippedRootFileCheck({ rootFileOwner });
|
|
81846
82355
|
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
81847
82356
|
if (!config.getFeatures(toolTarget).includes("rules")) continue;
|
|
81848
|
-
const processor =
|
|
81849
|
-
outputRoot: resolveToolOutputRoot({
|
|
81850
|
-
outputRoot,
|
|
81851
|
-
toolTarget,
|
|
81852
|
-
global: config.getGlobal()
|
|
81853
|
-
}),
|
|
81854
|
-
inputRoots: config.getInputRoots(),
|
|
82357
|
+
const processor = createProcessor({
|
|
81855
82358
|
toolTarget,
|
|
81856
|
-
|
|
81857
|
-
simulateCommands: config.getSimulateCommands(),
|
|
81858
|
-
simulateSubagents: config.getSimulateSubagents(),
|
|
81859
|
-
simulateSkills: config.getSimulateSkills(),
|
|
81860
|
-
language: config.getLanguage(),
|
|
81861
|
-
deriveSubprojectPathFromGlobs: config.getDeriveSubprojectPathFromGlobs(),
|
|
81862
|
-
skills,
|
|
82359
|
+
outputRoot,
|
|
81863
82360
|
featureOptions: config.getFeatureOptions(toolTarget, "rules"),
|
|
81864
|
-
|
|
81865
|
-
dryRun: config.isPreviewMode(),
|
|
81866
|
-
logger
|
|
82361
|
+
skills
|
|
81867
82362
|
});
|
|
81868
82363
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
81869
82364
|
const skipFilePaths = /* @__PURE__ */ new Set();
|
|
@@ -81876,16 +82371,39 @@ async function generateRulesCore(params) {
|
|
|
81876
82371
|
rulesyncFiles,
|
|
81877
82372
|
sweepPlan,
|
|
81878
82373
|
skipFilePaths: skipFilePaths.size > 0 ? skipFilePaths : void 0,
|
|
81879
|
-
onToolFiles: (toolFiles) =>
|
|
81880
|
-
|
|
81881
|
-
|
|
81882
|
-
|
|
82374
|
+
onToolFiles: (toolFiles) => {
|
|
82375
|
+
foldRootOverwriteWatch.observe({
|
|
82376
|
+
toolTarget,
|
|
82377
|
+
toolFiles
|
|
82378
|
+
});
|
|
82379
|
+
skippedRootFileCheck.observe({
|
|
82380
|
+
toolTarget,
|
|
82381
|
+
outputRoot,
|
|
82382
|
+
processor,
|
|
82383
|
+
toolFiles,
|
|
82384
|
+
skipFilePaths
|
|
82385
|
+
});
|
|
82386
|
+
}
|
|
81883
82387
|
});
|
|
81884
82388
|
totalCount += result.count;
|
|
81885
82389
|
allPaths.push(...result.paths);
|
|
81886
82390
|
if (result.hasDiff) hasDiff = true;
|
|
81887
82391
|
if (result.sourceLoadFailed) sourceLoadFailed = true;
|
|
81888
82392
|
}
|
|
82393
|
+
if (isCheck) {
|
|
82394
|
+
const skippedResult = await skippedRootFileCheck.verify({
|
|
82395
|
+
isOwnerProcessed: (owner) => toolTargets.includes(owner) && config.getFeatures(owner).includes("rules"),
|
|
82396
|
+
createOwnerProcessor: async (ownerParams) => supportedTargets.includes(ownerParams.toolTarget) && config.getOutputRoots(ownerParams.toolTarget).includes(ownerParams.outputRoot) ? createProcessor({
|
|
82397
|
+
...ownerParams,
|
|
82398
|
+
featureOptions: config.getConfigFileFeatureOptions(ownerParams.toolTarget, "rules"),
|
|
82399
|
+
skills: config.getSimulateSkills() && SkillsProcessor.getToolTargetsSimulated().includes(ownerParams.toolTarget) ? await loadFullRunSkills() : void 0
|
|
82400
|
+
}) : void 0
|
|
82401
|
+
});
|
|
82402
|
+
totalCount += skippedResult.count;
|
|
82403
|
+
allPaths.push(...skippedResult.paths);
|
|
82404
|
+
if (skippedResult.hasDiff) hasDiff = true;
|
|
82405
|
+
if (skippedResult.sourceLoadFailed) sourceLoadFailed = true;
|
|
82406
|
+
}
|
|
81889
82407
|
foldRootOverwriteWatch.report();
|
|
81890
82408
|
return {
|
|
81891
82409
|
count: totalCount,
|
|
@@ -82317,6 +82835,7 @@ function getToolOutputRoot({ config, tool }) {
|
|
|
82317
82835
|
async function importFromTool(params) {
|
|
82318
82836
|
const { config, tool, logger } = params;
|
|
82319
82837
|
resetRunWarningState();
|
|
82838
|
+
if (config.getGlobal() && isPackagingToolTarget(tool)) throw new Error(formatPackagingTargetGlobalMessage(tool));
|
|
82320
82839
|
await assertPluginRootSafe({
|
|
82321
82840
|
toolTarget: tool,
|
|
82322
82841
|
outputRoot: getToolOutputRoot({
|
|
@@ -82377,7 +82896,7 @@ async function importRulesCore(params) {
|
|
|
82377
82896
|
if (!config.getFeatures(tool).includes("rules")) return 0;
|
|
82378
82897
|
const global = config.getGlobal();
|
|
82379
82898
|
if (!RulesProcessor.getToolTargets({ global }).includes(tool)) return 0;
|
|
82380
|
-
const
|
|
82899
|
+
const baseParams = {
|
|
82381
82900
|
outputRoot: getToolOutputRoot({
|
|
82382
82901
|
config,
|
|
82383
82902
|
tool
|
|
@@ -82385,13 +82904,23 @@ async function importRulesCore(params) {
|
|
|
82385
82904
|
toolTarget: tool,
|
|
82386
82905
|
global,
|
|
82387
82906
|
logger
|
|
82388
|
-
}
|
|
82907
|
+
};
|
|
82908
|
+
const rulesProcessor = new RulesProcessor(baseParams);
|
|
82389
82909
|
const toolFiles = await rulesProcessor.loadToolFiles();
|
|
82390
82910
|
if (toolFiles.length === 0) {
|
|
82391
82911
|
logger.warn(`No rule files found for ${tool}. Skipping import.`);
|
|
82392
82912
|
return 0;
|
|
82393
82913
|
}
|
|
82394
|
-
|
|
82914
|
+
try {
|
|
82915
|
+
await new RulesProcessor({
|
|
82916
|
+
...baseParams,
|
|
82917
|
+
inputRoots: config.getInputRoots(),
|
|
82918
|
+
deriveSubprojectPathFromGlobs: config.getDeriveSubprojectPathFromGlobs(),
|
|
82919
|
+
featureOptions: config.getFeatureOptions(tool, "rules")
|
|
82920
|
+
}).warnForFoldImportDuplicationRisk();
|
|
82921
|
+
} catch (error) {
|
|
82922
|
+
logger.warn(`Skipped the ${tool} import duplication check: ${formatError(error)}`);
|
|
82923
|
+
}
|
|
82395
82924
|
const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
|
|
82396
82925
|
const { count: writtenCount } = await rulesProcessor.writeAiFiles(rulesyncFiles);
|
|
82397
82926
|
if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} rule files`);
|