rulesync 10.1.0 → 12.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 +2 -2
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-Dcj5HiOE.cjs → import-C0N0Lo_F.cjs} +388 -37
- package/dist/{import-B02AyWkQ.js → import-hM4jDZn1.js} +376 -25
- package/dist/import-hM4jDZn1.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-B02AyWkQ.js.map +0 -1
|
@@ -114,25 +114,25 @@ function formatError(error) {
|
|
|
114
114
|
}
|
|
115
115
|
//#endregion
|
|
116
116
|
//#region src/constants/rulesync-paths.ts
|
|
117
|
-
const { join: join$
|
|
117
|
+
const { join: join$252 } = node_path.posix;
|
|
118
118
|
const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
|
|
119
119
|
const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
|
|
120
120
|
const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
|
|
121
|
-
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$
|
|
122
|
-
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$
|
|
123
|
-
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$
|
|
124
|
-
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$
|
|
125
|
-
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$
|
|
126
|
-
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$
|
|
127
|
-
const RULESYNC_MCP_JSONC_RELATIVE_FILE_PATH = join$
|
|
128
|
-
const RULESYNC_HOOKS_JSONC_RELATIVE_FILE_PATH = join$
|
|
129
|
-
const RULESYNC_PERMISSIONS_JSONC_RELATIVE_FILE_PATH = join$
|
|
121
|
+
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "rules");
|
|
122
|
+
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "commands");
|
|
123
|
+
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "subagents");
|
|
124
|
+
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
|
|
125
|
+
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
|
|
126
|
+
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
|
|
127
|
+
const RULESYNC_MCP_JSONC_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
|
|
128
|
+
const RULESYNC_HOOKS_JSONC_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
|
|
129
|
+
const RULESYNC_PERMISSIONS_JSONC_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
|
|
130
130
|
const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
|
|
131
|
-
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$
|
|
131
|
+
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
|
|
132
132
|
const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
|
|
133
133
|
const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
|
|
134
|
-
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$
|
|
135
|
-
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$
|
|
134
|
+
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, "skills");
|
|
135
|
+
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$252(RULESYNC_SKILLS_RELATIVE_DIR_PATH, ".curated");
|
|
136
136
|
const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
|
|
137
137
|
const RULESYNC_MCP_FILE_NAME = "mcp.json";
|
|
138
138
|
const RULESYNC_HOOKS_FILE_NAME = "hooks.json";
|
|
@@ -289,6 +289,7 @@ const subagentsProcessorToolTargetTuple = [
|
|
|
289
289
|
"kiro-ide",
|
|
290
290
|
"opencode",
|
|
291
291
|
"qwencode",
|
|
292
|
+
"reasonix",
|
|
292
293
|
"roo",
|
|
293
294
|
"rovodev",
|
|
294
295
|
"takt",
|
|
@@ -5118,6 +5119,9 @@ const REASONIX_SETTINGS_FILE_NAME = "settings.json";
|
|
|
5118
5119
|
const REASONIX_COMMANDS_DIR_PATH = (0, node_path.join)(REASONIX_DIR, "commands");
|
|
5119
5120
|
const REASONIX_RULE_FILE_NAME = "REASONIX.md";
|
|
5120
5121
|
const REASONIX_SKILLS_DIR_PATH = (0, node_path.join)(REASONIX_DIR, "skills");
|
|
5122
|
+
const REASONIX_SUBAGENTS_DIR_PATH = REASONIX_SKILLS_DIR_PATH;
|
|
5123
|
+
const REASONIX_SUBAGENT_INVOCATION = "manual";
|
|
5124
|
+
const REASONIX_SUBAGENT_RUN_AS = "subagent";
|
|
5121
5125
|
//#endregion
|
|
5122
5126
|
//#region src/features/commands/reasonix-command.ts
|
|
5123
5127
|
/**
|
|
@@ -17454,7 +17458,11 @@ const CodexApprovalPolicySchema = zod_mini.z.enum([
|
|
|
17454
17458
|
]);
|
|
17455
17459
|
/**
|
|
17456
17460
|
* Codex CLI's classic sandbox mode. Serialized as a kebab-case string in
|
|
17457
|
-
* `.codex/config.toml`.
|
|
17461
|
+
* `.codex/config.toml`. Deprecated in favor of permission profiles
|
|
17462
|
+
* (`base_permission_profile`): Codex prioritizes these legacy sandbox keys
|
|
17463
|
+
* over permission profiles when both are present, so authoring one disables
|
|
17464
|
+
* the managed `[permissions.rulesync]` profile. Kept so existing configs
|
|
17465
|
+
* round-trip.
|
|
17458
17466
|
* @see https://learn.chatgpt.com/docs/config-file/config-reference
|
|
17459
17467
|
*/
|
|
17460
17468
|
const CodexSandboxModeSchema = zod_mini.z.enum([
|
|
@@ -17474,6 +17482,18 @@ const CodexApprovalsReviewerSchema = zod_mini.z.enum([
|
|
|
17474
17482
|
"guardian_subagent"
|
|
17475
17483
|
]);
|
|
17476
17484
|
/**
|
|
17485
|
+
* Codex CLI's built-in permission profiles that the managed
|
|
17486
|
+
* `[permissions.rulesync]` profile may extend. Codex ships three built-in
|
|
17487
|
+
* profiles (`:read-only`, `:workspace`, `:danger-full-access`; the leading
|
|
17488
|
+
* colon is reserved for built-ins), but `extends` rejects
|
|
17489
|
+
* `:danger-full-access` at config load time, so only the two extendable
|
|
17490
|
+
* baselines are accepted here. The value list is exported so the Codex CLI
|
|
17491
|
+
* translator derives its import-side baseline check from the same source.
|
|
17492
|
+
* @see https://learn.chatgpt.com/docs/permissions
|
|
17493
|
+
*/
|
|
17494
|
+
const CODEX_BASE_PERMISSION_PROFILES = [":read-only", ":workspace"];
|
|
17495
|
+
const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_PROFILES);
|
|
17496
|
+
/**
|
|
17477
17497
|
* Codex CLI-scoped permission override.
|
|
17478
17498
|
*
|
|
17479
17499
|
* Codex CLI's permission surface is richer than the canonical allow/ask/deny
|
|
@@ -17482,16 +17502,37 @@ const CodexApprovalsReviewerSchema = zod_mini.z.enum([
|
|
|
17482
17502
|
* override whose fields are written verbatim as top-level `.codex/config.toml`
|
|
17483
17503
|
* keys (the override wins per key; existing sibling keys the user set directly
|
|
17484
17504
|
* are preserved):
|
|
17505
|
+
* - `base_permission_profile` — the built-in profile the managed
|
|
17506
|
+
* `[permissions.rulesync]` profile extends (`:read-only` | `:workspace`).
|
|
17507
|
+
* Unlike the other keys it is not a top-level config key: it is emitted as
|
|
17508
|
+
* the profile's `extends` value. Defaults to `:workspace` when unspecified.
|
|
17485
17509
|
* - `approval_policy` — `untrusted` | `on-request` (legacy alias `on-failure`) |
|
|
17486
17510
|
* `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
|
|
17487
17511
|
* schema has required fields that are brittle to model as typed keys).
|
|
17488
|
-
*
|
|
17489
|
-
*
|
|
17490
|
-
*
|
|
17512
|
+
* Defaults to `on-request` when neither the override nor the existing
|
|
17513
|
+
* config sets it.
|
|
17514
|
+
* - `sandbox_mode` — **deprecated.** `read-only` | `workspace-write` |
|
|
17515
|
+
* `danger-full-access`, with the sibling `sandbox_workspace_write` table
|
|
17516
|
+
* (`network_access`, `writable_roots`, …). Codex has superseded the classic
|
|
17517
|
+
* sandbox system with permission profiles and prioritizes these legacy keys
|
|
17518
|
+
* over profiles when both are present, so setting them disables the managed
|
|
17519
|
+
* `[permissions.rulesync]` profile. Use `base_permission_profile` and the
|
|
17520
|
+
* shared `permission` block instead. Still accepted so existing configs
|
|
17521
|
+
* round-trip.
|
|
17491
17522
|
* - `apps` — per-app tool gating (`apps.<id>.tools.<tool>.approval_mode` /
|
|
17492
17523
|
* `.enabled`, `apps.<id>.default_tools_approval_mode`).
|
|
17493
17524
|
* - `approvals_reviewer` — the reviewer-approval surface (`user` | `auto_review`
|
|
17494
17525
|
* | `guardian_subagent`), or a table for the richer reviewer config.
|
|
17526
|
+
* Defaults to `auto_review` when neither the override nor the existing
|
|
17527
|
+
* config sets it.
|
|
17528
|
+
* - `git_write_rules` — whether the managed profile's `:workspace_roots` table
|
|
17529
|
+
* emits the default `.git` carve-outs (`".git/**" = "write"` with
|
|
17530
|
+
* `".git/config" = "read"` kept read-only as a security guard). Codex's
|
|
17531
|
+
* `:workspace` baseline makes `.git` read-only, which denies basic git
|
|
17532
|
+
* workflows (commit/stage writes to `.git/index`, `.git/objects`, refs,
|
|
17533
|
+
* logs), so the carve-outs are emitted by default. Defaults to `true`; only
|
|
17534
|
+
* an explicit `false` suppresses them. Like `base_permission_profile` it is
|
|
17535
|
+
* consumed by the profile builder, not written as a top-level config key.
|
|
17495
17536
|
*
|
|
17496
17537
|
* Two surfaces are deliberately NOT authorable here so the override can never
|
|
17497
17538
|
* clobber a feature-owned key: `mcp_servers.*` per-MCP gating is owned by the
|
|
@@ -17510,11 +17551,15 @@ const CodexApprovalsReviewerSchema = zod_mini.z.enum([
|
|
|
17510
17551
|
*/
|
|
17511
17552
|
const CodexcliPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
17512
17553
|
permission: zod_mini.z.optional(ToolScopedPermissionSchema),
|
|
17554
|
+
base_permission_profile: zod_mini.z.optional(CodexBasePermissionProfileSchema),
|
|
17513
17555
|
approval_policy: zod_mini.z.optional(zod_mini.z.union([CodexApprovalPolicySchema, zod_mini.z.looseObject({})])),
|
|
17556
|
+
/** @deprecated Superseded by `base_permission_profile` (permission profiles). */
|
|
17514
17557
|
sandbox_mode: zod_mini.z.optional(CodexSandboxModeSchema),
|
|
17558
|
+
/** @deprecated Superseded by `base_permission_profile` (permission profiles). */
|
|
17515
17559
|
sandbox_workspace_write: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
17516
17560
|
apps: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
17517
|
-
approvals_reviewer: zod_mini.z.optional(zod_mini.z.union([CodexApprovalsReviewerSchema, zod_mini.z.looseObject({})]))
|
|
17561
|
+
approvals_reviewer: zod_mini.z.optional(zod_mini.z.union([CodexApprovalsReviewerSchema, zod_mini.z.looseObject({})])),
|
|
17562
|
+
git_write_rules: zod_mini.z.optional(zod_mini.z.boolean())
|
|
17518
17563
|
});
|
|
17519
17564
|
/**
|
|
17520
17565
|
* Permissions configuration.
|
|
@@ -19483,14 +19528,16 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
|
|
|
19483
19528
|
const RULESYNC_PROFILE_NAME = "rulesync";
|
|
19484
19529
|
const CODEX_WORKSPACE_ROOTS_KEY = ":workspace_roots";
|
|
19485
19530
|
const CODEX_WORKSPACE_BASELINE = ":workspace";
|
|
19531
|
+
const CODEX_READ_ONLY_BASELINE = ":read-only";
|
|
19532
|
+
const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_BASE_PERMISSION_PROFILES);
|
|
19533
|
+
const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
|
|
19534
|
+
const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
|
|
19486
19535
|
const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
|
|
19487
|
-
const WORKSPACE_WIDE_WRITE_PATTERNS = /* @__PURE__ */ new Set([
|
|
19488
|
-
".",
|
|
19489
|
-
"./",
|
|
19490
|
-
"**",
|
|
19491
|
-
"./**"
|
|
19492
|
-
]);
|
|
19493
19536
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
19537
|
+
const CODEX_GIT_WRITE_RULES = {
|
|
19538
|
+
".git/**": "write",
|
|
19539
|
+
".git/config": "read"
|
|
19540
|
+
};
|
|
19494
19541
|
const GLOBAL_WILDCARD_DOMAIN = "*";
|
|
19495
19542
|
var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
19496
19543
|
static getSettablePaths(_options = {}) {
|
|
@@ -19579,6 +19626,7 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
19579
19626
|
domainsHadUnknown
|
|
19580
19627
|
});
|
|
19581
19628
|
const override = extractCodexcliOverride(table);
|
|
19629
|
+
if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
|
|
19582
19630
|
const result = Object.keys(override).length > 0 ? {
|
|
19583
19631
|
...config,
|
|
19584
19632
|
codexcli: override
|
|
@@ -19656,23 +19704,34 @@ function convertRulesyncToCodexProfile({ config, logger }) {
|
|
|
19656
19704
|
}
|
|
19657
19705
|
logger?.warn(`Codex CLI permissions support only read/edit/write/webfetch categories. Skipping: ${toolName}`);
|
|
19658
19706
|
}
|
|
19707
|
+
applyDefaultGitWriteRules({
|
|
19708
|
+
config,
|
|
19709
|
+
filesystem,
|
|
19710
|
+
workspaceRootFilesystem
|
|
19711
|
+
});
|
|
19659
19712
|
if (Object.keys(workspaceRootFilesystem).length > 0) {
|
|
19660
19713
|
if (typeof filesystem[CODEX_WORKSPACE_ROOTS_KEY] === "string") logger?.warn(`"${CODEX_WORKSPACE_ROOTS_KEY}" is set as a direct filesystem access rule in the permissions, but it will be overwritten by workspace-root rules. Consider removing the direct "${CODEX_WORKSPACE_ROOTS_KEY}" entry.`);
|
|
19661
19714
|
if (Object.keys(workspaceRootFilesystem).some((pattern) => pattern.includes("**"))) filesystem.glob_scan_max_depth = CODEX_GLOB_SCAN_MAX_DEPTH;
|
|
19662
19715
|
filesystem[CODEX_WORKSPACE_ROOTS_KEY] = workspaceRootFilesystem;
|
|
19663
19716
|
}
|
|
19664
|
-
const
|
|
19717
|
+
const basePermissionProfile = config.codexcli?.base_permission_profile ?? CODEX_WORKSPACE_BASELINE;
|
|
19665
19718
|
const hasAllowDomain = Object.values(domains).some((action) => action === "allow");
|
|
19666
19719
|
const network = Object.keys(domains).length > 0 ? {
|
|
19667
19720
|
...hasAllowDomain ? { enabled: true } : {},
|
|
19668
19721
|
domains
|
|
19669
19722
|
} : void 0;
|
|
19670
19723
|
return {
|
|
19671
|
-
|
|
19724
|
+
extends: basePermissionProfile,
|
|
19672
19725
|
filesystem,
|
|
19673
19726
|
...network ? { network } : {}
|
|
19674
19727
|
};
|
|
19675
19728
|
}
|
|
19729
|
+
function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem }) {
|
|
19730
|
+
if (config.codexcli?.git_write_rules === false) return;
|
|
19731
|
+
if (config.codexcli?.base_permission_profile === CODEX_READ_ONLY_BASELINE) return;
|
|
19732
|
+
if (typeof filesystem[CODEX_WORKSPACE_ROOTS_KEY] === "string") return;
|
|
19733
|
+
for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) workspaceRootFilesystem[pattern] ??= access;
|
|
19734
|
+
}
|
|
19676
19735
|
function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
|
|
19677
19736
|
const permission = {};
|
|
19678
19737
|
if (profile?.filesystem) {
|
|
@@ -19684,13 +19743,12 @@ function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
|
|
|
19684
19743
|
addRulesyncFilesystemRule(permission, pattern, access);
|
|
19685
19744
|
continue;
|
|
19686
19745
|
}
|
|
19687
|
-
if (isCodexFilesystemRuleTable(access)) for (const [nestedPattern, nestedAccess] of Object.entries(access))
|
|
19746
|
+
if (isCodexFilesystemRuleTable(access)) for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
|
|
19747
|
+
if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess) continue;
|
|
19748
|
+
addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
|
|
19749
|
+
}
|
|
19688
19750
|
}
|
|
19689
19751
|
}
|
|
19690
|
-
if (profile?.extends === CODEX_WORKSPACE_BASELINE) {
|
|
19691
|
-
permission.edit ??= {};
|
|
19692
|
-
permission.edit["."] ??= "allow";
|
|
19693
|
-
}
|
|
19694
19752
|
if (profile?.network && profile.network.enabled !== false) {
|
|
19695
19753
|
const networkEnabled = profile.network.enabled === true;
|
|
19696
19754
|
const importedEntries = (profile.network.domains ? Object.entries(profile.network.domains) : []).filter(([, value]) => value === "deny" || networkEnabled);
|
|
@@ -19729,10 +19787,29 @@ function toCodexProfile(value) {
|
|
|
19729
19787
|
};
|
|
19730
19788
|
}
|
|
19731
19789
|
function warnAboutPreservedProfileState({ existingProfile, newProfile, existingDomainsHadUnknown, logger }) {
|
|
19732
|
-
if (existingProfile
|
|
19790
|
+
if (existingProfile !== void 0 && existingProfile.extends !== newProfile.extends) logger?.warn(`Existing "extends" value "${existingProfile.extends ?? "(none)"}" will be replaced by Rulesync-managed "${newProfile.extends ?? "(none)"}".`);
|
|
19791
|
+
warnAboutPreservedNetworkState({
|
|
19792
|
+
existingProfile,
|
|
19793
|
+
newProfile,
|
|
19794
|
+
logger
|
|
19795
|
+
});
|
|
19796
|
+
if (existingDomainsHadUnknown) logger?.warn(`Existing "network.domains" contained unrecognized values. These entries were skipped and will not be imported.`);
|
|
19797
|
+
}
|
|
19798
|
+
function networkHasAllowDomain(network) {
|
|
19799
|
+
return Object.values(network?.domains ?? {}).some((action) => action === "allow");
|
|
19800
|
+
}
|
|
19801
|
+
function warnAboutNetworkEnabledState({ existingProfile, newProfile, logger }) {
|
|
19802
|
+
if (existingProfile?.network?.enabled !== void 0 && newProfile.network?.enabled === void 0 && !networkHasAllowDomain(existingProfile.network)) logger?.warn(`Preserving existing "network.enabled" from config. Review this value manually as it may enable network access beyond the Rulesync-managed domain rules.`);
|
|
19803
|
+
if (existingProfile?.network?.enabled === false && newProfile.network?.enabled === true) logger?.warn(`Existing "network.enabled = false" will be replaced by Rulesync-managed "enabled = true" because the canonical model contains an allow domain.`);
|
|
19804
|
+
}
|
|
19805
|
+
function warnAboutPreservedNetworkState({ existingProfile, newProfile, logger }) {
|
|
19733
19806
|
if (existingProfile?.network?.unix_sockets !== void 0) logger?.warn(`Preserving existing "network.unix_sockets" from config. Review these entries manually as they may grant broad system access.`);
|
|
19807
|
+
warnAboutNetworkEnabledState({
|
|
19808
|
+
existingProfile,
|
|
19809
|
+
newProfile,
|
|
19810
|
+
logger
|
|
19811
|
+
});
|
|
19734
19812
|
if (existingProfile?.network?.mode !== void 0) logger?.warn(`Preserving existing "network.mode" from config. Review this value manually as it may grant broader network access than the Rulesync-managed domain rules.`);
|
|
19735
|
-
if (existingDomainsHadUnknown) logger?.warn(`Existing "network.domains" contained unrecognized values. These entries were skipped and will not be imported.`);
|
|
19736
19813
|
}
|
|
19737
19814
|
const MANAGED_PROFILE_KEYS = /* @__PURE__ */ new Set([
|
|
19738
19815
|
"description",
|
|
@@ -19774,6 +19851,7 @@ function preserveUnmanagedProfileKeys({ rawExistingProfile, profile, logger }) {
|
|
|
19774
19851
|
function mergeWithExistingProfile({ newProfile, existingProfile }) {
|
|
19775
19852
|
if (!existingProfile) return newProfile;
|
|
19776
19853
|
const mergedNetwork = { ...newProfile.network };
|
|
19854
|
+
if (existingProfile.network?.enabled !== void 0 && mergedNetwork.enabled === void 0 && !networkHasAllowDomain(existingProfile.network)) mergedNetwork.enabled = existingProfile.network.enabled;
|
|
19777
19855
|
if (existingProfile.network?.mode !== void 0 && mergedNetwork.mode === void 0) mergedNetwork.mode = existingProfile.network.mode;
|
|
19778
19856
|
if (existingProfile.network?.unix_sockets !== void 0 && mergedNetwork.unix_sockets === void 0) mergedNetwork.unix_sockets = existingProfile.network.unix_sockets;
|
|
19779
19857
|
const hasNetwork = Object.keys(mergedNetwork).length > 0;
|
|
@@ -19823,20 +19901,26 @@ function isPlainObject(value) {
|
|
|
19823
19901
|
}
|
|
19824
19902
|
function computeCodexcliOverridePatch({ existing, override, logger }) {
|
|
19825
19903
|
const patch = {};
|
|
19826
|
-
if (!override) return patch;
|
|
19827
19904
|
const allowed = new Set(CODEXCLI_OVERRIDE_KEYS);
|
|
19828
|
-
for (const [key, value] of Object.entries(override)) {
|
|
19905
|
+
for (const [key, value] of Object.entries(override ?? {})) {
|
|
19906
|
+
if (key === "base_permission_profile" || key === "git_write_rules") continue;
|
|
19829
19907
|
if (!allowed.has(key)) {
|
|
19830
19908
|
logger?.warn(`Codex CLI permission override key "${key}" is not managed and was skipped. "permissions"/"default_permissions" are owned by the canonical permission model and "mcp_servers" gating by the MCP feature.`);
|
|
19831
19909
|
continue;
|
|
19832
19910
|
}
|
|
19833
19911
|
if (value === void 0) continue;
|
|
19912
|
+
if (key === "sandbox_mode" || key === "sandbox_workspace_write") logger?.warn(`Codex CLI permission override key "${key}" is deprecated. Codex prioritizes the legacy sandbox settings over permission profiles when both are present, so it disables the generated "${RULESYNC_PROFILE_NAME}" permissions profile. Use "base_permission_profile" and the shared "permission" block instead.`);
|
|
19834
19913
|
const existingValue = existing[key];
|
|
19835
19914
|
patch[key] = isPlainObject(existingValue) && isPlainObject(value) ? {
|
|
19836
19915
|
...existingValue,
|
|
19837
19916
|
...value
|
|
19838
19917
|
} : value;
|
|
19839
19918
|
}
|
|
19919
|
+
const defaults = {
|
|
19920
|
+
approval_policy: CODEX_DEFAULT_APPROVAL_POLICY,
|
|
19921
|
+
approvals_reviewer: CODEX_DEFAULT_APPROVALS_REVIEWER
|
|
19922
|
+
};
|
|
19923
|
+
for (const [key, value] of Object.entries(defaults)) if (patch[key] === void 0 && existing[key] === void 0) patch[key] = value;
|
|
19840
19924
|
return patch;
|
|
19841
19925
|
}
|
|
19842
19926
|
function extractCodexcliOverride(table) {
|
|
@@ -28459,6 +28543,24 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
|
|
|
28459
28543
|
global: loaded.global
|
|
28460
28544
|
});
|
|
28461
28545
|
}
|
|
28546
|
+
/**
|
|
28547
|
+
* Whether the skill directory belongs to the skills feature.
|
|
28548
|
+
*
|
|
28549
|
+
* `.reasonix/skills/` is shared with the subagents feature: a directory whose
|
|
28550
|
+
* SKILL.md declares `runAs: subagent` is a subagent profile, not a regular
|
|
28551
|
+
* skill, so it must be neither imported as a skill nor deleted as an orphan
|
|
28552
|
+
* skill. Directories without a readable/parsable SKILL.md keep the default
|
|
28553
|
+
* skills-feature ownership, matching the previous behavior for such dirs.
|
|
28554
|
+
*/
|
|
28555
|
+
static async isDirOwned({ outputRoot, relativeDirPath, dirName }) {
|
|
28556
|
+
const skillFilePath = (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME);
|
|
28557
|
+
try {
|
|
28558
|
+
const { frontmatter } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
|
|
28559
|
+
return frontmatter["runAs"] !== REASONIX_SUBAGENT_RUN_AS;
|
|
28560
|
+
} catch {
|
|
28561
|
+
return true;
|
|
28562
|
+
}
|
|
28563
|
+
}
|
|
28462
28564
|
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
28463
28565
|
return new ReasonixSkill({
|
|
28464
28566
|
outputRoot,
|
|
@@ -29757,6 +29859,11 @@ var SkillsProcessor = class extends DirFeatureProcessor {
|
|
|
29757
29859
|
for (const dirPath of dirPaths) {
|
|
29758
29860
|
const dirName = (0, node_path.basename)(dirPath);
|
|
29759
29861
|
if (seenDirNames.has(dirName)) continue;
|
|
29862
|
+
if (factory.class.isDirOwned && !await factory.class.isDirOwned({
|
|
29863
|
+
outputRoot: this.outputRoot,
|
|
29864
|
+
relativeDirPath: root,
|
|
29865
|
+
dirName
|
|
29866
|
+
})) continue;
|
|
29760
29867
|
seenDirNames.add(dirName);
|
|
29761
29868
|
loadEntries.push({
|
|
29762
29869
|
root,
|
|
@@ -29783,6 +29890,11 @@ var SkillsProcessor = class extends DirFeatureProcessor {
|
|
|
29783
29890
|
const dirPaths = await findFilesByGlobs((0, node_path.join)(skillsDirPath, "*"), { type: "dir" });
|
|
29784
29891
|
for (const dirPath of dirPaths) {
|
|
29785
29892
|
const dirName = (0, node_path.basename)(dirPath);
|
|
29893
|
+
if (factory.class.isDirOwned && !await factory.class.isDirOwned({
|
|
29894
|
+
outputRoot: this.outputRoot,
|
|
29895
|
+
relativeDirPath: root,
|
|
29896
|
+
dirName
|
|
29897
|
+
})) continue;
|
|
29786
29898
|
const toolSkill = factory.class.forDeletion({
|
|
29787
29899
|
outputRoot: this.outputRoot,
|
|
29788
29900
|
relativeDirPath: root,
|
|
@@ -33082,6 +33194,188 @@ var OpenCodeSubagent = class OpenCodeSubagent extends OpenCodeStyleSubagent {
|
|
|
33082
33194
|
}
|
|
33083
33195
|
};
|
|
33084
33196
|
//#endregion
|
|
33197
|
+
//#region src/features/subagents/reasonix-subagent.ts
|
|
33198
|
+
const ReasonixSubagentFrontmatterSchema = zod_mini.z.looseObject({
|
|
33199
|
+
name: zod_mini.z.string(),
|
|
33200
|
+
description: zod_mini.z.optional(zod_mini.z.string()),
|
|
33201
|
+
invocation: zod_mini.z.optional(zod_mini.z.string()),
|
|
33202
|
+
runAs: zod_mini.z.optional(zod_mini.z.string()),
|
|
33203
|
+
model: zod_mini.z.optional(zod_mini.z.string()),
|
|
33204
|
+
effort: zod_mini.z.optional(zod_mini.z.string()),
|
|
33205
|
+
"allowed-tools": zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
|
|
33206
|
+
color: zod_mini.z.optional(zod_mini.z.string())
|
|
33207
|
+
});
|
|
33208
|
+
/**
|
|
33209
|
+
* Represents a DeepSeek-Reasonix subagent profile.
|
|
33210
|
+
*
|
|
33211
|
+
* Reasonix discovers subagents as directory-layout Skills (`<name>/SKILL.md`)
|
|
33212
|
+
* under `.reasonix/skills/` (project) and `~/.reasonix/skills/` (global); the
|
|
33213
|
+
* global scope is served by the processor supplying the home directory as
|
|
33214
|
+
* outputRoot, so the relative directory path is identical for both scopes. A
|
|
33215
|
+
* subagent is a Skill whose frontmatter declares `invocation: manual` and
|
|
33216
|
+
* `runAs: subagent`.
|
|
33217
|
+
*
|
|
33218
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/SUBAGENT_PROFILES.md
|
|
33219
|
+
*/
|
|
33220
|
+
var ReasonixSubagent = class ReasonixSubagent extends ToolSubagent {
|
|
33221
|
+
frontmatter;
|
|
33222
|
+
body;
|
|
33223
|
+
constructor({ frontmatter, body, fileContent, ...rest }) {
|
|
33224
|
+
if (rest.validate !== false) {
|
|
33225
|
+
const result = ReasonixSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
33226
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
33227
|
+
}
|
|
33228
|
+
super({
|
|
33229
|
+
...rest,
|
|
33230
|
+
fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
|
|
33231
|
+
});
|
|
33232
|
+
this.frontmatter = frontmatter;
|
|
33233
|
+
this.body = body;
|
|
33234
|
+
}
|
|
33235
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
33236
|
+
return { relativeDirPath: REASONIX_SUBAGENTS_DIR_PATH };
|
|
33237
|
+
}
|
|
33238
|
+
getFrontmatter() {
|
|
33239
|
+
return this.frontmatter;
|
|
33240
|
+
}
|
|
33241
|
+
getBody() {
|
|
33242
|
+
return this.body;
|
|
33243
|
+
}
|
|
33244
|
+
toRulesyncSubagent() {
|
|
33245
|
+
const { name, description, invocation: _invocation, runAs: _runAs, ...restFields } = this.frontmatter;
|
|
33246
|
+
const reasonixSection = { ...restFields };
|
|
33247
|
+
const rulesyncFrontmatter = {
|
|
33248
|
+
targets: ["*"],
|
|
33249
|
+
name,
|
|
33250
|
+
description,
|
|
33251
|
+
...Object.keys(reasonixSection).length > 0 && { reasonix: reasonixSection }
|
|
33252
|
+
};
|
|
33253
|
+
return new RulesyncSubagent({
|
|
33254
|
+
outputRoot: this.getOutputRoot(),
|
|
33255
|
+
frontmatter: rulesyncFrontmatter,
|
|
33256
|
+
body: this.body,
|
|
33257
|
+
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
33258
|
+
relativeFilePath: `${this.getSubagentName()}.md`,
|
|
33259
|
+
validate: true
|
|
33260
|
+
});
|
|
33261
|
+
}
|
|
33262
|
+
/**
|
|
33263
|
+
* Derive the subagent name from this instance's relative file path.
|
|
33264
|
+
*
|
|
33265
|
+
* The tool-side layout is `<name>/SKILL.md`, so the name is the parent
|
|
33266
|
+
* directory of the file. If the path is unexpectedly flat (e.g. a legacy
|
|
33267
|
+
* `<name>.md`), fall back to the basename without extension.
|
|
33268
|
+
*/
|
|
33269
|
+
getSubagentName() {
|
|
33270
|
+
const relativeFilePath = this.getRelativeFilePath();
|
|
33271
|
+
const dir = (0, node_path.dirname)(relativeFilePath);
|
|
33272
|
+
if (dir && dir !== ".") return (0, node_path.basename)(dir);
|
|
33273
|
+
return (0, node_path.basename)(relativeFilePath, (0, node_path.extname)(relativeFilePath));
|
|
33274
|
+
}
|
|
33275
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
33276
|
+
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
33277
|
+
const reasonixSection = this.filterToolSpecificSection(rulesyncFrontmatter.reasonix ?? {}, ["name", "description"]);
|
|
33278
|
+
const rawReasonixFrontmatter = {
|
|
33279
|
+
name: rulesyncFrontmatter.name,
|
|
33280
|
+
description: rulesyncFrontmatter.description,
|
|
33281
|
+
...reasonixSection,
|
|
33282
|
+
invocation: REASONIX_SUBAGENT_INVOCATION,
|
|
33283
|
+
runAs: REASONIX_SUBAGENT_RUN_AS
|
|
33284
|
+
};
|
|
33285
|
+
const result = ReasonixSubagentFrontmatterSchema.safeParse(rawReasonixFrontmatter);
|
|
33286
|
+
if (!result.success) throw new Error(`Invalid reasonix subagent frontmatter in ${rulesyncSubagent.getRelativeFilePath()}: ${formatError(result.error)}`);
|
|
33287
|
+
const reasonixFrontmatter = result.data;
|
|
33288
|
+
const body = rulesyncSubagent.getBody();
|
|
33289
|
+
const fileContent = stringifyFrontmatter(body, reasonixFrontmatter);
|
|
33290
|
+
const paths = this.getSettablePaths({ global });
|
|
33291
|
+
const subagentName = (0, node_path.basename)(rulesyncSubagent.getRelativeFilePath(), (0, node_path.extname)(rulesyncSubagent.getRelativeFilePath()));
|
|
33292
|
+
return new ReasonixSubagent({
|
|
33293
|
+
outputRoot,
|
|
33294
|
+
frontmatter: reasonixFrontmatter,
|
|
33295
|
+
body,
|
|
33296
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33297
|
+
relativeFilePath: (0, node_path.join)(subagentName, SKILL_FILE_NAME),
|
|
33298
|
+
fileContent,
|
|
33299
|
+
validate,
|
|
33300
|
+
global
|
|
33301
|
+
});
|
|
33302
|
+
}
|
|
33303
|
+
validate() {
|
|
33304
|
+
if (!this.frontmatter) return {
|
|
33305
|
+
success: true,
|
|
33306
|
+
error: null
|
|
33307
|
+
};
|
|
33308
|
+
const result = ReasonixSubagentFrontmatterSchema.safeParse(this.frontmatter);
|
|
33309
|
+
if (result.success) return {
|
|
33310
|
+
success: true,
|
|
33311
|
+
error: null
|
|
33312
|
+
};
|
|
33313
|
+
else return {
|
|
33314
|
+
success: false,
|
|
33315
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
33316
|
+
};
|
|
33317
|
+
}
|
|
33318
|
+
static isTargetedByRulesyncSubagent(rulesyncSubagent) {
|
|
33319
|
+
return this.isTargetedByRulesyncSubagentDefault({
|
|
33320
|
+
rulesyncSubagent,
|
|
33321
|
+
toolTarget: "reasonix"
|
|
33322
|
+
});
|
|
33323
|
+
}
|
|
33324
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
33325
|
+
const paths = this.getSettablePaths({ global });
|
|
33326
|
+
const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
33327
|
+
const fileContent = await readFileContent(filePath);
|
|
33328
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
|
|
33329
|
+
const result = ReasonixSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
33330
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
33331
|
+
return new ReasonixSubagent({
|
|
33332
|
+
outputRoot,
|
|
33333
|
+
relativeDirPath: paths.relativeDirPath,
|
|
33334
|
+
relativeFilePath,
|
|
33335
|
+
frontmatter: result.data,
|
|
33336
|
+
body: content.trim(),
|
|
33337
|
+
fileContent,
|
|
33338
|
+
validate,
|
|
33339
|
+
global
|
|
33340
|
+
});
|
|
33341
|
+
}
|
|
33342
|
+
/**
|
|
33343
|
+
* Whether the SKILL.md at the given path is a subagent profile.
|
|
33344
|
+
*
|
|
33345
|
+
* `.reasonix/skills/` is shared with the skills feature: a regular skill and
|
|
33346
|
+
* a subagent profile differ only by their frontmatter markers. Only files
|
|
33347
|
+
* carrying `runAs: subagent` belong to this feature, so regular skills are
|
|
33348
|
+
* neither imported as subagents nor deleted as orphans by the subagents
|
|
33349
|
+
* feature. `runAs` alone is checked (not `invocation`) because it is the
|
|
33350
|
+
* marker that switches the execution mode; generation always emits both.
|
|
33351
|
+
* Unreadable or unparsable files are treated as not owned, erring on the
|
|
33352
|
+
* side of leaving foreign files untouched.
|
|
33353
|
+
*/
|
|
33354
|
+
static async isFileOwned({ outputRoot, relativeDirPath, relativeFilePath }) {
|
|
33355
|
+
const filePath = (0, node_path.join)(outputRoot, relativeDirPath, relativeFilePath);
|
|
33356
|
+
try {
|
|
33357
|
+
const { frontmatter } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
33358
|
+
return frontmatter["runAs"] === REASONIX_SUBAGENT_RUN_AS;
|
|
33359
|
+
} catch {
|
|
33360
|
+
return false;
|
|
33361
|
+
}
|
|
33362
|
+
}
|
|
33363
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
33364
|
+
return new ReasonixSubagent({
|
|
33365
|
+
outputRoot,
|
|
33366
|
+
relativeDirPath,
|
|
33367
|
+
relativeFilePath,
|
|
33368
|
+
frontmatter: {
|
|
33369
|
+
name: "",
|
|
33370
|
+
description: ""
|
|
33371
|
+
},
|
|
33372
|
+
body: "",
|
|
33373
|
+
fileContent: "",
|
|
33374
|
+
validate: false
|
|
33375
|
+
});
|
|
33376
|
+
}
|
|
33377
|
+
};
|
|
33378
|
+
//#endregion
|
|
33085
33379
|
//#region src/features/subagents/roo-subagent.ts
|
|
33086
33380
|
/**
|
|
33087
33381
|
* Default tool groups assigned to a generated mode when the rulesync source
|
|
@@ -33716,6 +34010,14 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
|
|
|
33716
34010
|
filePattern: "*.md"
|
|
33717
34011
|
}
|
|
33718
34012
|
}],
|
|
34013
|
+
["reasonix", {
|
|
34014
|
+
class: ReasonixSubagent,
|
|
34015
|
+
meta: {
|
|
34016
|
+
supportsSimulated: false,
|
|
34017
|
+
supportsGlobal: true,
|
|
34018
|
+
filePattern: (0, node_path.join)("*", "SKILL.md")
|
|
34019
|
+
}
|
|
34020
|
+
}],
|
|
33719
34021
|
["roo", {
|
|
33720
34022
|
class: RooSubagent,
|
|
33721
34023
|
meta: {
|
|
@@ -33868,8 +34170,17 @@ var SubagentsProcessor = class extends FeatureProcessor {
|
|
|
33868
34170
|
const baseDir = (0, node_path.join)(this.outputRoot, dirPath);
|
|
33869
34171
|
const subagentFilePaths = await findFilesByGlobs((0, node_path.join)(baseDir, factory.meta.filePattern));
|
|
33870
34172
|
const toRelativeFilePath = (path) => (0, node_path.relative)(baseDir, path);
|
|
34173
|
+
let ownedFilePaths = subagentFilePaths;
|
|
34174
|
+
if (factory.class.isFileOwned) {
|
|
34175
|
+
const ownership = await Promise.all(subagentFilePaths.map((path) => factory.class.isFileOwned({
|
|
34176
|
+
outputRoot: this.outputRoot,
|
|
34177
|
+
relativeDirPath: dirPath,
|
|
34178
|
+
relativeFilePath: toRelativeFilePath(path)
|
|
34179
|
+
})));
|
|
34180
|
+
ownedFilePaths = subagentFilePaths.filter((_, index) => ownership[index]);
|
|
34181
|
+
}
|
|
33871
34182
|
if (forDeletion) {
|
|
33872
|
-
toolSubagents.push(...
|
|
34183
|
+
toolSubagents.push(...ownedFilePaths.map((path) => factory.class.forDeletion({
|
|
33873
34184
|
outputRoot: this.outputRoot,
|
|
33874
34185
|
relativeDirPath: dirPath,
|
|
33875
34186
|
relativeFilePath: toRelativeFilePath(path),
|
|
@@ -33877,7 +34188,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
|
|
|
33877
34188
|
})).filter((subagent) => subagent.isDeletable()));
|
|
33878
34189
|
continue;
|
|
33879
34190
|
}
|
|
33880
|
-
const loaded = await Promise.all(
|
|
34191
|
+
const loaded = await Promise.all(ownedFilePaths.map((path) => factory.class.fromFile({
|
|
33881
34192
|
outputRoot: this.outputRoot,
|
|
33882
34193
|
relativeDirPath: dirPath,
|
|
33883
34194
|
relativeFilePath: toRelativeFilePath(path),
|
|
@@ -39696,11 +40007,51 @@ const GENERATION_STEP_GRAPH = [
|
|
|
39696
40007
|
}
|
|
39697
40008
|
];
|
|
39698
40009
|
/**
|
|
40010
|
+
* Warn when a rulesync skill and a rulesync subagent share a name for a tool
|
|
40011
|
+
* that emits both features into the same directory (e.g. Reasonix, where both
|
|
40012
|
+
* write `<name>/SKILL.md` under `.reasonix/skills/`). The colliding outputs
|
|
40013
|
+
* target the same on-disk file, so whichever generation step runs later
|
|
40014
|
+
* silently overwrites the other's file.
|
|
40015
|
+
*/
|
|
40016
|
+
async function warnSkillSubagentNameCollisions(params) {
|
|
40017
|
+
const { config, logger } = params;
|
|
40018
|
+
const global = config.getGlobal();
|
|
40019
|
+
for (const toolTarget of config.getTargets()) {
|
|
40020
|
+
const features = config.getFeatures(toolTarget);
|
|
40021
|
+
if (!features.includes("skills") || !features.includes("subagents")) continue;
|
|
40022
|
+
if (!SubagentsProcessor.getToolTargets({ global }).includes(toolTarget) || !SkillsProcessor.getToolTargets({ global }).includes(toolTarget)) continue;
|
|
40023
|
+
const subagentFactory = SubagentsProcessor.getFactory(toolTarget);
|
|
40024
|
+
const skillFactory = SkillsProcessor.getFactory(toolTarget);
|
|
40025
|
+
if (!subagentFactory || !skillFactory) continue;
|
|
40026
|
+
const subagentsDirPath = subagentFactory.class.getSettablePaths({ global }).relativeDirPath;
|
|
40027
|
+
if (subagentsDirPath !== skillFactory.class.getSettablePaths({ global }).relativeDirPath) continue;
|
|
40028
|
+
const subagentsProcessor = new SubagentsProcessor({
|
|
40029
|
+
inputRoot: config.getInputRoot(),
|
|
40030
|
+
toolTarget,
|
|
40031
|
+
global,
|
|
40032
|
+
logger
|
|
40033
|
+
});
|
|
40034
|
+
const subagentNames = new Set((await subagentsProcessor.loadRulesyncFiles()).filter((file) => file instanceof RulesyncSubagent).filter((file) => subagentFactory.class.isTargetedByRulesyncSubagent(file)).map((file) => (0, node_path.basename)(file.getRelativeFilePath(), (0, node_path.extname)(file.getRelativeFilePath()))));
|
|
40035
|
+
if (subagentNames.size === 0) continue;
|
|
40036
|
+
const skillNames = (await new SkillsProcessor({
|
|
40037
|
+
inputRoot: config.getInputRoot(),
|
|
40038
|
+
toolTarget,
|
|
40039
|
+
global,
|
|
40040
|
+
logger
|
|
40041
|
+
}).loadRulesyncDirs()).filter((dir) => dir instanceof RulesyncSkill).filter((skill) => skillFactory.class.isTargetedByRulesyncSkill(skill)).map((skill) => skill.getDirName());
|
|
40042
|
+
for (const name of skillNames) if (subagentNames.has(name)) logger.warn(`Skill "${name}" and subagent "${name}" both target '${toolTarget}' and write the same path '${(0, node_path.join)(subagentsDirPath, name)}'; the later generation step overwrites the other's output. Rename one of them or narrow their targets.`);
|
|
40043
|
+
}
|
|
40044
|
+
}
|
|
40045
|
+
/**
|
|
39699
40046
|
* Generate configuration files for AI tools.
|
|
39700
40047
|
* @throws Error if generation fails
|
|
39701
40048
|
*/
|
|
39702
40049
|
async function generate(params) {
|
|
39703
40050
|
const { config, logger } = params;
|
|
40051
|
+
await warnSkillSubagentNameCollisions({
|
|
40052
|
+
config,
|
|
40053
|
+
logger
|
|
40054
|
+
});
|
|
39704
40055
|
let skillsResult;
|
|
39705
40056
|
const runners = {
|
|
39706
40057
|
ignore: () => generateIgnoreCore({
|