rulesync 12.0.0 → 13.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.
@@ -37,6 +37,7 @@ let gray_matter = require("gray-matter");
37
37
  gray_matter = __toESM(gray_matter, 1);
38
38
  let js_yaml = require("js-yaml");
39
39
  let es_toolkit_object = require("es-toolkit/object");
40
+ let node_crypto = require("node:crypto");
40
41
  let _toon_format_toon = require("@toon-format/toon");
41
42
  //#region src/types/features.ts
42
43
  const ALL_FEATURES = [
@@ -47,7 +48,8 @@ const ALL_FEATURES = [
47
48
  "commands",
48
49
  "skills",
49
50
  "hooks",
50
- "permissions"
51
+ "permissions",
52
+ "checks"
51
53
  ];
52
54
  const ALL_FEATURES_WITH_WILDCARD = [...ALL_FEATURES, "*"];
53
55
  const FeatureSchema = zod_mini.z.enum(ALL_FEATURES);
@@ -114,25 +116,26 @@ function formatError(error) {
114
116
  }
115
117
  //#endregion
116
118
  //#region src/constants/rulesync-paths.ts
117
- const { join: join$252 } = node_path.posix;
119
+ const { join: join$256 } = node_path.posix;
118
120
  const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
119
121
  const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
120
122
  const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
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");
123
+ const RULESYNC_RULES_RELATIVE_DIR_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "rules");
124
+ const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "commands");
125
+ const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "subagents");
126
+ const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "checks");
127
+ const RULESYNC_MCP_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
128
+ const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
129
+ const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
130
+ const RULESYNC_MCP_JSONC_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
131
+ const RULESYNC_HOOKS_JSONC_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
132
+ const RULESYNC_PERMISSIONS_JSONC_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
130
133
  const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
131
- const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$252(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
134
+ const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
132
135
  const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
133
136
  const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
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");
137
+ const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$256(RULESYNC_RELATIVE_DIR_PATH, "skills");
138
+ const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$256(RULESYNC_SKILLS_RELATIVE_DIR_PATH, ".curated");
136
139
  const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
137
140
  const RULESYNC_MCP_FILE_NAME = "mcp.json";
138
141
  const RULESYNC_HOOKS_FILE_NAME = "hooks.json";
@@ -386,6 +389,7 @@ const permissionsProcessorToolTargetTuple = [
386
389
  "warp",
387
390
  "zed"
388
391
  ];
392
+ const checksProcessorToolTargetTuple = ["amp"];
389
393
  //#endregion
390
394
  //#region src/types/tool-targets.ts
391
395
  const ALL_TOOL_TARGETS = [...new Set([
@@ -396,7 +400,8 @@ const ALL_TOOL_TARGETS = [...new Set([
396
400
  subagentsProcessorToolTargetTuple,
397
401
  skillsProcessorToolTargetTuple,
398
402
  hooksProcessorToolTargetTuple,
399
- permissionsProcessorToolTargetTuple
403
+ permissionsProcessorToolTargetTuple,
404
+ checksProcessorToolTargetTuple
400
405
  ].flat())];
401
406
  const ALL_TOOL_TARGETS_WITH_WILDCARD = [...ALL_TOOL_TARGETS, "*"];
402
407
  const ToolTargetSchema = zod_mini.z.enum(ALL_TOOL_TARGETS);
@@ -1470,7 +1475,7 @@ function isPlainObject$1(value) {
1470
1475
  /**
1471
1476
  * Type guard to check if a value is an array of strings.
1472
1477
  */
1473
- function isStringArray(value) {
1478
+ function isStringArray$1(value) {
1474
1479
  return Array.isArray(value) && value.every((item) => typeof item === "string");
1475
1480
  }
1476
1481
  //#endregion
@@ -1718,13 +1723,17 @@ var FeatureProcessor = class {
1718
1723
  }
1719
1724
  };
1720
1725
  //#endregion
1721
- //#region src/constants/agentsmd-paths.ts
1722
- const AGENTSMD_DIR = ".agents";
1723
- const AGENTSMD_MEMORIES_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "memories");
1724
- const AGENTSMD_COMMANDS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "commands");
1725
- const AGENTSMD_SKILLS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "skills");
1726
- const AGENTSMD_SUBAGENTS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "subagents");
1727
- const AGENTSMD_RULE_FILE_NAME = "AGENTS.md";
1726
+ //#region src/constants/amp-paths.ts
1727
+ const AMP_DIR = ".amp";
1728
+ const AMP_GLOBAL_DIR = (0, node_path.join)(".config", "amp");
1729
+ const AMP_AGENTS_DIR = ".agents";
1730
+ const AMP_SKILLS_PROJECT_DIR = (0, node_path.join)(AMP_AGENTS_DIR, "skills");
1731
+ const AMP_SKILLS_GLOBAL_DIR = (0, node_path.join)(".config", "agents", "skills");
1732
+ const AMP_CHECKS_PROJECT_DIR = (0, node_path.join)(AMP_AGENTS_DIR, "checks");
1733
+ const AMP_CHECKS_GLOBAL_DIR = (0, node_path.join)(AMP_GLOBAL_DIR, "checks");
1734
+ const AMP_RULE_FILE_NAME = "AGENTS.md";
1735
+ const AMP_SETTINGS_FILE_NAME = "settings.json";
1736
+ const AMP_SETTINGS_JSONC_FILE_NAME = "settings.jsonc";
1728
1737
  //#endregion
1729
1738
  //#region src/types/ai-file.ts
1730
1739
  var AiFile = class {
@@ -1798,6 +1807,414 @@ var AiFile = class {
1798
1807
  }
1799
1808
  };
1800
1809
  //#endregion
1810
+ //#region src/types/rulesync-file.ts
1811
+ var RulesyncFile = class extends AiFile {
1812
+ static async fromFile(_params) {
1813
+ throw new Error("Please implement this method in the subclass.");
1814
+ }
1815
+ };
1816
+ //#endregion
1817
+ //#region src/features/checks/rulesync-check.ts
1818
+ const RulesyncCheckFrontmatterSchema = zod_mini.z.looseObject({
1819
+ targets: zod_mini.z._default(RulesyncTargetsSchema, ["*"]),
1820
+ description: zod_mini.z.optional(zod_mini.z.string()),
1821
+ severity: zod_mini.z.optional(zod_mini.z.enum([
1822
+ "low",
1823
+ "medium",
1824
+ "high",
1825
+ "critical"
1826
+ ])),
1827
+ tools: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string()))
1828
+ });
1829
+ var RulesyncCheck = class RulesyncCheck extends RulesyncFile {
1830
+ frontmatter;
1831
+ body;
1832
+ constructor({ frontmatter, body, ...rest }) {
1833
+ const parseResult = RulesyncCheckFrontmatterSchema.safeParse(frontmatter);
1834
+ if (!parseResult.success && rest.validate !== false) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`);
1835
+ const parsedFrontmatter = parseResult.success ? {
1836
+ ...frontmatter,
1837
+ ...parseResult.data
1838
+ } : {
1839
+ ...frontmatter,
1840
+ targets: frontmatter?.targets ?? ["*"]
1841
+ };
1842
+ super({
1843
+ ...rest,
1844
+ fileContent: stringifyFrontmatter(body, parsedFrontmatter)
1845
+ });
1846
+ this.frontmatter = parsedFrontmatter;
1847
+ this.body = body;
1848
+ }
1849
+ static getSettablePaths() {
1850
+ return { relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH };
1851
+ }
1852
+ getFrontmatter() {
1853
+ return this.frontmatter;
1854
+ }
1855
+ getBody() {
1856
+ return this.body;
1857
+ }
1858
+ validate() {
1859
+ if (!this.frontmatter) return {
1860
+ success: true,
1861
+ error: null
1862
+ };
1863
+ const result = RulesyncCheckFrontmatterSchema.safeParse(this.frontmatter);
1864
+ if (result.success) return {
1865
+ success: true,
1866
+ error: null
1867
+ };
1868
+ else return {
1869
+ success: false,
1870
+ error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
1871
+ };
1872
+ }
1873
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath }) {
1874
+ const filePath = (0, node_path.join)(outputRoot, RULESYNC_CHECKS_RELATIVE_DIR_PATH, relativeFilePath);
1875
+ const { frontmatter, body: content, hasFrontmatter } = parseFrontmatter(await readFileContent(filePath), filePath);
1876
+ if (!hasFrontmatter) throw new Error(`Missing frontmatter in ${filePath}. Rulesync files must begin with a YAML frontmatter block delimited by '---'.`);
1877
+ const result = RulesyncCheckFrontmatterSchema.safeParse(frontmatter);
1878
+ if (!result.success) throw new Error(`Invalid frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
1879
+ const filename = (0, node_path.basename)(relativeFilePath);
1880
+ return new RulesyncCheck({
1881
+ outputRoot,
1882
+ relativeDirPath: this.getSettablePaths().relativeDirPath,
1883
+ relativeFilePath: filename,
1884
+ frontmatter: result.data,
1885
+ body: content.trim()
1886
+ });
1887
+ }
1888
+ };
1889
+ //#endregion
1890
+ //#region src/types/tool-file.ts
1891
+ var ToolFile = class extends AiFile {};
1892
+ //#endregion
1893
+ //#region src/features/checks/tool-check.ts
1894
+ var ToolCheck = class extends ToolFile {
1895
+ static getSettablePaths(_options = {}) {
1896
+ throw new Error("Please implement this method in the subclass.");
1897
+ }
1898
+ static async fromFile(_params) {
1899
+ throw new Error("Please implement this method in the subclass.");
1900
+ }
1901
+ /**
1902
+ * Create a minimal instance for deletion purposes.
1903
+ * This method does not read or parse file content, making it safe to use
1904
+ * even when files have old/incompatible formats.
1905
+ */
1906
+ static forDeletion(_params) {
1907
+ throw new Error("Please implement this method in the subclass.");
1908
+ }
1909
+ static fromRulesyncCheck(_params) {
1910
+ throw new Error("Please implement this method in the subclass.");
1911
+ }
1912
+ static isTargetedByRulesyncCheck(_rulesyncCheck) {
1913
+ throw new Error("Please implement this method in the subclass.");
1914
+ }
1915
+ static isTargetedByRulesyncCheckDefault({ rulesyncCheck, toolTarget }) {
1916
+ const targets = rulesyncCheck.getFrontmatter().targets;
1917
+ if (!targets) return true;
1918
+ if (targets.includes("*")) return true;
1919
+ if (targets.includes(toolTarget)) return true;
1920
+ return false;
1921
+ }
1922
+ static filterToolSpecificSection(rawSection, excludeFields) {
1923
+ const filtered = {};
1924
+ for (const [key, value] of Object.entries(rawSection)) if (!excludeFields.includes(key)) filtered[key] = value;
1925
+ return filtered;
1926
+ }
1927
+ };
1928
+ //#endregion
1929
+ //#region src/features/checks/amp-check.ts
1930
+ const AmpCheckFrontmatterSchema = zod_mini.z.looseObject({
1931
+ name: zod_mini.z.string(),
1932
+ description: zod_mini.z.optional(zod_mini.z.string()),
1933
+ "severity-default": zod_mini.z.optional(zod_mini.z.enum([
1934
+ "low",
1935
+ "medium",
1936
+ "high",
1937
+ "critical"
1938
+ ])),
1939
+ tools: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string()))
1940
+ });
1941
+ /**
1942
+ * Represents an Amp code review check.
1943
+ *
1944
+ * Amp natively reads code review checks as Markdown files with YAML frontmatter,
1945
+ * scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`).
1946
+ * Each check runs as a per-check subagent during code review.
1947
+ * @see https://ampcode.com/manual
1948
+ */
1949
+ var AmpCheck = class AmpCheck extends ToolCheck {
1950
+ frontmatter;
1951
+ body;
1952
+ constructor({ frontmatter, body, fileContent, ...rest }) {
1953
+ if (rest.validate !== false) {
1954
+ const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
1955
+ if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
1956
+ }
1957
+ super({
1958
+ ...rest,
1959
+ fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
1960
+ });
1961
+ this.frontmatter = frontmatter;
1962
+ this.body = body;
1963
+ }
1964
+ static getSettablePaths({ global = false } = {}) {
1965
+ return { relativeDirPath: global ? AMP_CHECKS_GLOBAL_DIR : AMP_CHECKS_PROJECT_DIR };
1966
+ }
1967
+ getFrontmatter() {
1968
+ return this.frontmatter;
1969
+ }
1970
+ getBody() {
1971
+ return this.body;
1972
+ }
1973
+ toRulesyncCheck() {
1974
+ const { name: _name, description, "severity-default": severityDefault, tools, ...restFields } = this.frontmatter;
1975
+ return new RulesyncCheck({
1976
+ outputRoot: ".",
1977
+ frontmatter: {
1978
+ targets: ["*"],
1979
+ ...description !== void 0 && { description },
1980
+ ...severityDefault !== void 0 && { severity: severityDefault },
1981
+ ...tools !== void 0 && { tools },
1982
+ ...restFields
1983
+ },
1984
+ body: this.body,
1985
+ relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH,
1986
+ relativeFilePath: this.getRelativeFilePath(),
1987
+ validate: true
1988
+ });
1989
+ }
1990
+ static fromRulesyncCheck({ outputRoot = process.cwd(), rulesyncCheck, validate = true, global = false }) {
1991
+ const rulesyncFrontmatter = rulesyncCheck.getFrontmatter();
1992
+ const relativeFilePath = rulesyncCheck.getRelativeFilePath();
1993
+ const name = (0, node_path.basename)(relativeFilePath, ".md");
1994
+ const { targets: _targets, description, severity, tools, ...restFields } = rulesyncFrontmatter;
1995
+ const toolTargetKeys = new Set(ALL_TOOL_TARGETS);
1996
+ const passthroughFields = Object.fromEntries(Object.entries(restFields).filter(([key]) => !toolTargetKeys.has(key) && key !== "name"));
1997
+ const ampSection = this.filterToolSpecificSection(rulesyncFrontmatter.amp ?? {}, ["name"]);
1998
+ const rawAmpFrontmatter = {
1999
+ name,
2000
+ ...description !== void 0 && { description },
2001
+ ...severity !== void 0 && { "severity-default": severity },
2002
+ ...tools !== void 0 && { tools },
2003
+ ...passthroughFields,
2004
+ ...ampSection
2005
+ };
2006
+ const result = AmpCheckFrontmatterSchema.safeParse(rawAmpFrontmatter);
2007
+ if (!result.success) throw new Error(`Invalid amp check frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
2008
+ const ampFrontmatter = result.data;
2009
+ const body = rulesyncCheck.getBody();
2010
+ const fileContent = stringifyFrontmatter(body, ampFrontmatter);
2011
+ const paths = this.getSettablePaths({ global });
2012
+ return new AmpCheck({
2013
+ outputRoot,
2014
+ frontmatter: ampFrontmatter,
2015
+ body,
2016
+ relativeDirPath: paths.relativeDirPath,
2017
+ relativeFilePath,
2018
+ fileContent,
2019
+ validate
2020
+ });
2021
+ }
2022
+ validate() {
2023
+ if (!this.frontmatter) return {
2024
+ success: true,
2025
+ error: null
2026
+ };
2027
+ const result = AmpCheckFrontmatterSchema.safeParse(this.frontmatter);
2028
+ if (result.success) return {
2029
+ success: true,
2030
+ error: null
2031
+ };
2032
+ else return {
2033
+ success: false,
2034
+ error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
2035
+ };
2036
+ }
2037
+ static isTargetedByRulesyncCheck(rulesyncCheck) {
2038
+ return this.isTargetedByRulesyncCheckDefault({
2039
+ rulesyncCheck,
2040
+ toolTarget: "amp"
2041
+ });
2042
+ }
2043
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
2044
+ const paths = this.getSettablePaths({ global });
2045
+ const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
2046
+ const fileContent = await readFileContent(filePath);
2047
+ const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
2048
+ const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
2049
+ if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
2050
+ return new AmpCheck({
2051
+ outputRoot,
2052
+ relativeDirPath: paths.relativeDirPath,
2053
+ relativeFilePath,
2054
+ frontmatter: result.data,
2055
+ body: content.trim(),
2056
+ fileContent,
2057
+ validate
2058
+ });
2059
+ }
2060
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
2061
+ return new AmpCheck({
2062
+ outputRoot,
2063
+ relativeDirPath,
2064
+ relativeFilePath,
2065
+ frontmatter: { name: "" },
2066
+ body: "",
2067
+ fileContent: "",
2068
+ validate: false
2069
+ });
2070
+ }
2071
+ };
2072
+ //#endregion
2073
+ //#region src/features/checks/checks-processor.ts
2074
+ const ChecksProcessorToolTargetSchema = zod_mini.z.enum(checksProcessorToolTargetTuple);
2075
+ /**
2076
+ * Factory Map mapping tool targets to their check factories.
2077
+ * Using Map to preserve insertion order for consistent iteration.
2078
+ */
2079
+ const toolCheckFactories = /* @__PURE__ */ new Map([["amp", {
2080
+ class: AmpCheck,
2081
+ meta: {
2082
+ supportsGlobal: true,
2083
+ filePattern: "*.md"
2084
+ }
2085
+ }]]);
2086
+ const defaultGetFactory$6 = (target) => {
2087
+ const factory = toolCheckFactories.get(target);
2088
+ if (!factory) throw new Error(`Unsupported tool target: ${target}`);
2089
+ return factory;
2090
+ };
2091
+ const allToolTargetKeys$5 = [...toolCheckFactories.keys()];
2092
+ const checksProcessorToolTargets = allToolTargetKeys$5;
2093
+ const checksProcessorToolTargetsGlobal = allToolTargetKeys$5.filter((target) => {
2094
+ return toolCheckFactories.get(target)?.meta.supportsGlobal ?? false;
2095
+ });
2096
+ var ChecksProcessor = class extends FeatureProcessor {
2097
+ toolTarget;
2098
+ global;
2099
+ getFactory;
2100
+ constructor({ outputRoot = process.cwd(), inputRoot = process.cwd(), toolTarget, global = false, getFactory = defaultGetFactory$6, dryRun = false, logger }) {
2101
+ super({
2102
+ outputRoot,
2103
+ inputRoot,
2104
+ dryRun,
2105
+ logger
2106
+ });
2107
+ const result = ChecksProcessorToolTargetSchema.safeParse(toolTarget);
2108
+ if (!result.success) throw new Error(`Invalid tool target for ChecksProcessor: ${toolTarget}. ${formatError(result.error)}`);
2109
+ this.toolTarget = result.data;
2110
+ this.global = global;
2111
+ this.getFactory = getFactory;
2112
+ }
2113
+ async convertRulesyncFilesToToolFiles(rulesyncFiles) {
2114
+ const rulesyncChecks = rulesyncFiles.filter((file) => file instanceof RulesyncCheck);
2115
+ const factory = this.getFactory(this.toolTarget);
2116
+ return rulesyncChecks.filter((rulesyncCheck) => factory.class.isTargetedByRulesyncCheck(rulesyncCheck)).map((rulesyncCheck) => factory.class.fromRulesyncCheck({
2117
+ outputRoot: this.outputRoot,
2118
+ relativeDirPath: RulesyncCheck.getSettablePaths().relativeDirPath,
2119
+ rulesyncCheck,
2120
+ global: this.global
2121
+ }));
2122
+ }
2123
+ async convertToolFilesToRulesyncFiles(toolFiles) {
2124
+ return toolFiles.filter((file) => file instanceof ToolCheck).map((toolCheck) => toolCheck.toRulesyncCheck());
2125
+ }
2126
+ /**
2127
+ * Implementation of abstract method from Processor
2128
+ * Load and parse rulesync check files from .rulesync/checks/ directory
2129
+ */
2130
+ async loadRulesyncFiles() {
2131
+ const checksDir = (0, node_path.join)(this.inputRoot, RulesyncCheck.getSettablePaths().relativeDirPath);
2132
+ if (!await directoryExists(checksDir)) {
2133
+ this.logger.debug(`Rulesync checks directory not found: ${checksDir}`);
2134
+ return [];
2135
+ }
2136
+ const mdFiles = (await listDirectoryFiles(checksDir)).filter((file) => file.endsWith(".md"));
2137
+ if (mdFiles.length === 0) {
2138
+ this.logger.debug(`No markdown files found in rulesync checks directory: ${checksDir}`);
2139
+ return [];
2140
+ }
2141
+ this.logger.debug(`Found ${mdFiles.length} check files in ${checksDir}`);
2142
+ const rulesyncChecks = [];
2143
+ for (const mdFile of mdFiles) {
2144
+ const filepath = (0, node_path.join)(checksDir, mdFile);
2145
+ try {
2146
+ const rulesyncCheck = await RulesyncCheck.fromFile({
2147
+ outputRoot: this.inputRoot,
2148
+ relativeFilePath: mdFile,
2149
+ validate: true
2150
+ });
2151
+ rulesyncChecks.push(rulesyncCheck);
2152
+ this.logger.debug(`Successfully loaded check: ${mdFile}`);
2153
+ } catch (error) {
2154
+ this.logger.warn(`Failed to load check file ${filepath}: ${formatError(error)}`);
2155
+ continue;
2156
+ }
2157
+ }
2158
+ this.logger.debug(`Successfully loaded ${rulesyncChecks.length} rulesync checks`);
2159
+ return rulesyncChecks;
2160
+ }
2161
+ /**
2162
+ * Implementation of abstract method from Processor
2163
+ * Load tool-specific check files and parse them into ToolCheck instances
2164
+ */
2165
+ async loadToolFiles({ forDeletion = false } = {}) {
2166
+ const factory = this.getFactory(this.toolTarget);
2167
+ const paths = factory.class.getSettablePaths({ global: this.global });
2168
+ const baseDir = (0, node_path.join)(this.outputRoot, paths.relativeDirPath);
2169
+ const checkFilePaths = await findFilesByGlobs((0, node_path.join)(baseDir, factory.meta.filePattern));
2170
+ const toRelativeFilePath = (path) => (0, node_path.relative)(baseDir, path);
2171
+ if (forDeletion) return checkFilePaths.map((path) => factory.class.forDeletion({
2172
+ outputRoot: this.outputRoot,
2173
+ relativeDirPath: paths.relativeDirPath,
2174
+ relativeFilePath: toRelativeFilePath(path),
2175
+ global: this.global
2176
+ })).filter((check) => check.isDeletable());
2177
+ const loaded = await Promise.all(checkFilePaths.map((path) => factory.class.fromFile({
2178
+ outputRoot: this.outputRoot,
2179
+ relativeDirPath: paths.relativeDirPath,
2180
+ relativeFilePath: toRelativeFilePath(path),
2181
+ global: this.global
2182
+ })));
2183
+ this.logger.debug(`Successfully loaded ${loaded.length} ${this.toolTarget} checks from ${paths.relativeDirPath}`);
2184
+ return loaded;
2185
+ }
2186
+ /**
2187
+ * Implementation of abstract method from FeatureProcessor
2188
+ * Return the tool targets that this processor supports
2189
+ */
2190
+ static getToolTargets({ global = false } = {}) {
2191
+ if (global) return [...checksProcessorToolTargetsGlobal];
2192
+ return [...checksProcessorToolTargets];
2193
+ }
2194
+ static getToolTargetsSimulated() {
2195
+ return [];
2196
+ }
2197
+ /**
2198
+ * Get the factory for a specific tool target.
2199
+ * This is a static version of the internal getFactory for external use.
2200
+ * @param target - The tool target. Must be a valid ChecksProcessorToolTarget.
2201
+ * @returns The factory for the target, or undefined if not found.
2202
+ */
2203
+ static getFactory(target) {
2204
+ const result = ChecksProcessorToolTargetSchema.safeParse(target);
2205
+ if (!result.success) return;
2206
+ return toolCheckFactories.get(result.data);
2207
+ }
2208
+ };
2209
+ //#endregion
2210
+ //#region src/constants/agentsmd-paths.ts
2211
+ const AGENTSMD_DIR = ".agents";
2212
+ const AGENTSMD_MEMORIES_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "memories");
2213
+ const AGENTSMD_COMMANDS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "commands");
2214
+ const AGENTSMD_SKILLS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "skills");
2215
+ const AGENTSMD_SUBAGENTS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "subagents");
2216
+ const AGENTSMD_RULE_FILE_NAME = "AGENTS.md";
2217
+ //#endregion
1801
2218
  //#region src/features/commands/tool-command.ts
1802
2219
  /**
1803
2220
  * Abstract base class for AI development tool-specific command formats.
@@ -2045,13 +2462,6 @@ const AntigravityCommandFrontmatterSchema = zod_mini.z.looseObject({
2045
2462
  ...AntigravityWorkflowFrontmatterSchema.shape
2046
2463
  });
2047
2464
  //#endregion
2048
- //#region src/types/rulesync-file.ts
2049
- var RulesyncFile = class extends AiFile {
2050
- static async fromFile(_params) {
2051
- throw new Error("Please implement this method in the subclass.");
2052
- }
2053
- };
2054
- //#endregion
2055
2465
  //#region src/features/commands/rulesync-command.ts
2056
2466
  const RulesyncCommandFrontmatterSchema = zod_mini.z.looseObject({
2057
2467
  targets: zod_mini.z._default(RulesyncTargetsSchema, ["*"]),
@@ -3112,135 +3522,118 @@ var CursorCommand = class CursorCommand extends ToolCommand {
3112
3522
  //#endregion
3113
3523
  //#region src/constants/devin-paths.ts
3114
3524
  const DEVIN_DIR = ".devin";
3115
- const CODEIUM_WINDSURF_DIR = (0, node_path.join)(".codeium", "windsurf");
3116
- const DEVIN_WORKFLOWS_DIR_PATH = (0, node_path.join)(DEVIN_DIR, "workflows");
3117
3525
  const DEVIN_SKILLS_DIR_PATH = (0, node_path.join)(DEVIN_DIR, "skills");
3118
3526
  const DEVIN_AGENTS_DIR_PATH = (0, node_path.join)(DEVIN_DIR, "agents");
3119
3527
  const DEVIN_GLOBAL_CONFIG_DIR_PATH = (0, node_path.join)(".config", "devin");
3120
3528
  const DEVIN_GLOBAL_AGENTS_DIR_PATH = (0, node_path.join)(DEVIN_GLOBAL_CONFIG_DIR_PATH, "agents");
3121
3529
  const DEVIN_GLOBAL_SKILLS_DIR_PATH = (0, node_path.join)(DEVIN_GLOBAL_CONFIG_DIR_PATH, "skills");
3122
- const CODEIUM_WINDSURF_GLOBAL_WORKFLOWS_DIR_PATH = (0, node_path.join)(CODEIUM_WINDSURF_DIR, "global_workflows");
3123
3530
  const DEVIN_CONFIG_FILE_NAME = "config.json";
3124
3531
  const DEVIN_HOOKS_V1_FILE_NAME = "hooks.v1.json";
3125
3532
  const DEVIN_GLOBAL_AGENTS_FILE_NAME = "AGENTS.md";
3126
3533
  const DEVIN_IGNORE_FILE_NAME = ".devinignore";
3127
3534
  const DEVIN_LEGACY_IGNORE_FILE_NAME = ".codeiumignore";
3128
3535
  //#endregion
3536
+ //#region src/constants/general.ts
3537
+ const SKILL_FILE_NAME$1 = "SKILL.md";
3538
+ //#endregion
3539
+ //#region src/features/commands/command-skill-ownership.ts
3540
+ /**
3541
+ * Slug used for the per-command `<slug>/SKILL.md` directory when a tool's
3542
+ * commands are emitted onto its skills surface (Devin, Hermes Agent).
3543
+ */
3544
+ function commandSlug(relativeFilePath) {
3545
+ return (0, node_path.basename)(relativeFilePath, ".md").replace(/[^a-zA-Z0-9_-]/g, "-");
3546
+ }
3547
+ /**
3548
+ * Whether a rulesync command exists whose slug matches `dirName`.
3549
+ *
3550
+ * Used by the skills-surface `isDirOwned` hooks of tools whose commands are
3551
+ * emitted as `<slug>/SKILL.md` into the skills tree: a directory matching a
3552
+ * current command slug is owned by the commands feature, so the skills
3553
+ * feature must neither import it as a skill nor delete it as an orphan
3554
+ * skill. Once the command is removed from `.rulesync/commands/`, the
3555
+ * directory stops matching and the skills feature cleans it up as a regular
3556
+ * orphan.
3557
+ */
3558
+ async function rulesyncCommandSlugExists({ inputRoot, dirName }) {
3559
+ return (await findFilesByGlobs((0, node_path.join)(inputRoot, RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "**", "*.md"))).some((filePath) => commandSlug((0, node_path.basename)(filePath)) === dirName);
3560
+ }
3561
+ //#endregion
3129
3562
  //#region src/features/commands/devin-command.ts
3130
- const DevinCommandFrontmatterSchema = zod_mini.z.looseObject({ description: zod_mini.z.optional(zod_mini.z.string()) });
3563
+ function commandSkillContent$1(rulesyncCommand) {
3564
+ const slug = commandSlug(rulesyncCommand.getRelativeFilePath());
3565
+ const description = rulesyncCommand.getFrontmatter().description ?? `${slug} command`;
3566
+ return stringifyFrontmatter(rulesyncCommand.getBody().trim(), {
3567
+ name: slug,
3568
+ description
3569
+ });
3570
+ }
3131
3571
  /**
3132
- * Represents a Devin (Cascade, now Devin Desktop) workflow command.
3133
- * Devin supports workflows in both project mode under .devin/workflows/
3134
- * (preferred since the Devin Desktop rebrand; .devin/workflows/ is the
3135
- * legacy fallback the tool still reads) and global mode under
3136
- * ~/.codeium/windsurf/global_workflows/ (unchanged by the rebrand).
3572
+ * Represents a Devin slash command, emitted as a Devin Skill.
3573
+ *
3574
+ * Devin's extensibility docs no longer document a standalone
3575
+ * workflows/commands component reusable prompts invoked as slash commands
3576
+ * are Skills (`/name`). Commands are therefore emitted onto the native skills
3577
+ * surface, one `SKILL.md` per command: `.devin/skills/<slug>/SKILL.md`
3578
+ * (project) and `~/.config/devin/skills/<slug>/SKILL.md` (global). The legacy
3579
+ * Windsurf/Cascade-era `.devin/workflows/` and
3580
+ * `~/.codeium/windsurf/global_workflows/` locations are no longer emitted.
3581
+ *
3582
+ * Import and deletion are intentionally no-ops for this target: the skills
3583
+ * feature owns the `.devin/skills/` tree, so importing it as commands would
3584
+ * double-import every skill (mirrors the Hermes Agent commands target).
3585
+ *
3586
+ * @see https://docs.devin.ai/cli/extensibility
3587
+ * @see https://docs.devin.ai/cli/extensibility/skills/overview
3137
3588
  */
3138
3589
  var DevinCommand = class DevinCommand extends ToolCommand {
3139
- frontmatter;
3140
- body;
3141
- constructor({ frontmatter, body, ...rest }) {
3142
- if (rest.validate) {
3143
- const result = DevinCommandFrontmatterSchema.safeParse(frontmatter);
3144
- if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
3145
- }
3146
- super({
3147
- ...rest,
3148
- fileContent: stringifyFrontmatter(body, frontmatter)
3590
+ static isTargetedByRulesyncCommand(rulesyncCommand) {
3591
+ return this.isTargetedByRulesyncCommandDefault({
3592
+ rulesyncCommand,
3593
+ toolTarget: "devin"
3149
3594
  });
3150
- this.frontmatter = frontmatter;
3151
- this.body = body;
3152
3595
  }
3153
3596
  static getSettablePaths({ global = false } = {}) {
3154
- if (global) return { relativeDirPath: CODEIUM_WINDSURF_GLOBAL_WORKFLOWS_DIR_PATH };
3155
- return { relativeDirPath: DEVIN_WORKFLOWS_DIR_PATH };
3156
- }
3157
- getBody() {
3158
- return this.body;
3159
- }
3160
- getFrontmatter() {
3161
- return this.frontmatter;
3162
- }
3163
- toRulesyncCommand() {
3164
- const { description, ...restFields } = this.frontmatter;
3165
- const rulesyncFrontmatter = {
3166
- targets: ["*"],
3167
- description,
3168
- ...Object.keys(restFields).length > 0 && { devin: restFields }
3169
- };
3170
- const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
3171
- return new RulesyncCommand({
3172
- outputRoot: process.cwd(),
3173
- frontmatter: rulesyncFrontmatter,
3174
- body: this.body,
3175
- relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
3176
- relativeFilePath: this.relativeFilePath,
3177
- fileContent,
3178
- validate: true
3179
- });
3597
+ return { relativeDirPath: global ? DEVIN_GLOBAL_SKILLS_DIR_PATH : DEVIN_SKILLS_DIR_PATH };
3180
3598
  }
3181
- static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
3182
- const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
3183
- const devinFields = rulesyncFrontmatter.devin ?? {};
3184
- const devinFrontmatter = {
3185
- description: rulesyncFrontmatter.description,
3186
- ...devinFields
3187
- };
3188
- const body = rulesyncCommand.getBody();
3189
- const paths = this.getSettablePaths({ global });
3190
- return new DevinCommand({
3191
- outputRoot,
3192
- frontmatter: devinFrontmatter,
3193
- body,
3194
- relativeDirPath: paths.relativeDirPath,
3195
- relativeFilePath: rulesyncCommand.getRelativeFilePath(),
3196
- validate
3599
+ constructor({ slug, ...params }) {
3600
+ super({
3601
+ ...params,
3602
+ ...slug !== void 0 && {
3603
+ relativeDirPath: (0, node_path.join)(params.relativeDirPath, slug),
3604
+ relativeFilePath: "SKILL.md"
3605
+ }
3197
3606
  });
3198
3607
  }
3199
3608
  validate() {
3200
- if (!this.frontmatter) return {
3201
- success: true,
3202
- error: null
3203
- };
3204
- const result = DevinCommandFrontmatterSchema.safeParse(this.frontmatter);
3205
- if (result.success) return {
3609
+ return {
3206
3610
  success: true,
3207
3611
  error: null
3208
3612
  };
3209
- else return {
3210
- success: false,
3211
- error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
3212
- };
3213
3613
  }
3214
- static isTargetedByRulesyncCommand(rulesyncCommand) {
3215
- return this.isTargetedByRulesyncCommandDefault({
3216
- rulesyncCommand,
3217
- toolTarget: "devin"
3614
+ toRulesyncCommand() {
3615
+ const slug = (0, node_path.basename)((0, node_path.dirname)(this.getRelativePathFromCwd()));
3616
+ const { frontmatter, body } = parseFrontmatter(this.getFileContent(), this.getFilePath());
3617
+ const description = typeof frontmatter.description === "string" ? frontmatter.description : void 0;
3618
+ return new RulesyncCommand({
3619
+ relativeDirPath: RULESYNC_COMMANDS_RELATIVE_DIR_PATH,
3620
+ relativeFilePath: `${slug}.md`,
3621
+ frontmatter: { description },
3622
+ body: body.trimStart()
3218
3623
  });
3219
3624
  }
3220
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
3221
- const paths = this.getSettablePaths({ global });
3222
- const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
3223
- const { frontmatter, body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
3224
- const result = DevinCommandFrontmatterSchema.safeParse(frontmatter);
3225
- if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
3625
+ static fromRulesyncCommand({ outputRoot, rulesyncCommand, global = false }) {
3626
+ const paths = DevinCommand.getSettablePaths({ global });
3226
3627
  return new DevinCommand({
3227
3628
  outputRoot,
3228
3629
  relativeDirPath: paths.relativeDirPath,
3229
- relativeFilePath,
3230
- frontmatter: result.data,
3231
- body: content.trim(),
3232
- validate
3630
+ relativeFilePath: SKILL_FILE_NAME$1,
3631
+ slug: commandSlug(rulesyncCommand.getRelativeFilePath()),
3632
+ fileContent: commandSkillContent$1(rulesyncCommand)
3233
3633
  });
3234
3634
  }
3235
- static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
3236
- return new DevinCommand({
3237
- outputRoot,
3238
- relativeDirPath,
3239
- relativeFilePath,
3240
- frontmatter: { description: "" },
3241
- body: "",
3242
- validate: false
3243
- });
3635
+ getFileContent() {
3636
+ return this.fileContent;
3244
3637
  }
3245
3638
  };
3246
3639
  //#endregion
@@ -3569,10 +3962,7 @@ const HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_MANIFEST_PATH = (0, node_path.join)(
3569
3962
  const HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_INIT_PATH = (0, node_path.join)(HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_DIR_PATH, "__init__.py");
3570
3963
  //#endregion
3571
3964
  //#region src/features/commands/hermesagent-command.ts
3572
- const SKILL_FILE_NAME$1 = "SKILL.md";
3573
- function commandSlug(relativeFilePath) {
3574
- return (0, node_path.basename)(relativeFilePath, ".md").replace(/[^a-zA-Z0-9_-]/g, "-");
3575
- }
3965
+ const SKILL_FILE_NAME = "SKILL.md";
3576
3966
  function commandSkillContent(rulesyncCommand) {
3577
3967
  const slug = commandSlug(rulesyncCommand.getRelativeFilePath());
3578
3968
  const description = rulesyncCommand.getFrontmatter().description ?? `${slug} command`;
@@ -3589,7 +3979,7 @@ var HermesagentCommand = class HermesagentCommand extends ToolCommand {
3589
3979
  static getSettablePaths({ slug = "command" } = {}) {
3590
3980
  return {
3591
3981
  relativeDirPath: (0, node_path.join)(HERMESAGENT_SKILLS_DIR_PATH, slug),
3592
- relativeFilePath: SKILL_FILE_NAME$1
3982
+ relativeFilePath: SKILL_FILE_NAME
3593
3983
  };
3594
3984
  }
3595
3985
  constructor({ slug, ...params }) {
@@ -3620,7 +4010,7 @@ var HermesagentCommand = class HermesagentCommand extends ToolCommand {
3620
4010
  return new HermesagentCommand({
3621
4011
  outputRoot,
3622
4012
  relativeDirPath: "",
3623
- relativeFilePath: SKILL_FILE_NAME$1,
4013
+ relativeFilePath: SKILL_FILE_NAME,
3624
4014
  slug: commandSlug(rulesyncCommand.getRelativeFilePath()),
3625
4015
  fileContent: commandSkillContent(rulesyncCommand)
3626
4016
  });
@@ -4855,6 +5245,7 @@ const PI_AGENT_SKILLS_DIR_PATH = (0, node_path.join)(PI_AGENT_DIR, "skills");
4855
5245
  const PI_PROMPTS_DIR_PATH = (0, node_path.join)(".pi", "prompts");
4856
5246
  const PI_SKILLS_DIR_PATH = (0, node_path.join)(".pi", "skills");
4857
5247
  const PI_RULE_FILE_NAME = "AGENTS.md";
5248
+ const PI_APPEND_SYSTEM_FILE_NAME = "APPEND_SYSTEM.md";
4858
5249
  //#endregion
4859
5250
  //#region src/features/commands/pi-command.ts
4860
5251
  /**
@@ -5389,9 +5780,6 @@ const ROVODEV_AGENTS_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
5389
5780
  const ROVODEV_PROMPTS_FILE_NAME = "prompts.yml";
5390
5781
  const ROVODEV_PROMPTS_DIR_PATH = (0, node_path.join)(ROVODEV_DIR, "prompts");
5391
5782
  //#endregion
5392
- //#region src/types/tool-file.ts
5393
- var ToolFile = class extends AiFile {};
5394
- //#endregion
5395
5783
  //#region src/features/commands/rovodev-command.ts
5396
5784
  /**
5397
5785
  * Rovo Dev CLI "saved prompts": a file-based custom-command surface made of a
@@ -5880,7 +6268,8 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
5880
6268
  supportsProject: false,
5881
6269
  supportsGlobal: true,
5882
6270
  isSimulated: false,
5883
- supportsSubdirectory: true
6271
+ supportsSubdirectory: true,
6272
+ skipToolFileScan: true
5884
6273
  }
5885
6274
  }],
5886
6275
  ["junie", {
@@ -6010,7 +6399,8 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
6010
6399
  supportsProject: true,
6011
6400
  supportsGlobal: true,
6012
6401
  isSimulated: false,
6013
- supportsSubdirectory: false
6402
+ supportsSubdirectory: false,
6403
+ skipToolFileScan: true
6014
6404
  }
6015
6405
  }]
6016
6406
  ]);
@@ -6113,6 +6503,7 @@ var CommandsProcessor = class extends FeatureProcessor {
6113
6503
  */
6114
6504
  async loadToolFiles({ forDeletion = false } = {}) {
6115
6505
  const factory = this.getFactory(this.toolTarget);
6506
+ if (factory.meta.skipToolFileScan) return [];
6116
6507
  const paths = factory.class.getSettablePaths({ global: this.global });
6117
6508
  const outputRootFull = (0, node_path.join)(this.outputRoot, paths.relativeDirPath);
6118
6509
  const commandFilePaths = await findFilesByGlobs(factory.meta.supportsSubdirectory ? (0, node_path.join)(outputRootFull, "**", `*.${factory.meta.extension}`) : (0, node_path.join)(outputRootFull, `*.${factory.meta.extension}`));
@@ -6222,7 +6613,10 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
6222
6613
  type: zod_mini.z.optional(zod_mini.z.enum([
6223
6614
  "command",
6224
6615
  "prompt",
6225
- "http"
6616
+ "http",
6617
+ "agent",
6618
+ "mcp_tool",
6619
+ "function"
6226
6620
  ])),
6227
6621
  url: zod_mini.z.optional(safeString),
6228
6622
  timeout: zod_mini.z.optional(zod_mini.z.number()),
@@ -6235,12 +6629,71 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
6235
6629
  sequential: zod_mini.z.optional(zod_mini.z.boolean()),
6236
6630
  async: zod_mini.z.optional(zod_mini.z.boolean()),
6237
6631
  env: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), safeString)),
6238
- shell: zod_mini.z.optional(safeString),
6632
+ shell: zod_mini.z.optional(zod_mini.z.enum(["bash", "powershell"])),
6239
6633
  statusMessage: zod_mini.z.optional(safeString),
6240
6634
  headers: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), safeString)),
6241
6635
  allowedEnvVars: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
6242
- once: zod_mini.z.optional(zod_mini.z.boolean())
6636
+ once: zod_mini.z.optional(zod_mini.z.boolean()),
6637
+ server: zod_mini.z.optional(safeString),
6638
+ tool: zod_mini.z.optional(safeString),
6639
+ input: zod_mini.z.optional(zod_mini.z.looseObject({})),
6640
+ model: zod_mini.z.optional(safeString)
6243
6641
  });
6642
+ /**
6643
+ * All canonical hook event names.
6644
+ * Each tool supports a subset of these events.
6645
+ */
6646
+ const HOOK_EVENTS = [
6647
+ "sessionStart",
6648
+ "sessionEnd",
6649
+ "preToolUse",
6650
+ "postToolUse",
6651
+ "preModelInvocation",
6652
+ "postModelInvocation",
6653
+ "beforeSubmitPrompt",
6654
+ "stop",
6655
+ "subagentStop",
6656
+ "preCompact",
6657
+ "postCompact",
6658
+ "contextOffload",
6659
+ "postToolUseFailure",
6660
+ "subagentStart",
6661
+ "beforeShellExecution",
6662
+ "afterShellExecution",
6663
+ "beforeMCPExecution",
6664
+ "afterMCPExecution",
6665
+ "beforeReadFile",
6666
+ "afterFileEdit",
6667
+ "beforeAgentResponse",
6668
+ "afterAgentResponse",
6669
+ "afterAgentThought",
6670
+ "beforeTabFileRead",
6671
+ "afterTabFileEdit",
6672
+ "permissionRequest",
6673
+ "notification",
6674
+ "setup",
6675
+ "afterError",
6676
+ "beforeToolSelection",
6677
+ "worktreeCreate",
6678
+ "worktreeRemove",
6679
+ "workspaceOpen",
6680
+ "messageDisplay",
6681
+ "todoCreated",
6682
+ "todoCompleted",
6683
+ "stopFailure",
6684
+ "instructionsLoaded",
6685
+ "userPromptExpansion",
6686
+ "postToolBatch",
6687
+ "permissionDenied",
6688
+ "taskCreated",
6689
+ "taskCompleted",
6690
+ "teammateIdle",
6691
+ "configChange",
6692
+ "cwdChanged",
6693
+ "fileChanged",
6694
+ "elicitation",
6695
+ "elicitationResult"
6696
+ ];
6244
6697
  /** Hook events supported by Cursor. */
6245
6698
  const CURSOR_HOOK_EVENTS = [
6246
6699
  "sessionStart",
@@ -6715,12 +7168,28 @@ const CANONICAL_TO_HERMESAGENT_EVENT_NAMES = {
6715
7168
  */
6716
7169
  const HERMESAGENT_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_HERMESAGENT_EVENT_NAMES).map(([k, v]) => [v, k]));
6717
7170
  const hooksRecordSchema = zod_mini.z.record(zod_mini.z.string(), zod_mini.z.array(HookDefinitionSchema));
7171
+ const HOOK_EVENT_SET = new Set(HOOK_EVENTS);
7172
+ /** Whether `value` is a canonical hook event name. */
7173
+ const isHookEvent = (value) => HOOK_EVENT_SET.has(value);
7174
+ /**
7175
+ * Top-level `hooks` record whose keys must be canonical event names, so typos
7176
+ * are rejected at parse time. Keys are validated with a refinement (instead of
7177
+ * an enum key schema) to keep the inferred type a plain string record.
7178
+ *
7179
+ * The per-tool override blocks below deliberately keep the lenient
7180
+ * `hooksRecordSchema`: some are documented to pass tool-native event keys
7181
+ * through verbatim (e.g. kiro-ide's IDE-only `PostFileSave`/`PreTaskExec`
7182
+ * triggers), which the canonical enum would reject.
7183
+ */
7184
+ const canonicalHooksRecordSchema = zod_mini.z.record(zod_mini.z.string(), zod_mini.z.array(HookDefinitionSchema)).check(zod_mini.z.refine((record) => Object.keys(record).every((key) => HOOK_EVENT_SET.has(key)), { error: (issue) => {
7185
+ return `unknown hook event name(s): ${Object.keys(issue.input ?? {}).filter((key) => !HOOK_EVENT_SET.has(key)).join(", ")}`;
7186
+ } }));
6718
7187
  /**
6719
7188
  * Canonical hooks config (canonical event names in camelCase).
6720
7189
  */
6721
7190
  const HooksConfigSchema = zod_mini.z.looseObject({
6722
7191
  version: zod_mini.z.optional(zod_mini.z.number()),
6723
- hooks: hooksRecordSchema,
7192
+ hooks: canonicalHooksRecordSchema,
6724
7193
  cursor: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
6725
7194
  claudecode: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
6726
7195
  copilot: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
@@ -7157,6 +7626,20 @@ const CANONICAL_TO_GROKCLI_EVENT_NAMES = {
7157
7626
  */
7158
7627
  const GROKCLI_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_GROKCLI_EVENT_NAMES).map(([k, v]) => [v, k]));
7159
7628
  //#endregion
7629
+ //#region src/utils/object.ts
7630
+ /**
7631
+ * Return a shallow copy of `obj` keeping only the entries whose value is
7632
+ * neither `undefined` nor `null`.
7633
+ *
7634
+ * Used to assemble generated config objects without one conditional spread per
7635
+ * optional field (which would otherwise exceed the lint complexity budget).
7636
+ */
7637
+ function compact(obj) {
7638
+ const result = {};
7639
+ for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== null) result[key] = value;
7640
+ return result;
7641
+ }
7642
+ //#endregion
7160
7643
  //#region src/features/hooks/tool-hooks-converter.ts
7161
7644
  function isToolMatcherEntry(x) {
7162
7645
  if (x === null || typeof x !== "object") return false;
@@ -7214,6 +7697,25 @@ function importBooleanPassthroughFields({ h, converterConfig }) {
7214
7697
  return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ tool }) => typeof h[tool] === "boolean").map(({ canonical, tool }) => [canonical, h[tool]]));
7215
7698
  }
7216
7699
  /**
7700
+ * Emit the payload fields specific to a hook type — `url`/`headers`/
7701
+ * `allowedEnvVars` for http, `server`/`tool`/`input` for mcp_tool, `model`
7702
+ * for prompt/agent. https://code.claude.com/docs/en/hooks
7703
+ */
7704
+ function emitTypePayloadFields({ def, hookType, converterConfig }) {
7705
+ if (hookType === "http") return compact({
7706
+ url: def.url,
7707
+ headers: def.headers,
7708
+ allowedEnvVars: def.allowedEnvVars
7709
+ });
7710
+ if (hookType === "mcp_tool") return compact({
7711
+ server: def.server,
7712
+ tool: def.tool,
7713
+ input: def.input
7714
+ });
7715
+ if ((hookType === "prompt" || hookType === "agent") && converterConfig.emitsPromptModel) return compact({ model: def.model });
7716
+ return {};
7717
+ }
7718
+ /**
7217
7719
  * Convert the definitions of a single matcher group into tool hook entries,
7218
7720
  * honoring supported hook types and passthrough fields.
7219
7721
  */
@@ -7235,6 +7737,11 @@ function buildToolHooks({ defs, converterConfig }) {
7235
7737
  ...command !== void 0 && command !== null && { command },
7236
7738
  ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
7237
7739
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
7740
+ ...emitTypePayloadFields({
7741
+ def,
7742
+ hookType,
7743
+ converterConfig
7744
+ }),
7238
7745
  ...converterConfig.passthroughFields?.includes("name") && def.name !== void 0 && def.name !== null && { name: def.name },
7239
7746
  ...converterConfig.passthroughFields?.includes("description") && def.description !== void 0 && def.description !== null && { description: def.description }
7240
7747
  });
@@ -7301,6 +7808,46 @@ function stripCommandPrefix({ command, converterConfig }) {
7301
7808
  return cmd;
7302
7809
  }
7303
7810
  /**
7811
+ * Hook types preserved verbatim on import — Claude Code's five documented
7812
+ * handler types. Anything else is coerced to `command` as before.
7813
+ * https://code.claude.com/docs/en/hooks
7814
+ */
7815
+ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
7816
+ "command",
7817
+ "prompt",
7818
+ "http",
7819
+ "mcp_tool",
7820
+ "agent"
7821
+ ]);
7822
+ function isImportedHookType(value) {
7823
+ return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
7824
+ }
7825
+ function isStringRecord(value) {
7826
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
7827
+ return Object.values(value).every((v) => typeof v === "string");
7828
+ }
7829
+ function isStringArray(value) {
7830
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
7831
+ }
7832
+ /**
7833
+ * Import the payload fields specific to a hook type, type-checking each raw
7834
+ * value before it enters the canonical definition.
7835
+ */
7836
+ function importTypePayloadFields({ h, hookType }) {
7837
+ if (hookType === "http") return {
7838
+ ...typeof h.url === "string" && { url: h.url },
7839
+ ...isStringRecord(h.headers) && { headers: h.headers },
7840
+ ...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
7841
+ };
7842
+ if (hookType === "mcp_tool") return {
7843
+ ...typeof h.server === "string" && { server: h.server },
7844
+ ...typeof h.tool === "string" && { tool: h.tool },
7845
+ ...h.input !== null && typeof h.input === "object" && !Array.isArray(h.input) && { input: h.input }
7846
+ };
7847
+ if (hookType === "prompt" || hookType === "agent") return typeof h.model === "string" ? { model: h.model } : {};
7848
+ return {};
7849
+ }
7850
+ /**
7304
7851
  * Convert a single tool hook record into a canonical hook definition.
7305
7852
  */
7306
7853
  function toolHookToCanonical({ h, rawEntry, converterConfig }) {
@@ -7308,7 +7855,7 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
7308
7855
  command: h.command,
7309
7856
  converterConfig
7310
7857
  });
7311
- const hookType = h.type === "command" || h.type === "prompt" ? h.type : "command";
7858
+ const hookType = isImportedHookType(h.type) ? h.type : "command";
7312
7859
  const timeout = typeof h.timeout === "number" ? h.timeout : void 0;
7313
7860
  const prompt = typeof h.prompt === "string" ? h.prompt : void 0;
7314
7861
  return {
@@ -7316,6 +7863,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
7316
7863
  ...command !== void 0 && command !== null && { command },
7317
7864
  ...timeout !== void 0 && timeout !== null && { timeout },
7318
7865
  ...prompt !== void 0 && prompt !== null && { prompt },
7866
+ ...importTypePayloadFields({
7867
+ h,
7868
+ hookType
7869
+ }),
7319
7870
  ...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
7320
7871
  ...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
7321
7872
  ...importBooleanPassthroughFields({
@@ -7335,6 +7886,32 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig }) {
7335
7886
  converterConfig
7336
7887
  }));
7337
7888
  }
7889
+ /**
7890
+ * Assemble the canonical hooks config a tool importer writes to
7891
+ * `.rulesync/hooks.json`.
7892
+ *
7893
+ * The top-level `hooks` record only accepts canonical event names, so any
7894
+ * imported native event key without a canonical mapping is moved under the
7895
+ * importing tool's own override block (`<overrideKey>.hooks`), whose keys stay
7896
+ * lenient. This mirrors the generate direction — override blocks pass
7897
+ * tool-native keys through verbatim — so documented native triggers (e.g.
7898
+ * kiro-ide's `PostFileSave`) survive an import → generate round-trip instead
7899
+ * of failing canonical validation.
7900
+ */
7901
+ function buildImportedHooksConfig({ hooks, overrideKey, version = 1, extraOverride }) {
7902
+ const canonical = {};
7903
+ const native = {};
7904
+ for (const [event, defs] of Object.entries(hooks)) if (isHookEvent(event)) canonical[event] = defs;
7905
+ else native[event] = defs;
7906
+ const override = { ...extraOverride };
7907
+ if (Object.keys(native).length > 0) override.hooks = native;
7908
+ const config = {
7909
+ version,
7910
+ hooks: canonical
7911
+ };
7912
+ if (Object.keys(override).length > 0) config[overrideKey] = override;
7913
+ return config;
7914
+ }
7338
7915
  function toolHooksToCanonical({ hooks, converterConfig }) {
7339
7916
  if (hooks === null || hooks === void 0 || typeof hooks !== "object") return {};
7340
7917
  const canonical = {};
@@ -7505,7 +8082,8 @@ const ANTIGRAVITY_CONVERTER_CONFIG = {
7505
8082
  "preModelInvocation",
7506
8083
  "postModelInvocation",
7507
8084
  "stop"
7508
- ])
8085
+ ]),
8086
+ supportedHookTypes: /* @__PURE__ */ new Set(["command"])
7509
8087
  };
7510
8088
  /**
7511
8089
  * Antigravity's `hooks.json` is keyed by a named hook whose value holds the
@@ -7612,10 +8190,11 @@ var AntigravityHooks = class extends ToolHooks {
7612
8190
  hooks: flattenAntigravityHooks(parsed),
7613
8191
  converterConfig: ANTIGRAVITY_CONVERTER_CONFIG
7614
8192
  });
7615
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7616
- version: 1,
7617
- hooks
7618
- }, null, 2) });
8193
+ const overrideKey = this.constructor.getOverrideKey();
8194
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8195
+ hooks,
8196
+ overrideKey
8197
+ }), null, 2) });
7619
8198
  }
7620
8199
  validate() {
7621
8200
  return {
@@ -7736,7 +8315,8 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
7736
8315
  "sessionEnd",
7737
8316
  "stop",
7738
8317
  "notification"
7739
- ])
8318
+ ]),
8319
+ supportedHookTypes: /* @__PURE__ */ new Set(["command"])
7740
8320
  };
7741
8321
  /**
7742
8322
  * AugmentCode (Auggie CLI) lifecycle hooks.
@@ -7818,10 +8398,10 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
7818
8398
  hooks: settings.hooks,
7819
8399
  converterConfig: AUGMENTCODE_CONVERTER_CONFIG
7820
8400
  });
7821
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7822
- version: 1,
7823
- hooks
7824
- }, null, 2) });
8401
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8402
+ hooks,
8403
+ overrideKey: "augmentcode"
8404
+ }), null, 2) });
7825
8405
  }
7826
8406
  validate() {
7827
8407
  return {
@@ -7856,7 +8436,15 @@ const CLAUDE_CONVERTER_CONFIG = {
7856
8436
  "taskCompleted",
7857
8437
  "teammateIdle",
7858
8438
  "cwdChanged"
7859
- ])
8439
+ ]),
8440
+ supportedHookTypes: /* @__PURE__ */ new Set([
8441
+ "command",
8442
+ "prompt",
8443
+ "http",
8444
+ "mcp_tool",
8445
+ "agent"
8446
+ ]),
8447
+ emitsPromptModel: true
7860
8448
  };
7861
8449
  var ClaudecodeHooks = class ClaudecodeHooks extends ToolHooks {
7862
8450
  constructor(params) {
@@ -7921,10 +8509,10 @@ var ClaudecodeHooks = class ClaudecodeHooks extends ToolHooks {
7921
8509
  hooks: settings.hooks,
7922
8510
  converterConfig: CLAUDE_CONVERTER_CONFIG
7923
8511
  });
7924
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7925
- version: 1,
7926
- hooks
7927
- }, null, 2) });
8512
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8513
+ hooks,
8514
+ overrideKey: "claudecode"
8515
+ }), null, 2) });
7928
8516
  }
7929
8517
  validate() {
7930
8518
  return {
@@ -8067,10 +8655,10 @@ var CodexcliHooks = class CodexcliHooks extends ToolHooks {
8067
8655
  hooks: parsed.hooks,
8068
8656
  converterConfig: CODEXCLI_CONVERTER_CONFIG
8069
8657
  });
8070
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8071
- version: 1,
8072
- hooks
8073
- }, null, 2) });
8658
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8659
+ hooks,
8660
+ overrideKey: "codexcli"
8661
+ }), null, 2) });
8074
8662
  }
8075
8663
  validate() {
8076
8664
  return {
@@ -8238,10 +8826,10 @@ var CopilotHooks = class CopilotHooks extends ToolHooks {
8238
8826
  throw new Error(`Failed to parse Copilot hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8239
8827
  }
8240
8828
  const hooks = copilotHooksToCanonical(parsed.hooks, options?.logger);
8241
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8242
- version: 1,
8243
- hooks
8244
- }, null, 2) });
8829
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8830
+ hooks,
8831
+ overrideKey: "copilot"
8832
+ }), null, 2) });
8245
8833
  }
8246
8834
  validate() {
8247
8835
  return {
@@ -8260,20 +8848,6 @@ var CopilotHooks = class CopilotHooks extends ToolHooks {
8260
8848
  }
8261
8849
  };
8262
8850
  //#endregion
8263
- //#region src/utils/object.ts
8264
- /**
8265
- * Return a shallow copy of `obj` keeping only the entries whose value is
8266
- * neither `undefined` nor `null`.
8267
- *
8268
- * Used to assemble generated config objects without one conditional spread per
8269
- * optional field (which would otherwise exceed the lint complexity budget).
8270
- */
8271
- function compact(obj) {
8272
- const result = {};
8273
- for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== null) result[key] = value;
8274
- return result;
8275
- }
8276
- //#endregion
8277
8851
  //#region src/features/hooks/copilotcli-hooks.ts
8278
8852
  /**
8279
8853
  * GitHub Copilot CLI hooks.
@@ -8406,7 +8980,7 @@ function buildCopilotCliEntriesForEvent({ eventName, definitions, canonicalSchem
8406
8980
  ...timeoutPart,
8407
8981
  ...rest
8408
8982
  });
8409
- else entries.push({
8983
+ else if (hookType === "command") entries.push({
8410
8984
  type: "command",
8411
8985
  ...matcherPart,
8412
8986
  ...compact({
@@ -8558,10 +9132,10 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
8558
9132
  throw new Error(`Failed to parse Copilot CLI hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8559
9133
  }
8560
9134
  const hooks = copilotCliHooksToCanonical(parsed.hooks, options?.logger);
8561
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8562
- version: 1,
8563
- hooks
8564
- }, null, 2) });
9135
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9136
+ hooks,
9137
+ overrideKey: "copilotcli"
9138
+ }), null, 2) });
8565
9139
  }
8566
9140
  validate() {
8567
9141
  return {
@@ -8616,9 +9190,10 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8616
9190
  ...config.cursor?.hooks
8617
9191
  };
8618
9192
  const mappedHooks = {};
9193
+ const cursorSupportedTypes = /* @__PURE__ */ new Set(["command", "prompt"]);
8619
9194
  for (const [eventName, defs] of Object.entries(mergedHooks)) {
8620
9195
  const cursorEventName = CANONICAL_TO_CURSOR_EVENT_NAMES[eventName] ?? eventName;
8621
- mappedHooks[cursorEventName] = defs.map((def) => ({
9196
+ const mappedDefs = defs.filter((def) => cursorSupportedTypes.has(def.type ?? "command")).map((def) => ({
8622
9197
  ...def.type !== void 0 && def.type !== null && { type: def.type },
8623
9198
  ...def.command !== void 0 && def.command !== null && { command: def.command },
8624
9199
  ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
@@ -8627,6 +9202,7 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8627
9202
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
8628
9203
  ...def.failClosed !== void 0 && def.failClosed !== null && { failClosed: def.failClosed }
8629
9204
  }));
9205
+ if (mappedDefs.length > 0) mappedHooks[cursorEventName] = mappedDefs;
8630
9206
  }
8631
9207
  const cursorConfig = {
8632
9208
  version: config.version ?? 1,
@@ -8653,10 +9229,11 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8653
9229
  canonicalHooks[eventName] = defs;
8654
9230
  }
8655
9231
  const version = parsed.version ?? 1;
8656
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8657
- version,
8658
- hooks: canonicalHooks
8659
- }, null, 2) });
9232
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9233
+ hooks: canonicalHooks,
9234
+ overrideKey: "cursor",
9235
+ version
9236
+ }), null, 2) });
8660
9237
  }
8661
9238
  validate() {
8662
9239
  return {
@@ -8711,7 +9288,7 @@ function canonicalToDeepagentsHooks(config) {
8711
9288
  const deepagentsEvent = CANONICAL_TO_DEEPAGENTS_EVENT_NAMES[canonicalEvent];
8712
9289
  if (!deepagentsEvent) continue;
8713
9290
  for (const def of definitions) {
8714
- if (def.type === "prompt") continue;
9291
+ if ((def.type ?? "command") !== "command") continue;
8715
9292
  if (!def.command) continue;
8716
9293
  if (def.matcher) continue;
8717
9294
  entries.push({
@@ -8801,10 +9378,10 @@ var DeepagentsHooks = class DeepagentsHooks extends ToolHooks {
8801
9378
  throw new Error(`Failed to parse deepagents hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8802
9379
  }
8803
9380
  const hooks = deepagentsToCanonicalHooks(isDeepagentsHooksFile(parsed) ? parsed.hooks : []);
8804
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8805
- version: 1,
8806
- hooks
8807
- }, null, 2) });
9381
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9382
+ hooks,
9383
+ overrideKey: "deepagents"
9384
+ }), null, 2) });
8808
9385
  }
8809
9386
  validate() {
8810
9387
  return {
@@ -8933,10 +9510,10 @@ var DevinHooks = class DevinHooks extends ToolHooks {
8933
9510
  hooks: this.getRelativeFilePath() === "config.json" ? isRecord(parsed) && isRecord(parsed.hooks) ? parsed.hooks : {} : parsed,
8934
9511
  converterConfig: DEVIN_CONVERTER_CONFIG
8935
9512
  });
8936
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8937
- version: 1,
8938
- hooks
8939
- }, null, 2) });
9513
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9514
+ hooks,
9515
+ overrideKey: "devin"
9516
+ }), null, 2) });
8940
9517
  }
8941
9518
  validate() {
8942
9519
  return {
@@ -8960,7 +9537,8 @@ const FACTORYDROID_CONVERTER_CONFIG = {
8960
9537
  supportedEvents: FACTORYDROID_HOOK_EVENTS,
8961
9538
  canonicalToToolEventNames: CANONICAL_TO_FACTORYDROID_EVENT_NAMES,
8962
9539
  toolToCanonicalEventNames: FACTORYDROID_TO_CANONICAL_EVENT_NAMES,
8963
- projectDirVar: "$FACTORY_PROJECT_DIR"
9540
+ projectDirVar: "$FACTORY_PROJECT_DIR",
9541
+ supportedHookTypes: /* @__PURE__ */ new Set(["command", "prompt"])
8964
9542
  };
8965
9543
  var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
8966
9544
  constructor(params) {
@@ -9028,10 +9606,10 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
9028
9606
  hooks: settings.hooks,
9029
9607
  converterConfig: FACTORYDROID_CONVERTER_CONFIG
9030
9608
  });
9031
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9032
- version: 1,
9033
- hooks
9034
- }, null, 2) });
9609
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9610
+ hooks,
9611
+ overrideKey: "factorydroid"
9612
+ }), null, 2) });
9035
9613
  }
9036
9614
  validate() {
9037
9615
  return {
@@ -9122,10 +9700,10 @@ var GooseHooks = class GooseHooks extends ToolHooks {
9122
9700
  hooks: parsed.hooks && typeof parsed.hooks === "object" && !Array.isArray(parsed.hooks) ? Object.fromEntries(Object.entries(parsed.hooks).filter(([eventName]) => Object.hasOwn(GOOSE_TO_CANONICAL_EVENT_NAMES, eventName))) : parsed.hooks,
9123
9701
  converterConfig: GOOSE_CONVERTER_CONFIG
9124
9702
  });
9125
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9126
- version: 1,
9127
- hooks
9128
- }, null, 2) });
9703
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9704
+ hooks,
9705
+ overrideKey: "goose"
9706
+ }), null, 2) });
9129
9707
  }
9130
9708
  validate() {
9131
9709
  return {
@@ -9283,10 +9861,10 @@ var GrokcliHooks = class GrokcliHooks extends ToolHooks {
9283
9861
  hooks: isRecord(parsed) && isRecord(parsed.hooks) ? parsed.hooks : {},
9284
9862
  converterConfig: GROKCLI_CONVERTER_CONFIG
9285
9863
  });
9286
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9287
- version: 1,
9288
- hooks
9289
- }, null, 2) });
9864
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9865
+ hooks,
9866
+ overrideKey: "grokcli"
9867
+ }), null, 2) });
9290
9868
  }
9291
9869
  validate() {
9292
9870
  return {
@@ -9447,10 +10025,10 @@ var HermesagentHooks = class HermesagentHooks extends ToolHooks {
9447
10025
  format: "yaml",
9448
10026
  fileContent: this.getFileContent()
9449
10027
  }).hooks);
9450
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9451
- version: 1,
9452
- hooks
9453
- }, null, 2) });
10028
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10029
+ hooks,
10030
+ overrideKey: "hermesagent"
10031
+ }), null, 2) });
9454
10032
  }
9455
10033
  static fromRulesyncHooks({ outputRoot, rulesyncHooks, logger }) {
9456
10034
  const config = rulesyncHooks.getJson();
@@ -9553,10 +10131,10 @@ var JunieHooks = class JunieHooks extends ToolHooks {
9553
10131
  hooks: settings.hooks,
9554
10132
  converterConfig: JUNIE_CONVERTER_CONFIG
9555
10133
  });
9556
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9557
- version: 1,
9558
- hooks
9559
- }, null, 2) });
10134
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10135
+ hooks,
10136
+ overrideKey: "junie"
10137
+ }), null, 2) });
9560
10138
  }
9561
10139
  validate() {
9562
10140
  return {
@@ -9601,7 +10179,7 @@ function collectOpencodeStyleHandlers({ effectiveHooks, eventMap, namedEventHand
9601
10179
  if (!toolEvent) continue;
9602
10180
  const handlers = [];
9603
10181
  for (const def of definitions) {
9604
- if (def.type === "prompt") continue;
10182
+ if ((def.type ?? "command") !== "command") continue;
9605
10183
  if (!def.command) continue;
9606
10184
  handlers.push({
9607
10185
  command: def.command,
@@ -9906,10 +10484,11 @@ var KiroHooks = class KiroHooks extends ToolHooks {
9906
10484
  throw new Error(`Failed to parse Kiro hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
9907
10485
  }
9908
10486
  const hooks = kiroHooksToCanonical(agentConfig.hooks);
9909
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9910
- version: 1,
9911
- hooks
9912
- }, null, 2) });
10487
+ const overrideKey = this.constructor.getOverrideKey();
10488
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10489
+ hooks,
10490
+ overrideKey
10491
+ }), null, 2) });
9913
10492
  }
9914
10493
  validate() {
9915
10494
  return {
@@ -10118,10 +10697,10 @@ var KiroIdeHooks = class KiroIdeHooks extends ToolHooks {
10118
10697
  throw new Error(`Failed to parse Kiro IDE hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10119
10698
  }
10120
10699
  const hooks = kiroIdeHooksToCanonical(parsed.hooks ?? []);
10121
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10122
- version: 1,
10123
- hooks
10124
- }, null, 2) });
10700
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10701
+ hooks,
10702
+ overrideKey: "kiro-ide"
10703
+ }), null, 2) });
10125
10704
  }
10126
10705
  validate() {
10127
10706
  return {
@@ -10245,11 +10824,18 @@ function canonicalToQwencodeHooks(config) {
10245
10824
  ...sharedHooks,
10246
10825
  ...config.qwencode?.hooks
10247
10826
  };
10827
+ const qwencodeSupportedTypes = /* @__PURE__ */ new Set([
10828
+ "command",
10829
+ "prompt",
10830
+ "http",
10831
+ "function"
10832
+ ]);
10248
10833
  const qwencode = {};
10249
10834
  for (const [eventName, definitions] of Object.entries(effectiveHooks)) {
10250
10835
  const qwencodeEventName = CANONICAL_TO_QWENCODE_EVENT_NAMES[eventName] ?? eventName;
10251
10836
  const byMatcher = /* @__PURE__ */ new Map();
10252
10837
  for (const def of definitions) {
10838
+ if (!qwencodeSupportedTypes.has(def.type ?? "command")) continue;
10253
10839
  const key = def.matcher ?? "";
10254
10840
  const list = byMatcher.get(key);
10255
10841
  if (list) list.push(def);
@@ -10309,9 +10895,10 @@ function qwencodeMatcherEntryToCanonical(entry) {
10309
10895
  const sequential = entry.sequential === true;
10310
10896
  const matcher = entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "" ? entry.matcher : void 0;
10311
10897
  for (const h of hooks) {
10312
- const hookType = h.type === "command" || h.type === "prompt" || h.type === "http" ? h.type : "command";
10898
+ const hookType = h.type === "command" || h.type === "prompt" || h.type === "http" || h.type === "function" ? h.type : "command";
10313
10899
  const isHttp = hookType === "http";
10314
10900
  const isCommand = hookType === "command";
10901
+ const shell = h.shell === "bash" || h.shell === "powershell" ? h.shell : void 0;
10315
10902
  defs.push({
10316
10903
  type: hookType,
10317
10904
  ...compact({
@@ -10323,7 +10910,7 @@ function qwencodeMatcherEntryToCanonical(entry) {
10323
10910
  statusMessage: h.statusMessage,
10324
10911
  async: isCommand ? h.async : void 0,
10325
10912
  env: isCommand ? h.env : void 0,
10326
- shell: isCommand ? h.shell : void 0,
10913
+ shell: isCommand ? shell : void 0,
10327
10914
  headers: isHttp ? h.headers : void 0,
10328
10915
  allowedEnvVars: isHttp ? h.allowedEnvVars : void 0,
10329
10916
  once: isHttp ? h.once : void 0,
@@ -10410,15 +10997,11 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
10410
10997
  } catch (error) {
10411
10998
  throw new Error(`Failed to parse Qwen Code hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10412
10999
  }
10413
- const hooks = qwencodeHooksToCanonical(settings.hooks);
10414
- const canonical = typeof settings.disableAllHooks === "boolean" ? {
10415
- version: 1,
10416
- hooks,
10417
- qwencode: { disableAllHooks: settings.disableAllHooks }
10418
- } : {
10419
- version: 1,
10420
- hooks
10421
- };
11000
+ const canonical = buildImportedHooksConfig({
11001
+ hooks: qwencodeHooksToCanonical(settings.hooks),
11002
+ overrideKey: "qwencode",
11003
+ ...typeof settings.disableAllHooks === "boolean" && { extraOverride: { disableAllHooks: settings.disableAllHooks } }
11004
+ });
10422
11005
  return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(canonical, null, 2) });
10423
11006
  }
10424
11007
  validate() {
@@ -10580,10 +11163,10 @@ var ReasonixHooks = class ReasonixHooks extends ToolHooks {
10580
11163
  throw new Error(`Failed to parse Reasonix hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10581
11164
  }
10582
11165
  const hooks = reasonixHooksToCanonical(settings.hooks);
10583
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10584
- version: 1,
10585
- hooks
10586
- }, null, 2) });
11166
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
11167
+ hooks,
11168
+ overrideKey: "reasonix"
11169
+ }), null, 2) });
10587
11170
  }
10588
11171
  validate() {
10589
11172
  return {
@@ -10806,10 +11389,10 @@ var VibeHooks = class VibeHooks extends ToolHooks {
10806
11389
  throw new Error(`Failed to parse Vibe hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10807
11390
  }
10808
11391
  const hooks = vibeHooksToCanonical(parsed);
10809
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10810
- version: 1,
10811
- hooks
10812
- }, null, 2) });
11392
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
11393
+ hooks,
11394
+ overrideKey: "vibe"
11395
+ }), null, 2) });
10813
11396
  }
10814
11397
  validate() {
10815
11398
  try {
@@ -10896,7 +11479,13 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
10896
11479
  supportsImport: true
10897
11480
  },
10898
11481
  supportedEvents: CLAUDE_HOOK_EVENTS,
10899
- supportedHookTypes: ["command", "prompt"],
11482
+ supportedHookTypes: [
11483
+ "command",
11484
+ "prompt",
11485
+ "http",
11486
+ "mcp_tool",
11487
+ "agent"
11488
+ ],
10900
11489
  supportsMatcher: true
10901
11490
  }],
10902
11491
  ["codexcli", {
@@ -11088,7 +11677,12 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
11088
11677
  supportsImport: true
11089
11678
  },
11090
11679
  supportedEvents: QWENCODE_HOOK_EVENTS,
11091
- supportedHookTypes: ["command"],
11680
+ supportedHookTypes: [
11681
+ "command",
11682
+ "prompt",
11683
+ "http",
11684
+ "function"
11685
+ ],
11092
11686
  supportsMatcher: true
11093
11687
  }],
11094
11688
  ["reasonix", {
@@ -12390,16 +12984,6 @@ var IgnoreProcessor = class extends FeatureProcessor {
12390
12984
  }
12391
12985
  };
12392
12986
  //#endregion
12393
- //#region src/constants/amp-paths.ts
12394
- const AMP_DIR = ".amp";
12395
- const AMP_GLOBAL_DIR = (0, node_path.join)(".config", "amp");
12396
- const AMP_AGENTS_DIR = ".agents";
12397
- const AMP_SKILLS_PROJECT_DIR = (0, node_path.join)(AMP_AGENTS_DIR, "skills");
12398
- const AMP_SKILLS_GLOBAL_DIR = (0, node_path.join)(".config", "agents", "skills");
12399
- const AMP_RULE_FILE_NAME = "AGENTS.md";
12400
- const AMP_SETTINGS_FILE_NAME = "settings.json";
12401
- const AMP_SETTINGS_JSONC_FILE_NAME = "settings.jsonc";
12402
- //#endregion
12403
12987
  //#region src/types/mcp.ts
12404
12988
  const EnvVarNameSchema = zod_mini.z.string().check((0, zod_mini.refine)((value) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(value), "envVars entries must be valid environment variable names"));
12405
12989
  const McpServerSchema = zod_mini.z.looseObject({
@@ -13491,10 +14075,19 @@ function mapOauthFromCodex(oauth) {
13491
14075
  }
13492
14076
  const CODEX_MCP_SERVER_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
13493
14077
  function normalizeCodexMcpServerName(name) {
13494
- if (PROTOTYPE_POLLUTION_KEYS.has(name)) return null;
13495
- if (CODEX_MCP_SERVER_NAME_PATTERN.test(name)) return name;
14078
+ if (!PROTOTYPE_POLLUTION_KEYS.has(name) && CODEX_MCP_SERVER_NAME_PATTERN.test(name)) return {
14079
+ codexName: name,
14080
+ usedFallback: false
14081
+ };
13496
14082
  const normalizedName = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
13497
- return normalizedName && !PROTOTYPE_POLLUTION_KEYS.has(normalizedName) ? normalizedName : null;
14083
+ if (normalizedName && !PROTOTYPE_POLLUTION_KEYS.has(normalizedName)) return {
14084
+ codexName: normalizedName,
14085
+ usedFallback: false
14086
+ };
14087
+ return {
14088
+ codexName: `mcp_${(0, node_crypto.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`,
14089
+ usedFallback: true
14090
+ };
13498
14091
  }
13499
14092
  function convertFromCodexFormat(codexMcp) {
13500
14093
  const result = {};
@@ -13508,7 +14101,7 @@ function convertFromCodexFormat(codexMcp) {
13508
14101
  } else if (key === "oauth" && isRecord(value)) converted[key] = mapOauthFromCodex(value);
13509
14102
  else if (Object.hasOwn(CODEX_TO_RULESYNC_FIELD_MAP, key)) {
13510
14103
  const mappedKey = CODEX_TO_RULESYNC_FIELD_MAP[key];
13511
- if (mappedKey) if (isStringArray(value)) converted[mappedKey] = value;
14104
+ if (mappedKey) if (isStringArray$1(value)) converted[mappedKey] = value;
13512
14105
  else warnWithFallback(void 0, `Ignored malformed array for ${key} in MCP server ${name}`);
13513
14106
  } else converted[key] = value;
13514
14107
  }
@@ -13520,12 +14113,9 @@ function convertToCodexFormat(mcpServers) {
13520
14113
  const result = {};
13521
14114
  const originalNames = /* @__PURE__ */ new Map();
13522
14115
  for (const [name, config] of Object.entries(mcpServers)) {
13523
- const codexName = normalizeCodexMcpServerName(name);
13524
- if (codexName === null) {
13525
- warnWithFallback(void 0, `MCP server "${name}" could not be normalized to a valid Codex MCP server name and was skipped.`);
13526
- continue;
13527
- }
13528
14116
  if (!isRecord(config)) continue;
14117
+ const { codexName, usedFallback } = normalizeCodexMcpServerName(name);
14118
+ if (usedFallback) warnWithFallback(void 0, `MCP server "${name}" cannot be represented as a Codex MCP server name (ASCII [a-zA-Z0-9_-] only), so the stable fallback name "${codexName}" was used. Rename the server in .rulesync/mcp.json to choose a readable Codex name.`);
13529
14119
  const converted = {};
13530
14120
  for (const [key, value] of Object.entries(config)) {
13531
14121
  if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
@@ -13534,12 +14124,12 @@ function convertToCodexFormat(mcpServers) {
13534
14124
  } else if (key === "oauth" && isRecord(value)) converted[key] = mapOauthToCodex(value);
13535
14125
  else if (Object.hasOwn(RULESYNC_TO_CODEX_FIELD_MAP, key)) {
13536
14126
  const mappedKey = RULESYNC_TO_CODEX_FIELD_MAP[key];
13537
- if (mappedKey) if (isStringArray(value)) converted[mappedKey] = value;
14127
+ if (mappedKey) if (isStringArray$1(value)) converted[mappedKey] = value;
13538
14128
  else warnWithFallback(void 0, `[CodexCliMcp] Skipping invalid value type for mapped key '${key}': expected string array, got ${typeof value}`);
13539
14129
  } else converted[key] = value;
13540
14130
  }
13541
14131
  const previousName = originalNames.get(codexName);
13542
- if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}". Only the last processed server will be used.`);
14132
+ if (previousName !== void 0) warnWithFallback(void 0, `Codex MCP server name collision: "${previousName}" and "${name}" both normalize to "${codexName}"; "${name}" (processed last) overwrites "${previousName}".`);
13543
14133
  originalNames.set(codexName, name);
13544
14134
  result[codexName] = converted;
13545
14135
  }
@@ -13606,7 +14196,7 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
13606
14196
  const rawServer = isRecord(rawMcpServers) ? rawMcpServers[serverName] : void 0;
13607
14197
  return [serverName, {
13608
14198
  ...serverConfig,
13609
- ...isRecord(rawServer) && isStringArray(rawServer.envVars) ? { envVars: rawServer.envVars } : {}
14199
+ ...isRecord(rawServer) && isStringArray$1(rawServer.envVars) ? { envVars: rawServer.envVars } : {}
13610
14200
  }];
13611
14201
  })));
13612
14202
  const filteredMcpServers = this.removeEmptyEntries(converted);
@@ -14343,11 +14933,11 @@ function applyGooseStdioFields(ext, config) {
14343
14933
  if (Array.isArray(command)) {
14344
14934
  if (typeof command[0] === "string") ext.cmd = command[0];
14345
14935
  const rest = command.slice(1).filter((c) => typeof c === "string");
14346
- const args = isStringArray(config.args) ? config.args : [];
14936
+ const args = isStringArray$1(config.args) ? config.args : [];
14347
14937
  if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
14348
14938
  } else if (typeof command === "string") {
14349
14939
  ext.cmd = command;
14350
- if (isStringArray(config.args)) ext.args = config.args;
14940
+ if (isStringArray$1(config.args)) ext.args = config.args;
14351
14941
  }
14352
14942
  if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
14353
14943
  }
@@ -14405,7 +14995,7 @@ function convertFromGooseFormat(extensions) {
14405
14995
  else if (type === "streamable_http") server.type = "http";
14406
14996
  else if (type === "stdio") server.type = "stdio";
14407
14997
  if (typeof ext.cmd === "string") server.command = ext.cmd;
14408
- if (isStringArray(ext.args)) server.args = ext.args;
14998
+ if (isStringArray$1(ext.args)) server.args = ext.args;
14409
14999
  if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
14410
15000
  if (typeof ext.uri === "string") server.url = ext.uri;
14411
15001
  if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
@@ -14425,11 +15015,11 @@ function buildGoosePluginStdioServer(config) {
14425
15015
  if (Array.isArray(command)) {
14426
15016
  if (typeof command[0] === "string") server.command = command[0];
14427
15017
  const rest = command.slice(1).filter((c) => typeof c === "string");
14428
- const args = isStringArray(config.args) ? config.args : [];
15018
+ const args = isStringArray$1(config.args) ? config.args : [];
14429
15019
  if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
14430
15020
  } else if (typeof command === "string") {
14431
15021
  server.command = command;
14432
- if (isStringArray(config.args)) server.args = config.args;
15022
+ if (isStringArray$1(config.args)) server.args = config.args;
14433
15023
  }
14434
15024
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
14435
15025
  if (typeof config.cwd === "string") server.cwd = config.cwd;
@@ -14742,7 +15332,7 @@ function resolveHermesTimeout(config) {
14742
15332
  */
14743
15333
  function copyHermesAdvancedFields(source, target) {
14744
15334
  if (typeof source.auth === "string") target.auth = source.auth;
14745
- if (typeof source.client_cert === "string" || isStringArray(source.client_cert)) target.client_cert = source.client_cert;
15335
+ if (typeof source.client_cert === "string" || isStringArray$1(source.client_cert)) target.client_cert = source.client_cert;
14746
15336
  if (typeof source.client_key === "string") target.client_key = source.client_key;
14747
15337
  if (typeof source.connect_timeout === "number") target.connect_timeout = source.connect_timeout;
14748
15338
  if (typeof source.supports_parallel_tool_calls === "boolean") target.supports_parallel_tool_calls = source.supports_parallel_tool_calls;
@@ -14761,8 +15351,8 @@ function copyHermesAdvancedFields(source, target) {
14761
15351
  */
14762
15352
  function buildHermesToolsBlock(config) {
14763
15353
  const tools = {};
14764
- if (isStringArray(config.enabledTools)) tools.include = config.enabledTools;
14765
- if (isStringArray(config.disabledTools)) tools.exclude = config.disabledTools;
15354
+ if (isStringArray$1(config.enabledTools)) tools.include = config.enabledTools;
15355
+ if (isStringArray$1(config.disabledTools)) tools.exclude = config.disabledTools;
14766
15356
  if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
14767
15357
  if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
14768
15358
  return tools;
@@ -14774,8 +15364,8 @@ function buildHermesToolsBlock(config) {
14774
15364
  * `promptsEnabled`/`resourcesEnabled` top-level toggles.
14775
15365
  */
14776
15366
  function applyHermesToolsBlock(hermesTools, server) {
14777
- if (isStringArray(hermesTools.include)) server.enabledTools = hermesTools.include;
14778
- if (isStringArray(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
15367
+ if (isStringArray$1(hermesTools.include)) server.enabledTools = hermesTools.include;
15368
+ if (isStringArray$1(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
14779
15369
  if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
14780
15370
  if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
14781
15371
  }
@@ -14799,11 +15389,11 @@ function convertServerToHermes(config) {
14799
15389
  if (Array.isArray(command)) {
14800
15390
  if (typeof command[0] === "string") out.command = command[0];
14801
15391
  const rest = command.slice(1).filter((c) => typeof c === "string");
14802
- const args = isStringArray(config.args) ? config.args : [];
15392
+ const args = isStringArray$1(config.args) ? config.args : [];
14803
15393
  if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
14804
15394
  } else if (typeof command === "string") {
14805
15395
  out.command = command;
14806
- if (isStringArray(config.args)) out.args = config.args;
15396
+ if (isStringArray$1(config.args)) out.args = config.args;
14807
15397
  }
14808
15398
  if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
14809
15399
  } else if (url !== void 0) {
@@ -14851,7 +15441,7 @@ function convertFromHermesFormat(mcpServers) {
14851
15441
  if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord(config)) continue;
14852
15442
  const server = {};
14853
15443
  if (typeof config.command === "string") server.command = config.command;
14854
- if (isStringArray(config.args)) server.args = config.args;
15444
+ if (isStringArray$1(config.args)) server.args = config.args;
14855
15445
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
14856
15446
  if (typeof config.url === "string") server.url = config.url;
14857
15447
  if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
@@ -17176,7 +17766,11 @@ const VibePermissionsOverrideSchema = zod_mini.z.looseObject({ permission: zod_m
17176
17766
  */
17177
17767
  const CursorPermissionsOverrideSchema = zod_mini.z.looseObject({
17178
17768
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17179
- approvalMode: zod_mini.z.optional(zod_mini.z.string()),
17769
+ approvalMode: zod_mini.z.optional(zod_mini.z.enum([
17770
+ "allowlist",
17771
+ "auto-review",
17772
+ "unrestricted"
17773
+ ])),
17180
17774
  sandbox: zod_mini.z.optional(zod_mini.z.looseObject({}))
17181
17775
  });
17182
17776
  /**
@@ -17264,7 +17858,11 @@ const FactorydroidPermissionsOverrideSchema = zod_mini.z.looseObject({
17264
17858
  */
17265
17859
  const WarpPermissionsOverrideSchema = zod_mini.z.looseObject({
17266
17860
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17267
- agent_mode_coding_permissions: zod_mini.z.optional(zod_mini.z.string()),
17861
+ agent_mode_coding_permissions: zod_mini.z.optional(zod_mini.z.enum([
17862
+ "always_ask_before_reading",
17863
+ "always_allow_reading",
17864
+ "allow_reading_specific_files"
17865
+ ])),
17268
17866
  agent_mode_coding_file_read_allowlist: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
17269
17867
  agent_mode_execute_readonly_commands: zod_mini.z.optional(zod_mini.z.boolean())
17270
17868
  });
@@ -17310,7 +17908,11 @@ const JuniePermissionsOverrideSchema = zod_mini.z.looseObject({
17310
17908
  */
17311
17909
  const TaktPermissionsOverrideSchema = zod_mini.z.looseObject({
17312
17910
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17313
- step_permission_overrides: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.string())),
17911
+ step_permission_overrides: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.enum([
17912
+ "readonly",
17913
+ "edit",
17914
+ "full"
17915
+ ]))),
17314
17916
  provider_options: zod_mini.z.optional(zod_mini.z.looseObject({}))
17315
17917
  });
17316
17918
  /**
@@ -17340,9 +17942,15 @@ const AmpPermissionsOverrideSchema = zod_mini.z.looseObject({
17340
17942
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17341
17943
  permissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({
17342
17944
  tool: zod_mini.z.string(),
17343
- action: zod_mini.z.string()
17945
+ action: zod_mini.z.enum([
17946
+ "allow",
17947
+ "ask",
17948
+ "reject",
17949
+ "delegate"
17950
+ ]),
17951
+ context: zod_mini.z.optional(zod_mini.z.enum(["thread", "subagent"]))
17344
17952
  }))),
17345
- mcpPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({}))),
17953
+ mcpPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({ action: zod_mini.z.enum(["allow", "reject"]) }))),
17346
17954
  guardedFiles: zod_mini.z.optional(zod_mini.z.looseObject({ allowlist: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())) })),
17347
17955
  dangerouslyAllowAll: zod_mini.z.optional(zod_mini.z.boolean())
17348
17956
  });
@@ -17367,7 +17975,12 @@ const AmpPermissionsOverrideSchema = zod_mini.z.looseObject({
17367
17975
  */
17368
17976
  const AntigravityCliPermissionsOverrideSchema = zod_mini.z.looseObject({
17369
17977
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17370
- toolPermission: zod_mini.z.optional(zod_mini.z.string()),
17978
+ toolPermission: zod_mini.z.optional(zod_mini.z.enum([
17979
+ "request-review",
17980
+ "proceed-in-sandbox",
17981
+ "always-proceed",
17982
+ "strict"
17983
+ ])),
17371
17984
  enableTerminalSandbox: zod_mini.z.optional(zod_mini.z.boolean())
17372
17985
  });
17373
17986
  /**
@@ -17396,7 +18009,14 @@ const AugmentcodePermissionsOverrideSchema = zod_mini.z.looseObject({
17396
18009
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17397
18010
  toolPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({
17398
18011
  toolName: zod_mini.z.string(),
17399
- permission: zod_mini.z.looseObject({ type: zod_mini.z.string() })
18012
+ eventType: zod_mini.z.optional(zod_mini.z.enum(["tool-call", "tool-response"])),
18013
+ permission: zod_mini.z.looseObject({ type: zod_mini.z.enum([
18014
+ "allow",
18015
+ "deny",
18016
+ "ask-user",
18017
+ "webhook-policy",
18018
+ "script-policy"
18019
+ ]) })
17400
18020
  })))
17401
18021
  });
17402
18022
  /**
@@ -17486,12 +18106,20 @@ const CodexApprovalsReviewerSchema = zod_mini.z.enum([
17486
18106
  * `[permissions.rulesync]` profile may extend. Codex ships three built-in
17487
18107
  * profiles (`:read-only`, `:workspace`, `:danger-full-access`; the leading
17488
18108
  * 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.
18109
+ * `:danger-full-access` at config load time, so only these two are valid
18110
+ * `extends` parents. The value list is exported so the Codex CLI translator
18111
+ * derives its import-side baseline check from the same source.
17492
18112
  * @see https://learn.chatgpt.com/docs/permissions
17493
18113
  */
17494
- const CODEX_BASE_PERMISSION_PROFILES = [":read-only", ":workspace"];
18114
+ const CODEX_EXTENDABLE_BASELINE_PROFILES = [":read-only", ":workspace"];
18115
+ /**
18116
+ * All accepted `codexcli.base_permission_profile` values. `:danger-full-access`
18117
+ * cannot be an `extends` parent, so selecting it makes rulesync emit
18118
+ * `default_permissions = ":danger-full-access"` directly and skip the managed
18119
+ * `[permissions.rulesync]` profile entirely (there is no sandbox for
18120
+ * filesystem/network rules to refine in that mode).
18121
+ */
18122
+ const CODEX_BASE_PERMISSION_PROFILES = [...CODEX_EXTENDABLE_BASELINE_PROFILES, ":danger-full-access"];
17495
18123
  const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_PROFILES);
17496
18124
  /**
17497
18125
  * Codex CLI-scoped permission override.
@@ -17502,10 +18130,14 @@ const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_P
17502
18130
  * override whose fields are written verbatim as top-level `.codex/config.toml`
17503
18131
  * keys (the override wins per key; existing sibling keys the user set directly
17504
18132
  * 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.
18133
+ * - `base_permission_profile` — the built-in baseline profile (`:read-only` |
18134
+ * `:workspace` | `:danger-full-access`). Unlike the other keys it is not a
18135
+ * top-level config key: the extendable baselines are emitted as the managed
18136
+ * `[permissions.rulesync]` profile's `extends` value, while
18137
+ * `:danger-full-access` (which Codex rejects as an `extends` parent) is
18138
+ * selected directly via `default_permissions` and skips the managed profile
18139
+ * entirely — canonical filesystem/network rules are ignored in that mode.
18140
+ * Defaults to `:workspace` when unspecified.
17509
18141
  * - `approval_policy` — `untrusted` | `on-request` (legacy alias `on-failure`) |
17510
18142
  * `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
17511
18143
  * schema has required fields that are brittle to model as typed keys).
@@ -17526,13 +18158,14 @@ const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_P
17526
18158
  * Defaults to `auto_review` when neither the override nor the existing
17527
18159
  * config sets it.
17528
18160
  * - `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
18161
+ * emits the default `.git` carve-out (`".git/**" = "write"`). Codex's
17531
18162
  * `:workspace` baseline makes `.git` read-only, which denies basic git
17532
18163
  * 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.
18164
+ * logs; everyday commands like `git remote add` or `git push -u` write to
18165
+ * `.git/config`), so the carve-out is emitted by default. Defaults to
18166
+ * `true`; only an explicit `false` suppresses it. Like
18167
+ * `base_permission_profile` it is consumed by the profile builder, not
18168
+ * written as a top-level config key.
17536
18169
  *
17537
18170
  * Two surfaces are deliberately NOT authorable here so the override can never
17538
18171
  * clobber a feature-owned key: `mcp_servers.*` per-MCP gating is owned by the
@@ -19529,15 +20162,13 @@ const RULESYNC_PROFILE_NAME = "rulesync";
19529
20162
  const CODEX_WORKSPACE_ROOTS_KEY = ":workspace_roots";
19530
20163
  const CODEX_WORKSPACE_BASELINE = ":workspace";
19531
20164
  const CODEX_READ_ONLY_BASELINE = ":read-only";
19532
- const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_BASE_PERMISSION_PROFILES);
20165
+ const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
20166
+ const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_EXTENDABLE_BASELINE_PROFILES);
19533
20167
  const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
19534
20168
  const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
19535
20169
  const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
19536
20170
  const CODEX_MINIMAL_KEY = ":minimal";
19537
- const CODEX_GIT_WRITE_RULES = {
19538
- ".git/**": "write",
19539
- ".git/config": "read"
19540
- };
20171
+ const CODEX_GIT_WRITE_RULES = { ".git/**": "write" };
19541
20172
  const GLOBAL_WILDCARD_DOMAIN = "*";
19542
20173
  var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19543
20174
  static getSettablePaths(_options = {}) {
@@ -19565,8 +20196,40 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19565
20196
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
19566
20197
  const existingContent = await readFileContentOrNull(filePath) ?? "";
19567
20198
  const existing = toMutableTable(smol_toml.parse(existingContent || smol_toml.stringify({})));
20199
+ const canonicalConfig = rulesyncPermissions.getJson();
20200
+ if (canonicalConfig.codexcli?.base_permission_profile === CODEX_DANGER_FULL_ACCESS_BASELINE) {
20201
+ const ignoredCategories = Object.entries(canonicalConfig.permission).filter(([category, rules]) => category !== "bash" && Object.keys(rules).length > 0).map(([category]) => category);
20202
+ if (ignoredCategories.length > 0) logger?.warn(`Codex CLI baseline ":danger-full-access" removes the sandbox, so canonical ${ignoredCategories.join("/")} rules are not representable and are ignored for Codex CLI.`);
20203
+ const permissionsTable = toMutableTable(existing.permissions);
20204
+ if (permissionsTable[RULESYNC_PROFILE_NAME] !== void 0) {
20205
+ logger?.warn(`Codex CLI baseline ":danger-full-access" prunes the managed "[permissions.${RULESYNC_PROFILE_NAME}]" profile; any hand-written keys inside it (e.g. network settings) are removed. Move them to a sibling profile or re-add them after switching baselines.`);
20206
+ delete permissionsTable[RULESYNC_PROFILE_NAME];
20207
+ }
20208
+ const overridePatch = computeCodexcliOverridePatch({
20209
+ existing,
20210
+ override: canonicalConfig.codexcli,
20211
+ logger
20212
+ });
20213
+ return new CodexcliPermissions({
20214
+ outputRoot,
20215
+ relativeDirPath: paths.relativeDirPath,
20216
+ relativeFilePath: paths.relativeFilePath,
20217
+ fileContent: applySharedConfigPatch({
20218
+ fileKey: sharedConfigFileKey(paths),
20219
+ feature: "permissions",
20220
+ existingContent,
20221
+ patch: {
20222
+ permissions: Object.keys(permissionsTable).length > 0 ? permissionsTable : void 0,
20223
+ default_permissions: CODEX_DANGER_FULL_ACCESS_BASELINE,
20224
+ ...overridePatch
20225
+ },
20226
+ filePath
20227
+ }),
20228
+ validate
20229
+ });
20230
+ }
19568
20231
  const newProfile = convertRulesyncToCodexProfile({
19569
- config: rulesyncPermissions.getJson(),
20232
+ config: canonicalConfig,
19570
20233
  logger
19571
20234
  });
19572
20235
  const permissionsTable = toMutableTable(existing.permissions);
@@ -19627,6 +20290,7 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19627
20290
  });
19628
20291
  const override = extractCodexcliOverride(table);
19629
20292
  if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
20293
+ if (defaultProfile === CODEX_DANGER_FULL_ACCESS_BASELINE) override.base_permission_profile = CODEX_DANGER_FULL_ACCESS_BASELINE;
19630
20294
  const result = Object.keys(override).length > 0 ? {
19631
20295
  ...config,
19632
20296
  codexcli: override
@@ -19682,16 +20346,10 @@ function convertRulesyncToCodexProfile({ config, logger }) {
19682
20346
  const filesystem = { [CODEX_MINIMAL_KEY]: "read" };
19683
20347
  const workspaceRootFilesystem = {};
19684
20348
  const domains = {};
20349
+ const filesystemCategoryRules = {};
19685
20350
  for (const [toolName, rules] of Object.entries(config.permission)) {
19686
20351
  if (toolName === "read" || toolName === "edit" || toolName === "write") {
19687
- const mapAction = toolName === "read" ? mapReadAction : mapWriteAction;
19688
- for (const [pattern, action] of Object.entries(rules)) addFilesystemRule({
19689
- filesystem,
19690
- workspaceRootFilesystem,
19691
- pattern,
19692
- access: mapAction(action),
19693
- logger
19694
- });
20352
+ filesystemCategoryRules[toolName] = rules;
19695
20353
  continue;
19696
20354
  }
19697
20355
  if (toolName === "webfetch") {
@@ -19704,6 +20362,16 @@ function convertRulesyncToCodexProfile({ config, logger }) {
19704
20362
  }
19705
20363
  logger?.warn(`Codex CLI permissions support only read/edit/write/webfetch categories. Skipping: ${toolName}`);
19706
20364
  }
20365
+ for (const [pattern, access] of mergeFilesystemCategoryRules({
20366
+ categoryRules: filesystemCategoryRules,
20367
+ logger
20368
+ })) addFilesystemRule({
20369
+ filesystem,
20370
+ workspaceRootFilesystem,
20371
+ pattern,
20372
+ access,
20373
+ logger
20374
+ });
19707
20375
  applyDefaultGitWriteRules({
19708
20376
  config,
19709
20377
  filesystem,
@@ -19984,6 +20652,67 @@ function mapReadAction(action) {
19984
20652
  function mapWriteAction(action) {
19985
20653
  return action === "allow" ? "write" : "deny";
19986
20654
  }
20655
+ /**
20656
+ * Merge the canonical read/edit/write category rules into one Codex access
20657
+ * level per path pattern (Codex models a single `deny` < `read` < `write`
20658
+ * level, with no `ask`).
20659
+ *
20660
+ * - `edit` and `write` collapse onto Codex's write side; when both carry the
20661
+ * same pattern, the more restrictive action wins (`deny` > `ask` > `allow`).
20662
+ * - `read: allow` + write-side `allow` → `"write"`.
20663
+ * - `read: allow` + write-side non-allow → `"read"` (readable but not
20664
+ * writable — exactly what Codex's `"read"` level expresses).
20665
+ * - `read` non-allow → `"deny"` regardless of the write side; a contradictory
20666
+ * write-side `allow` (unreadable but writable is not expressible in Codex)
20667
+ * is warned about.
20668
+ * - Single-category patterns keep the existing mapReadAction/mapWriteAction
20669
+ * mappings.
20670
+ *
20671
+ * Iteration order is read → edit → write with first-seen pattern order, so
20672
+ * the emitted table is stable regardless of the authored category order.
20673
+ * Note the merge is one-way: `"{path}" = "read"` imports back as
20674
+ * `read: allow` only (the explicit write-side deny is implied by Codex's
20675
+ * access level and not re-materialized).
20676
+ */
20677
+ function mergeFilesystemCategoryRules({ categoryRules, logger }) {
20678
+ const readRules = categoryRules.read ?? {};
20679
+ const writeSideRestrictiveness = {
20680
+ deny: 2,
20681
+ ask: 1,
20682
+ allow: 0
20683
+ };
20684
+ const writeSideRules = {};
20685
+ for (const category of ["edit", "write"]) for (const [pattern, action] of Object.entries(categoryRules[category] ?? {})) {
20686
+ const existing = writeSideRules[pattern];
20687
+ if (existing === void 0 || writeSideRestrictiveness[action] > writeSideRestrictiveness[existing]) writeSideRules[pattern] = action;
20688
+ }
20689
+ const patterns = [];
20690
+ const seen = /* @__PURE__ */ new Set();
20691
+ for (const pattern of [...Object.keys(readRules), ...Object.keys(writeSideRules)]) if (!seen.has(pattern)) {
20692
+ seen.add(pattern);
20693
+ patterns.push(pattern);
20694
+ }
20695
+ const merged = [];
20696
+ for (const pattern of patterns) {
20697
+ const readAction = readRules[pattern];
20698
+ const writeAction = writeSideRules[pattern];
20699
+ if (readAction === void 0) {
20700
+ merged.push([pattern, mapWriteAction(writeAction)]);
20701
+ continue;
20702
+ }
20703
+ if (writeAction === void 0) {
20704
+ merged.push([pattern, mapReadAction(readAction)]);
20705
+ continue;
20706
+ }
20707
+ if (readAction === "allow") {
20708
+ merged.push([pattern, writeAction === "allow" ? "write" : "read"]);
20709
+ continue;
20710
+ }
20711
+ if (writeAction === "allow") logger?.warn(`Codex CLI cannot express "writable but not readable": pattern "${pattern}" has read: ${readAction} and a write-side allow. Emitting "deny".`);
20712
+ merged.push([pattern, "deny"]);
20713
+ }
20714
+ return merged;
20715
+ }
19987
20716
  function buildCodexBashRulesContent(config) {
19988
20717
  const bashRules = config.permission.bash ?? {};
19989
20718
  const entries = Object.entries(bashRules);
@@ -20935,7 +21664,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
20935
21664
  function convertGoosePermissionConfigToRulesync(userPermission) {
20936
21665
  const permission = {};
20937
21666
  for (const key of GOOSE_PERMISSION_LIST_KEYS) {
20938
- const toolNames = isStringArray(userPermission[key]) ? userPermission[key] : [];
21667
+ const toolNames = isStringArray$1(userPermission[key]) ? userPermission[key] : [];
20939
21668
  const action = GOOSE_LIST_TO_ACTION[key];
20940
21669
  for (const toolName of toolNames) {
20941
21670
  const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
@@ -21175,7 +21904,7 @@ const ACTION_RANK = {
21175
21904
  * (mirrors the Cursor adapter's preservation of unmanaged types).
21176
21905
  */
21177
21906
  function unmanagedEntries(existingPermission, key) {
21178
- return (isStringArray(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
21907
+ return (isStringArray$1(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
21179
21908
  }
21180
21909
  /**
21181
21910
  * Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
@@ -21216,9 +21945,9 @@ function buildGrokPermissionArrays(config, existingPermission, logger) {
21216
21945
  * arrays resolves to the strictest action.
21217
21946
  */
21218
21947
  function parseGrokPermissionArrays(permission) {
21219
- const allow = isStringArray(permission.allow) ? permission.allow : void 0;
21220
- const deny = isStringArray(permission.deny) ? permission.deny : void 0;
21221
- const ask = isStringArray(permission.ask) ? permission.ask : void 0;
21948
+ const allow = isStringArray$1(permission.allow) ? permission.allow : void 0;
21949
+ const deny = isStringArray$1(permission.deny) ? permission.deny : void 0;
21950
+ const ask = isStringArray$1(permission.ask) ? permission.ask : void 0;
21222
21951
  if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) === 0) return null;
21223
21952
  const result = {};
21224
21953
  const apply = (entries, action) => {
@@ -22999,7 +23728,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
22999
23728
  permission[category][CATCH_ALL_PATTERN$1] = value;
23000
23729
  }
23001
23730
  }
23002
- if (isStringArray(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
23731
+ if (isStringArray$1(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
23003
23732
  permission.read ??= {};
23004
23733
  permission.read[path] = "allow";
23005
23734
  }
@@ -23559,8 +24288,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
23559
24288
  const agents = isRecord(settings.agents) ? settings.agents : {};
23560
24289
  const profiles = isRecord(agents.profiles) ? agents.profiles : {};
23561
24290
  const config = convertWarpToRulesyncPermissions({
23562
- allow: isStringArray(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
23563
- deny: isStringArray(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
24291
+ allow: isStringArray$1(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
24292
+ deny: isStringArray$1(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
23564
24293
  });
23565
24294
  const warpOverride = {};
23566
24295
  for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
@@ -24140,9 +24869,6 @@ var PermissionsProcessor = class extends FeatureProcessor {
24140
24869
  }
24141
24870
  };
24142
24871
  //#endregion
24143
- //#region src/constants/general.ts
24144
- const SKILL_FILE_NAME = "SKILL.md";
24145
- //#endregion
24146
24872
  //#region src/types/ai-dir.ts
24147
24873
  var AiDir = class {
24148
24874
  /**
@@ -24343,10 +25069,10 @@ var ToolSkill = class extends AiDir {
24343
25069
  const settablePaths = getSettablePaths({ global });
24344
25070
  const actualRelativeDirPath = relativeDirPath ?? settablePaths.relativeDirPath;
24345
25071
  const skillDirPath = (0, node_path.join)(outputRoot, actualRelativeDirPath, dirName);
24346
- const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME);
24347
- if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME} not found in ${skillDirPath}`);
25072
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25073
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24348
25074
  const { frontmatter, body: content } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24349
- const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME);
25075
+ const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME$1);
24350
25076
  return {
24351
25077
  outputRoot,
24352
25078
  relativeDirPath: actualRelativeDirPath,
@@ -24388,7 +25114,7 @@ var SimulatedSkill = class extends ToolSkill {
24388
25114
  relativeDirPath,
24389
25115
  dirName,
24390
25116
  mainFile: {
24391
- name: SKILL_FILE_NAME,
25117
+ name: SKILL_FILE_NAME$1,
24392
25118
  body,
24393
25119
  frontmatter: { ...frontmatter }
24394
25120
  },
@@ -24446,12 +25172,12 @@ var SimulatedSkill = class extends ToolSkill {
24446
25172
  const settablePaths = this.getSettablePaths();
24447
25173
  const actualRelativeDirPath = relativeDirPath ?? settablePaths.relativeDirPath;
24448
25174
  const skillDirPath = (0, node_path.join)(outputRoot, actualRelativeDirPath, dirName);
24449
- const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME);
24450
- if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME} not found in ${skillDirPath}`);
25175
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25176
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24451
25177
  const { frontmatter, body: content } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24452
25178
  const result = SimulatedSkillFrontmatterSchema.safeParse(frontmatter);
24453
25179
  if (!result.success) throw new Error(`Invalid frontmatter in ${skillFilePath}: ${formatError(result.error)}`);
24454
- const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME);
25180
+ const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME$1);
24455
25181
  return {
24456
25182
  outputRoot,
24457
25183
  relativeDirPath: actualRelativeDirPath,
@@ -24661,7 +25387,7 @@ var RulesyncSkill = class RulesyncSkill extends AiDir {
24661
25387
  relativeDirPath,
24662
25388
  dirName,
24663
25389
  mainFile: {
24664
- name: SKILL_FILE_NAME,
25390
+ name: SKILL_FILE_NAME$1,
24665
25391
  body,
24666
25392
  frontmatter: { ...frontmatter }
24667
25393
  },
@@ -24696,13 +25422,13 @@ var RulesyncSkill = class RulesyncSkill extends AiDir {
24696
25422
  }
24697
25423
  static async fromDir({ outputRoot = process.cwd(), relativeDirPath = RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName, global = false }) {
24698
25424
  const skillDirPath = (0, node_path.join)(outputRoot, relativeDirPath, dirName);
24699
- const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME);
24700
- if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME} not found in ${skillDirPath}`);
25425
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25426
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24701
25427
  const { frontmatter, body: content, hasFrontmatter } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24702
25428
  if (!hasFrontmatter) throw new Error(`Missing frontmatter in ${skillFilePath}. Rulesync files must begin with a YAML frontmatter block delimited by '---'.`);
24703
25429
  const result = RulesyncSkillFrontmatterSchema.safeParse(frontmatter);
24704
25430
  if (!result.success) throw new Error(`Invalid frontmatter in ${skillFilePath}: ${formatError(result.error)}`);
24705
- const otherFiles = await this.collectOtherFiles(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME);
25431
+ const otherFiles = await this.collectOtherFiles(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME$1);
24706
25432
  return new RulesyncSkill({
24707
25433
  outputRoot,
24708
25434
  relativeDirPath,
@@ -24742,7 +25468,7 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24742
25468
  relativeDirPath,
24743
25469
  dirName,
24744
25470
  mainFile: {
24745
- name: SKILL_FILE_NAME,
25471
+ name: SKILL_FILE_NAME$1,
24746
25472
  body,
24747
25473
  frontmatter: { ...frontmatter }
24748
25474
  },
@@ -24769,7 +25495,7 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24769
25495
  validate() {
24770
25496
  if (!this.mainFile) return {
24771
25497
  success: false,
24772
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
25498
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
24773
25499
  };
24774
25500
  const result = RovodevSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
24775
25501
  if (!result.success) return {
@@ -24845,10 +25571,10 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24845
25571
  const result = RovodevSkillFrontmatterSchema.safeParse(loaded.frontmatter);
24846
25572
  if (!result.success) {
24847
25573
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
24848
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
25574
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
24849
25575
  }
24850
25576
  if (result.data.name !== loaded.dirName) {
24851
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
25577
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
24852
25578
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
24853
25579
  }
24854
25580
  return new RovodevSkill({
@@ -25013,7 +25739,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25013
25739
  relativeDirPath,
25014
25740
  dirName,
25015
25741
  mainFile: {
25016
- name: SKILL_FILE_NAME,
25742
+ name: SKILL_FILE_NAME$1,
25017
25743
  body,
25018
25744
  frontmatter: { ...frontmatter }
25019
25745
  },
@@ -25037,7 +25763,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25037
25763
  validate() {
25038
25764
  if (!this.mainFile) return {
25039
25765
  success: false,
25040
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
25766
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25041
25767
  };
25042
25768
  const result = AgentsSkillsSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25043
25769
  if (!result.success) return {
@@ -25109,7 +25835,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25109
25835
  const result = AgentsSkillsSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25110
25836
  if (!result.success) {
25111
25837
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25112
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
25838
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25113
25839
  }
25114
25840
  return new this({
25115
25841
  outputRoot: loaded.outputRoot,
@@ -25166,7 +25892,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25166
25892
  relativeDirPath,
25167
25893
  dirName,
25168
25894
  mainFile: {
25169
- name: SKILL_FILE_NAME,
25895
+ name: SKILL_FILE_NAME$1,
25170
25896
  body,
25171
25897
  frontmatter: { ...frontmatter }
25172
25898
  },
@@ -25191,7 +25917,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25191
25917
  validate() {
25192
25918
  if (!this.mainFile) return {
25193
25919
  success: false,
25194
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
25920
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25195
25921
  };
25196
25922
  const result = AiassistantSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25197
25923
  if (!result.success) return {
@@ -25251,7 +25977,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25251
25977
  const result = AiassistantSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25252
25978
  if (!result.success) {
25253
25979
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25254
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
25980
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25255
25981
  }
25256
25982
  return new AiassistantSkill({
25257
25983
  outputRoot: loaded.outputRoot,
@@ -25302,7 +26028,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25302
26028
  relativeDirPath,
25303
26029
  dirName,
25304
26030
  mainFile: {
25305
- name: SKILL_FILE_NAME,
26031
+ name: SKILL_FILE_NAME$1,
25306
26032
  body,
25307
26033
  frontmatter: { ...frontmatter }
25308
26034
  },
@@ -25326,7 +26052,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25326
26052
  validate() {
25327
26053
  if (!this.mainFile) return {
25328
26054
  success: false,
25329
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26055
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25330
26056
  };
25331
26057
  const result = AmpSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25332
26058
  if (!result.success) return {
@@ -25386,7 +26112,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25386
26112
  const result = AmpSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25387
26113
  if (!result.success) {
25388
26114
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25389
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
26115
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25390
26116
  }
25391
26117
  return new AmpSkill({
25392
26118
  outputRoot: loaded.outputRoot,
@@ -25442,7 +26168,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25442
26168
  relativeDirPath,
25443
26169
  dirName,
25444
26170
  mainFile: {
25445
- name: SKILL_FILE_NAME,
26171
+ name: SKILL_FILE_NAME$1,
25446
26172
  body,
25447
26173
  frontmatter: { ...frontmatter }
25448
26174
  },
@@ -25475,7 +26201,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25475
26201
  validate() {
25476
26202
  if (this.mainFile === void 0) return {
25477
26203
  success: false,
25478
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26204
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25479
26205
  };
25480
26206
  const result = AntigravitySkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25481
26207
  if (!result.success) return {
@@ -25535,7 +26261,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25535
26261
  const result = AntigravitySkillFrontmatterSchema.safeParse(loaded.frontmatter);
25536
26262
  if (!result.success) {
25537
26263
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25538
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
26264
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25539
26265
  }
25540
26266
  return new this({
25541
26267
  outputRoot: loaded.outputRoot,
@@ -25619,7 +26345,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25619
26345
  relativeDirPath,
25620
26346
  dirName,
25621
26347
  mainFile: {
25622
- name: SKILL_FILE_NAME,
26348
+ name: SKILL_FILE_NAME$1,
25623
26349
  body,
25624
26350
  frontmatter: { ...frontmatter }
25625
26351
  },
@@ -25646,7 +26372,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25646
26372
  validate() {
25647
26373
  if (this.mainFile === void 0) return {
25648
26374
  success: false,
25649
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26375
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25650
26376
  };
25651
26377
  const result = AugmentcodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25652
26378
  if (!result.success) return {
@@ -25706,7 +26432,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25706
26432
  const result = AugmentcodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25707
26433
  if (!result.success) {
25708
26434
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25709
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
26435
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25710
26436
  }
25711
26437
  return new AugmentcodeSkill({
25712
26438
  outputRoot: loaded.outputRoot,
@@ -25847,7 +26573,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25847
26573
  relativeDirPath,
25848
26574
  dirName,
25849
26575
  mainFile: {
25850
- name: SKILL_FILE_NAME,
26576
+ name: SKILL_FILE_NAME$1,
25851
26577
  body,
25852
26578
  frontmatter: { ...frontmatter }
25853
26579
  },
@@ -25874,7 +26600,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25874
26600
  validate() {
25875
26601
  if (this.mainFile === void 0) return {
25876
26602
  success: false,
25877
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26603
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25878
26604
  };
25879
26605
  const result = ClaudecodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25880
26606
  if (!result.success) return {
@@ -25962,7 +26688,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25962
26688
  const result = ClaudecodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25963
26689
  if (!result.success) {
25964
26690
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
25965
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
26691
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25966
26692
  }
25967
26693
  return new ClaudecodeSkill({
25968
26694
  outputRoot: loaded.outputRoot,
@@ -26008,7 +26734,7 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26008
26734
  relativeDirPath,
26009
26735
  dirName,
26010
26736
  mainFile: {
26011
- name: SKILL_FILE_NAME,
26737
+ name: SKILL_FILE_NAME$1,
26012
26738
  body,
26013
26739
  frontmatter: { ...frontmatter }
26014
26740
  },
@@ -26032,7 +26758,7 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26032
26758
  validate() {
26033
26759
  if (!this.mainFile) return {
26034
26760
  success: false,
26035
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26761
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26036
26762
  };
26037
26763
  const result = ClineSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26038
26764
  if (!result.success) return {
@@ -26096,10 +26822,10 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26096
26822
  const result = ClineSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26097
26823
  if (!result.success) {
26098
26824
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26099
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
26825
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26100
26826
  }
26101
26827
  if (result.data.name !== loaded.dirName) {
26102
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
26828
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
26103
26829
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
26104
26830
  }
26105
26831
  return new ClineSkill({
@@ -26213,7 +26939,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26213
26939
  relativeDirPath,
26214
26940
  dirName,
26215
26941
  mainFile: {
26216
- name: SKILL_FILE_NAME,
26942
+ name: SKILL_FILE_NAME$1,
26217
26943
  body,
26218
26944
  frontmatter: { ...frontmatter }
26219
26945
  },
@@ -26237,7 +26963,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26237
26963
  validate() {
26238
26964
  if (!this.mainFile) return {
26239
26965
  success: false,
26240
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26966
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26241
26967
  };
26242
26968
  const result = CodexCliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26243
26969
  if (!result.success) return {
@@ -26315,7 +27041,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26315
27041
  const result = CodexCliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26316
27042
  if (!result.success) {
26317
27043
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26318
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27044
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26319
27045
  }
26320
27046
  return new CodexCliSkill({
26321
27047
  outputRoot: loaded.outputRoot,
@@ -26367,7 +27093,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26367
27093
  relativeDirPath,
26368
27094
  dirName,
26369
27095
  mainFile: {
26370
- name: SKILL_FILE_NAME,
27096
+ name: SKILL_FILE_NAME$1,
26371
27097
  body,
26372
27098
  frontmatter: { ...frontmatter }
26373
27099
  },
@@ -26392,7 +27118,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26392
27118
  validate() {
26393
27119
  if (!this.mainFile) return {
26394
27120
  success: false,
26395
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27121
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26396
27122
  };
26397
27123
  const result = CopilotSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26398
27124
  if (!result.success) return {
@@ -26459,7 +27185,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26459
27185
  const result = CopilotSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26460
27186
  if (!result.success) {
26461
27187
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26462
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27188
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26463
27189
  }
26464
27190
  return new CopilotSkill({
26465
27191
  outputRoot: loaded.outputRoot,
@@ -26513,7 +27239,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26513
27239
  relativeDirPath,
26514
27240
  dirName,
26515
27241
  mainFile: {
26516
- name: SKILL_FILE_NAME,
27242
+ name: SKILL_FILE_NAME$1,
26517
27243
  body,
26518
27244
  frontmatter: { ...frontmatter }
26519
27245
  },
@@ -26538,7 +27264,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26538
27264
  validate() {
26539
27265
  if (!this.mainFile) return {
26540
27266
  success: false,
26541
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27267
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26542
27268
  };
26543
27269
  const result = CopilotcliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26544
27270
  if (!result.success) return {
@@ -26607,7 +27333,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26607
27333
  const result = CopilotcliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26608
27334
  if (!result.success) {
26609
27335
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26610
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27336
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26611
27337
  }
26612
27338
  return new CopilotcliSkill({
26613
27339
  outputRoot: loaded.outputRoot,
@@ -26657,7 +27383,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26657
27383
  relativeDirPath,
26658
27384
  dirName,
26659
27385
  mainFile: {
26660
- name: SKILL_FILE_NAME,
27386
+ name: SKILL_FILE_NAME$1,
26661
27387
  body,
26662
27388
  frontmatter: { ...frontmatter }
26663
27389
  },
@@ -26681,7 +27407,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26681
27407
  validate() {
26682
27408
  if (!this.mainFile) return {
26683
27409
  success: false,
26684
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27410
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26685
27411
  };
26686
27412
  const result = CursorSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26687
27413
  if (!result.success) return {
@@ -26755,7 +27481,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26755
27481
  const result = CursorSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26756
27482
  if (!result.success) {
26757
27483
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26758
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27484
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26759
27485
  }
26760
27486
  return new CursorSkill({
26761
27487
  outputRoot: loaded.outputRoot,
@@ -26802,7 +27528,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26802
27528
  relativeDirPath,
26803
27529
  dirName,
26804
27530
  mainFile: {
26805
- name: SKILL_FILE_NAME,
27531
+ name: SKILL_FILE_NAME$1,
26806
27532
  body,
26807
27533
  frontmatter: { ...frontmatter }
26808
27534
  },
@@ -26826,7 +27552,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26826
27552
  validate() {
26827
27553
  if (!this.mainFile) return {
26828
27554
  success: false,
26829
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27555
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26830
27556
  };
26831
27557
  const result = DeepagentsSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26832
27558
  if (!result.success) return {
@@ -26902,7 +27628,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26902
27628
  const result = DeepagentsSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26903
27629
  if (!result.success) {
26904
27630
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
26905
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27631
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26906
27632
  }
26907
27633
  return new DeepagentsSkill({
26908
27634
  outputRoot: loaded.outputRoot,
@@ -26953,7 +27679,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
26953
27679
  relativeDirPath,
26954
27680
  dirName,
26955
27681
  mainFile: {
26956
- name: SKILL_FILE_NAME,
27682
+ name: SKILL_FILE_NAME$1,
26957
27683
  body,
26958
27684
  frontmatter: { ...frontmatter }
26959
27685
  },
@@ -26978,7 +27704,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
26978
27704
  validate() {
26979
27705
  if (!this.mainFile) return {
26980
27706
  success: false,
26981
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27707
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26982
27708
  };
26983
27709
  const result = DevinSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26984
27710
  if (!result.success) return {
@@ -27030,6 +27756,18 @@ var DevinSkill = class DevinSkill extends ToolSkill {
27030
27756
  const targets = rulesyncSkill.getFrontmatter().targets;
27031
27757
  return targets.includes("*") || targets.includes("devin");
27032
27758
  }
27759
+ /**
27760
+ * Commands are emitted into this same skills tree as `<slug>/SKILL.md`
27761
+ * (see `DevinCommand`), so a directory matching a current rulesync command
27762
+ * slug is owned by the commands feature: it must not be imported as a
27763
+ * skill nor deleted as an orphan skill.
27764
+ */
27765
+ static async isDirOwned({ dirName, inputRoot }) {
27766
+ return !await rulesyncCommandSlugExists({
27767
+ inputRoot,
27768
+ dirName
27769
+ });
27770
+ }
27033
27771
  static async fromDir(params) {
27034
27772
  const loaded = await this.loadSkillDirContent({
27035
27773
  ...params,
@@ -27038,7 +27776,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
27038
27776
  const result = DevinSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27039
27777
  if (!result.success) {
27040
27778
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27041
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27779
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27042
27780
  }
27043
27781
  return new DevinSkill({
27044
27782
  outputRoot: loaded.outputRoot,
@@ -27090,7 +27828,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27090
27828
  relativeDirPath,
27091
27829
  dirName,
27092
27830
  mainFile: {
27093
- name: SKILL_FILE_NAME,
27831
+ name: SKILL_FILE_NAME$1,
27094
27832
  body,
27095
27833
  frontmatter: { ...frontmatter }
27096
27834
  },
@@ -27114,7 +27852,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27114
27852
  validate() {
27115
27853
  if (this.mainFile === void 0) return {
27116
27854
  success: false,
27117
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27855
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27118
27856
  };
27119
27857
  const result = FactorydroidSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27120
27858
  if (!result.success) return {
@@ -27189,7 +27927,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27189
27927
  const result = FactorydroidSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27190
27928
  if (!result.success) {
27191
27929
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27192
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
27930
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27193
27931
  }
27194
27932
  return new FactorydroidSkill({
27195
27933
  outputRoot: loaded.outputRoot,
@@ -27236,7 +27974,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27236
27974
  relativeDirPath,
27237
27975
  dirName,
27238
27976
  mainFile: {
27239
- name: SKILL_FILE_NAME,
27977
+ name: SKILL_FILE_NAME$1,
27240
27978
  body,
27241
27979
  frontmatter: { ...frontmatter }
27242
27980
  },
@@ -27261,7 +27999,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27261
27999
  validate() {
27262
28000
  if (!this.mainFile) return {
27263
28001
  success: false,
27264
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28002
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27265
28003
  };
27266
28004
  const result = GooseSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27267
28005
  if (!result.success) return {
@@ -27321,7 +28059,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27321
28059
  const result = GooseSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27322
28060
  if (!result.success) {
27323
28061
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27324
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28062
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27325
28063
  }
27326
28064
  return new GooseSkill({
27327
28065
  outputRoot: loaded.outputRoot,
@@ -27374,7 +28112,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27374
28112
  relativeDirPath,
27375
28113
  dirName,
27376
28114
  mainFile: {
27377
- name: SKILL_FILE_NAME,
28115
+ name: SKILL_FILE_NAME$1,
27378
28116
  body,
27379
28117
  frontmatter: { ...frontmatter }
27380
28118
  },
@@ -27398,7 +28136,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27398
28136
  validate() {
27399
28137
  if (this.mainFile === void 0) return {
27400
28138
  success: false,
27401
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28139
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27402
28140
  };
27403
28141
  const result = GrokcliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27404
28142
  if (!result.success) return {
@@ -27458,7 +28196,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27458
28196
  const result = GrokcliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27459
28197
  if (!result.success) {
27460
28198
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27461
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28199
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27462
28200
  }
27463
28201
  return new GrokcliSkill({
27464
28202
  outputRoot: loaded.outputRoot,
@@ -27493,6 +28231,18 @@ var HermesagentSkill = class extends AgentsSkillsSkill {
27493
28231
  static getSettablePaths() {
27494
28232
  return { relativeDirPath: HERMESAGENT_SKILLS_DIR_PATH };
27495
28233
  }
28234
+ /**
28235
+ * Commands are emitted into this same skills tree as `<slug>/SKILL.md`
28236
+ * (see `HermesagentCommand`), so a directory matching a current rulesync
28237
+ * command slug is owned by the commands feature: it must not be imported
28238
+ * as a skill nor deleted as an orphan skill.
28239
+ */
28240
+ static async isDirOwned({ dirName, inputRoot }) {
28241
+ return !await rulesyncCommandSlugExists({
28242
+ inputRoot,
28243
+ dirName
28244
+ });
28245
+ }
27496
28246
  constructor(params) {
27497
28247
  super({
27498
28248
  ...params,
@@ -27517,7 +28267,7 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27517
28267
  relativeDirPath,
27518
28268
  dirName,
27519
28269
  mainFile: {
27520
- name: SKILL_FILE_NAME,
28270
+ name: SKILL_FILE_NAME$1,
27521
28271
  body,
27522
28272
  frontmatter: { ...frontmatter }
27523
28273
  },
@@ -27541,7 +28291,7 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27541
28291
  validate() {
27542
28292
  if (!this.mainFile) return {
27543
28293
  success: false,
27544
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28294
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27545
28295
  };
27546
28296
  const result = JunieSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27547
28297
  if (!result.success) return {
@@ -27605,10 +28355,10 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27605
28355
  const result = JunieSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27606
28356
  if (!result.success) {
27607
28357
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27608
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28358
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27609
28359
  }
27610
28360
  if (result.data.name !== loaded.dirName) {
27611
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
28361
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
27612
28362
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
27613
28363
  }
27614
28364
  return new JunieSkill({
@@ -27656,7 +28406,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27656
28406
  relativeDirPath,
27657
28407
  dirName,
27658
28408
  mainFile: {
27659
- name: SKILL_FILE_NAME,
28409
+ name: SKILL_FILE_NAME$1,
27660
28410
  body,
27661
28411
  frontmatter: { ...frontmatter }
27662
28412
  },
@@ -27680,7 +28430,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27680
28430
  validate() {
27681
28431
  if (this.mainFile === void 0) return {
27682
28432
  success: false,
27683
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28433
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27684
28434
  };
27685
28435
  const result = KiloSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27686
28436
  if (!result.success) return {
@@ -27752,7 +28502,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27752
28502
  const result = KiloSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27753
28503
  if (!result.success) {
27754
28504
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27755
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28505
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27756
28506
  }
27757
28507
  return new KiloSkill({
27758
28508
  outputRoot: loaded.outputRoot,
@@ -27798,7 +28548,7 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27798
28548
  relativeDirPath,
27799
28549
  dirName,
27800
28550
  mainFile: {
27801
- name: SKILL_FILE_NAME,
28551
+ name: SKILL_FILE_NAME$1,
27802
28552
  body,
27803
28553
  frontmatter: { ...frontmatter }
27804
28554
  },
@@ -27822,7 +28572,7 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27822
28572
  validate() {
27823
28573
  if (!this.mainFile) return {
27824
28574
  success: false,
27825
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28575
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27826
28576
  };
27827
28577
  const result = KiroSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27828
28578
  if (!result.success) return {
@@ -27886,10 +28636,10 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27886
28636
  const result = KiroSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27887
28637
  if (!result.success) {
27888
28638
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
27889
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28639
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27890
28640
  }
27891
28641
  if (result.data.name !== loaded.dirName) {
27892
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
28642
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
27893
28643
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
27894
28644
  }
27895
28645
  return new KiroSkill({
@@ -27972,7 +28722,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
27972
28722
  relativeDirPath,
27973
28723
  dirName,
27974
28724
  mainFile: {
27975
- name: SKILL_FILE_NAME,
28725
+ name: SKILL_FILE_NAME$1,
27976
28726
  body,
27977
28727
  frontmatter: { ...frontmatter }
27978
28728
  },
@@ -27999,7 +28749,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
27999
28749
  validate() {
28000
28750
  if (this.mainFile === void 0) return {
28001
28751
  success: false,
28002
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28752
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28003
28753
  };
28004
28754
  const result = OpenCodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28005
28755
  if (!result.success) return {
@@ -28078,7 +28828,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
28078
28828
  const result = OpenCodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28079
28829
  if (!result.success) {
28080
28830
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28081
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28831
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28082
28832
  }
28083
28833
  return new OpenCodeSkill({
28084
28834
  outputRoot: loaded.outputRoot,
@@ -28139,7 +28889,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28139
28889
  relativeDirPath: resolvedDirPath,
28140
28890
  dirName,
28141
28891
  mainFile: {
28142
- name: SKILL_FILE_NAME,
28892
+ name: SKILL_FILE_NAME$1,
28143
28893
  body,
28144
28894
  frontmatter: { ...frontmatter }
28145
28895
  },
@@ -28164,7 +28914,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28164
28914
  validate() {
28165
28915
  if (!this.mainFile) return {
28166
28916
  success: false,
28167
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28917
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28168
28918
  };
28169
28919
  const result = PiSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28170
28920
  if (!result.success) return {
@@ -28239,7 +28989,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28239
28989
  const result = PiSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28240
28990
  if (!result.success) {
28241
28991
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28242
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
28992
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28243
28993
  }
28244
28994
  return new PiSkill({
28245
28995
  outputRoot: loaded.outputRoot,
@@ -28292,7 +29042,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28292
29042
  relativeDirPath,
28293
29043
  dirName,
28294
29044
  mainFile: {
28295
- name: SKILL_FILE_NAME,
29045
+ name: SKILL_FILE_NAME$1,
28296
29046
  body,
28297
29047
  frontmatter: { ...frontmatter }
28298
29048
  },
@@ -28316,7 +29066,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28316
29066
  validate() {
28317
29067
  if (this.mainFile === void 0) return {
28318
29068
  success: false,
28319
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29069
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28320
29070
  };
28321
29071
  const result = QwencodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28322
29072
  if (!result.success) return {
@@ -28396,7 +29146,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28396
29146
  const result = QwencodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28397
29147
  if (!result.success) {
28398
29148
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28399
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
29149
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28400
29150
  }
28401
29151
  return new QwencodeSkill({
28402
29152
  outputRoot: loaded.outputRoot,
@@ -28446,7 +29196,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28446
29196
  relativeDirPath,
28447
29197
  dirName,
28448
29198
  mainFile: {
28449
- name: SKILL_FILE_NAME,
29199
+ name: SKILL_FILE_NAME$1,
28450
29200
  body,
28451
29201
  frontmatter: { ...frontmatter }
28452
29202
  },
@@ -28470,7 +29220,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28470
29220
  validate() {
28471
29221
  if (this.mainFile === void 0) return {
28472
29222
  success: false,
28473
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29223
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28474
29224
  };
28475
29225
  const result = ReasonixSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28476
29226
  if (!result.success) return {
@@ -28530,7 +29280,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28530
29280
  const result = ReasonixSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28531
29281
  if (!result.success) {
28532
29282
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28533
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
29283
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28534
29284
  }
28535
29285
  return new ReasonixSkill({
28536
29286
  outputRoot: loaded.outputRoot,
@@ -28553,7 +29303,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28553
29303
  * skills-feature ownership, matching the previous behavior for such dirs.
28554
29304
  */
28555
29305
  static async isDirOwned({ outputRoot, relativeDirPath, dirName }) {
28556
- const skillFilePath = (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME);
29306
+ const skillFilePath = (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME$1);
28557
29307
  try {
28558
29308
  const { frontmatter } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
28559
29309
  return frontmatter["runAs"] !== REASONIX_SUBAGENT_RUN_AS;
@@ -28603,7 +29353,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28603
29353
  relativeDirPath,
28604
29354
  dirName,
28605
29355
  mainFile: {
28606
- name: SKILL_FILE_NAME,
29356
+ name: SKILL_FILE_NAME$1,
28607
29357
  body,
28608
29358
  frontmatter: { ...frontmatter }
28609
29359
  },
@@ -28627,7 +29377,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28627
29377
  validate() {
28628
29378
  if (!this.mainFile) return {
28629
29379
  success: false,
28630
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29380
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28631
29381
  };
28632
29382
  const result = ReplitSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28633
29383
  if (!result.success) return {
@@ -28695,7 +29445,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28695
29445
  const result = ReplitSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28696
29446
  if (!result.success) {
28697
29447
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28698
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
29448
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28699
29449
  }
28700
29450
  return new ReplitSkill({
28701
29451
  outputRoot: loaded.outputRoot,
@@ -28742,7 +29492,7 @@ var RooSkill = class RooSkill extends ToolSkill {
28742
29492
  relativeDirPath,
28743
29493
  dirName,
28744
29494
  mainFile: {
28745
- name: SKILL_FILE_NAME,
29495
+ name: SKILL_FILE_NAME$1,
28746
29496
  body,
28747
29497
  frontmatter: { ...frontmatter }
28748
29498
  },
@@ -28766,7 +29516,7 @@ var RooSkill = class RooSkill extends ToolSkill {
28766
29516
  validate() {
28767
29517
  if (!this.mainFile) return {
28768
29518
  success: false,
28769
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29519
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28770
29520
  };
28771
29521
  const result = RooSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28772
29522
  if (!result.success) return {
@@ -28830,10 +29580,10 @@ var RooSkill = class RooSkill extends ToolSkill {
28830
29580
  const result = RooSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28831
29581
  if (!result.success) {
28832
29582
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
28833
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
29583
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28834
29584
  }
28835
29585
  if (result.data.name !== loaded.dirName) {
28836
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
29586
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
28837
29587
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
28838
29588
  }
28839
29589
  return new RooSkill({
@@ -29050,7 +29800,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29050
29800
  relativeDirPath,
29051
29801
  dirName,
29052
29802
  mainFile: {
29053
- name: SKILL_FILE_NAME,
29803
+ name: SKILL_FILE_NAME$1,
29054
29804
  body,
29055
29805
  frontmatter: { ...frontmatter }
29056
29806
  },
@@ -29077,7 +29827,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29077
29827
  validate() {
29078
29828
  if (!this.mainFile) return {
29079
29829
  success: false,
29080
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29830
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29081
29831
  };
29082
29832
  const result = VibeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29083
29833
  if (!result.success) return {
@@ -29141,7 +29891,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29141
29891
  const result = VibeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29142
29892
  if (!result.success) {
29143
29893
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
29144
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
29894
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29145
29895
  }
29146
29896
  return new VibeSkill({
29147
29897
  outputRoot: loaded.outputRoot,
@@ -29197,7 +29947,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29197
29947
  relativeDirPath,
29198
29948
  dirName,
29199
29949
  mainFile: {
29200
- name: SKILL_FILE_NAME,
29950
+ name: SKILL_FILE_NAME$1,
29201
29951
  body,
29202
29952
  frontmatter: { ...frontmatter }
29203
29953
  },
@@ -29221,7 +29971,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29221
29971
  validate() {
29222
29972
  if (this.mainFile === void 0) return {
29223
29973
  success: false,
29224
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29974
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29225
29975
  };
29226
29976
  const result = WarpSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29227
29977
  if (!result.success) return {
@@ -29281,7 +30031,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29281
30031
  const result = WarpSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29282
30032
  if (!result.success) {
29283
30033
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
29284
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
30034
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29285
30035
  }
29286
30036
  return new WarpSkill({
29287
30037
  outputRoot: loaded.outputRoot,
@@ -29329,7 +30079,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29329
30079
  relativeDirPath,
29330
30080
  dirName,
29331
30081
  mainFile: {
29332
- name: SKILL_FILE_NAME,
30082
+ name: SKILL_FILE_NAME$1,
29333
30083
  body,
29334
30084
  frontmatter: { ...frontmatter }
29335
30085
  },
@@ -29353,7 +30103,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29353
30103
  validate() {
29354
30104
  if (!this.mainFile) return {
29355
30105
  success: false,
29356
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
30106
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29357
30107
  };
29358
30108
  const result = ZedSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29359
30109
  if (!result.success) return {
@@ -29422,7 +30172,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29422
30172
  const result = ZedSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29423
30173
  if (!result.success) {
29424
30174
  const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
29425
- throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
30175
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29426
30176
  }
29427
30177
  return new ZedSkill({
29428
30178
  outputRoot: loaded.outputRoot,
@@ -29862,7 +30612,8 @@ var SkillsProcessor = class extends DirFeatureProcessor {
29862
30612
  if (factory.class.isDirOwned && !await factory.class.isDirOwned({
29863
30613
  outputRoot: this.outputRoot,
29864
30614
  relativeDirPath: root,
29865
- dirName
30615
+ dirName,
30616
+ inputRoot: this.inputRoot
29866
30617
  })) continue;
29867
30618
  seenDirNames.add(dirName);
29868
30619
  loadEntries.push({
@@ -29893,7 +30644,8 @@ var SkillsProcessor = class extends DirFeatureProcessor {
29893
30644
  if (factory.class.isDirOwned && !await factory.class.isDirOwned({
29894
30645
  outputRoot: this.outputRoot,
29895
30646
  relativeDirPath: root,
29896
- dirName
30647
+ dirName,
30648
+ inputRoot: this.inputRoot
29897
30649
  })) continue;
29898
30650
  const toolSkill = factory.class.forDeletion({
29899
30651
  outputRoot: this.outputRoot,
@@ -33294,7 +34046,7 @@ var ReasonixSubagent = class ReasonixSubagent extends ToolSubagent {
33294
34046
  frontmatter: reasonixFrontmatter,
33295
34047
  body,
33296
34048
  relativeDirPath: paths.relativeDirPath,
33297
- relativeFilePath: (0, node_path.join)(subagentName, SKILL_FILE_NAME),
34049
+ relativeFilePath: (0, node_path.join)(subagentName, SKILL_FILE_NAME$1),
33298
34050
  fileContent,
33299
34051
  validate,
33300
34052
  global
@@ -34300,6 +35052,7 @@ const RulesyncRuleFrontmatterSchema = zod_mini.z.object({
34300
35052
  name: zod_mini.z.optional(zod_mini.z.string()),
34301
35053
  description: zod_mini.z.optional(zod_mini.z.string())
34302
35054
  })),
35055
+ pi: zod_mini.z.optional(zod_mini.z.looseObject({ systemPrompt: zod_mini.z.optional(zod_mini.z.enum(["append"])) })),
34303
35056
  takt: zod_mini.z.optional(zod_mini.z.looseObject({
34304
35057
  name: zod_mini.z.optional(zod_mini.z.string()),
34305
35058
  extends: zod_mini.z.optional(zod_mini.z.string()),
@@ -34463,6 +35216,16 @@ var ToolRule = class extends ToolFile {
34463
35216
  isRoot() {
34464
35217
  return this.root;
34465
35218
  }
35219
+ /**
35220
+ * Whether this rule must be left out of the root rule's reference/MCP
35221
+ * instruction listings even though it is a non-root survivor. Used by files
35222
+ * the tool loads through its own mechanism (e.g. Pi's `APPEND_SYSTEM.md`,
35223
+ * which Pi appends to the system prompt itself — referencing it from
35224
+ * `AGENTS.md` would double-load the content).
35225
+ */
35226
+ isExcludedFromRootReferences() {
35227
+ return false;
35228
+ }
34466
35229
  getDescription() {
34467
35230
  return this.description;
34468
35231
  }
@@ -36809,6 +37572,15 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
36809
37572
  * is `undefined`). This mirrors the grokcli, warp, and deepagents targets.
36810
37573
  *
36811
37574
  * Goose uses plain markdown files (.goosehints) without frontmatter.
37575
+ *
37576
+ * Global scope emits only `~/.config/goose/.goosehints`. Goose v1.41.0 (PR
37577
+ * block/goose#9736) additionally loads the vendor-neutral
37578
+ * `~/.agents/AGENTS.md` alongside the config-dir hints, but rulesync
37579
+ * deliberately does not emit that shared path from the goose target: the
37580
+ * config-dir hints remain fully loaded (no capability loss), and the
37581
+ * cross-tool `~/.agents/AGENTS.md` is already written by targets that own it
37582
+ * (e.g. cline in global mode) — a second writer would need cross-target
37583
+ * shared-file coordination for zero gain (decision recorded in issue #2207).
36812
37584
  */
36813
37585
  var GooseRule = class GooseRule extends ToolRule {
36814
37586
  static getSettablePaths({ global } = {}) {
@@ -37010,14 +37782,21 @@ var HermesagentRule = class HermesagentRule extends ToolRule {
37010
37782
  /**
37011
37783
  * Rule generator for JetBrains Junie AI coding agent
37012
37784
  *
37013
- * Generates `.junie/AGENTS.md` files based on rulesync rule content. `.junie/AGENTS.md`
37014
- * is the preferred guideline file in current Junie; the legacy `.junie/guidelines.md`
37015
- * is still read by Junie and is accepted as an import fallback, but generation always
37016
- * targets `.junie/AGENTS.md`. Junie uses plain markdown without frontmatter requirements.
37017
- *
37018
- * Global (user) scope writes a single `~/.junie/AGENTS.md` file. Junie merges these
37019
- * user-scope guidelines with the project `.junie/AGENTS.md` (project takes priority on
37020
- * conflicts); memory files (`.junie/memories/`) remain project-scoped only.
37785
+ * Generates `.junie/AGENTS.md` files based on rulesync rule content. Junie CLI
37786
+ * resolves project guidelines **first-match-wins**: `.junie/AGENTS.md` → root
37787
+ * `AGENTS.md` legacy `.junie/guidelines.md` / `.junie/guidelines/`. Only the
37788
+ * first match is loaded, it documents no `@`-reference or file-inclusion
37789
+ * mechanism, and no `.junie/memories/` read path exists — so non-root rules
37790
+ * are folded into the single root `.junie/AGENTS.md` by the RulesProcessor
37791
+ * (`nonRoot` is `undefined`, mirroring the grokcli / warp / deepagents
37792
+ * targets; decision recorded in issue #2211). The legacy
37793
+ * `.junie/guidelines.md` is still accepted as an import fallback, but
37794
+ * generation always targets `.junie/AGENTS.md`. Junie uses plain markdown
37795
+ * without frontmatter requirements.
37796
+ *
37797
+ * Global (user) scope writes a single `~/.junie/AGENTS.md` file. Junie merges
37798
+ * these user-scope guidelines with the project guidelines (both are included
37799
+ * and marked clearly).
37021
37800
  *
37022
37801
  * @see https://junie.jetbrains.com/docs/junie-ide-plugin.html
37023
37802
  * @see https://junie.jetbrains.com/docs/guidelines-and-memory.html
@@ -37036,15 +37815,12 @@ var JunieRule = class JunieRule extends ToolRule {
37036
37815
  alternativeRoots: [{
37037
37816
  relativeDirPath: buildToolPath(JUNIE_DIR, ".", excludeToolDir),
37038
37817
  relativeFilePath: JUNIE_LEGACY_RULE_FILE_NAME
37039
- }],
37040
- nonRoot: { relativeDirPath: buildToolPath(JUNIE_DIR, "memories", excludeToolDir) }
37818
+ }]
37041
37819
  };
37042
37820
  }
37043
37821
  /**
37044
37822
  * Determines whether a given relative file path refers to a root guideline file.
37045
37823
  * The preferred file is `AGENTS.md`; the legacy `guidelines.md` is still accepted.
37046
- * Memory files live under `.junie/memories/` and are passed in as bare filenames
37047
- * (e.g. `memo.md`), so a top-level `AGENTS.md`/`guidelines.md` is the root entry.
37048
37824
  */
37049
37825
  static isRootRelativeFilePath(relativeFilePath) {
37050
37826
  return relativeFilePath === "AGENTS.md" || relativeFilePath === "guidelines.md";
@@ -37063,10 +37839,7 @@ var JunieRule = class JunieRule extends ToolRule {
37063
37839
  root: true
37064
37840
  });
37065
37841
  }
37066
- const isRoot = JunieRule.isRootRelativeFilePath(relativeFilePath);
37067
- const settablePaths = this.getSettablePaths();
37068
- if (!settablePaths.nonRoot) throw new Error("JunieRule project settable paths must include a nonRoot path");
37069
- const relativeDirPath = isRoot ? settablePaths.root.relativeDirPath : settablePaths.nonRoot.relativeDirPath;
37842
+ const relativeDirPath = this.getSettablePaths().root.relativeDirPath;
37070
37843
  const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(relativeDirPath, relativeFilePath)));
37071
37844
  return new JunieRule({
37072
37845
  outputRoot,
@@ -37074,7 +37847,7 @@ var JunieRule = class JunieRule extends ToolRule {
37074
37847
  relativeFilePath,
37075
37848
  fileContent,
37076
37849
  validate,
37077
- root: isRoot
37850
+ root: JunieRule.isRootRelativeFilePath(relativeFilePath)
37078
37851
  });
37079
37852
  }
37080
37853
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
@@ -37089,13 +37862,15 @@ var JunieRule = class JunieRule extends ToolRule {
37089
37862
  rootPath: paths.root
37090
37863
  }));
37091
37864
  }
37092
- return new JunieRule(this.buildToolRuleParamsDefault({
37865
+ const { root } = this.getSettablePaths();
37866
+ return new JunieRule({
37093
37867
  outputRoot,
37094
- rulesyncRule,
37868
+ relativeDirPath: root.relativeDirPath,
37869
+ relativeFilePath: root.relativeFilePath,
37870
+ fileContent: rulesyncRule.getBody(),
37095
37871
  validate,
37096
- rootPath: this.getSettablePaths().root,
37097
- nonRootPath: this.getSettablePaths().nonRoot
37098
- }));
37872
+ root: rulesyncRule.getFrontmatter().root ?? false
37873
+ });
37099
37874
  }
37100
37875
  toRulesyncRule() {
37101
37876
  return this.toRulesyncRuleDefault();
@@ -37498,31 +38273,68 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
37498
38273
  * RulesProcessor (there is no separate non-root output location — `nonRoot` is
37499
38274
  * `undefined`). This mirrors the codexcli, grokcli, warp, and deepagents targets.
37500
38275
  *
37501
- * Pi also loads two system-prompt instruction files that rulesync does NOT emit:
38276
+ * Pi also loads two system-prompt instruction files. `.pi/APPEND_SYSTEM.md`
38277
+ * (global `~/.pi/agent/APPEND_SYSTEM.md`) *appends* to the default system prompt,
38278
+ * and rulesync emits it from any rule that opts in via the `pi.systemPrompt:
38279
+ * append` frontmatter block: those bodies are routed here instead of being folded
38280
+ * into `AGENTS.md`, and multiple opted-in rules concatenate in source order.
37502
38281
  * `.pi/SYSTEM.md` (global `~/.pi/agent/SYSTEM.md`) *replaces* the default system
37503
- * prompt entirely, and `.pi/APPEND_SYSTEM.md` (global
37504
- * `~/.pi/agent/APPEND_SYSTEM.md`) *appends* to it. rulesync's rules model only
37505
- * routes a designated `root` rule to a single context file and has no convention
37506
- * for marking a rule as "replace" vs "append" the system prompt, so these
37507
- * surfaces are documented in docs/reference/file-formats.md and left to be
37508
- * authored by hand rather than mapped to a speculative new frontmatter flag.
38282
+ * prompt entirely which silently disables Pi's built-in tool instructions — so
38283
+ * rulesync deliberately never emits it and leaves it to be authored by hand.
38284
+ * See docs/reference/file-formats.md.
37509
38285
  */
37510
38286
  var PiRule = class PiRule extends ToolRule {
37511
- constructor({ fileContent, root, ...rest }) {
38287
+ appendSystemPrompt;
38288
+ constructor({ fileContent, root, appendSystemPrompt = false, ...rest }) {
37512
38289
  super({
37513
38290
  ...rest,
37514
38291
  fileContent,
37515
38292
  root: root ?? false
37516
38293
  });
38294
+ this.appendSystemPrompt = appendSystemPrompt;
37517
38295
  }
37518
38296
  static getSettablePaths({ global = false, excludeToolDir } = {}) {
37519
- return { root: {
37520
- relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".",
37521
- relativeFilePath: PI_RULE_FILE_NAME
37522
- } };
38297
+ return {
38298
+ root: {
38299
+ relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".",
38300
+ relativeFilePath: PI_RULE_FILE_NAME
38301
+ },
38302
+ appendSystemPrompt: {
38303
+ relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".pi",
38304
+ relativeFilePath: PI_APPEND_SYSTEM_FILE_NAME
38305
+ }
38306
+ };
37523
38307
  }
37524
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
37525
- const { root } = this.getSettablePaths({ global });
38308
+ /**
38309
+ * Extra fixed files this tool manages beyond the root rule. The
38310
+ * RulesProcessor enumerates these for import and deletion so a stale
38311
+ * `APPEND_SYSTEM.md` is cleaned up once no rule opts in anymore.
38312
+ */
38313
+ static getExtraFixedFiles({ global = false } = {}) {
38314
+ return [this.getSettablePaths({ global }).appendSystemPrompt];
38315
+ }
38316
+ /**
38317
+ * Pi appends `APPEND_SYSTEM.md` to the system prompt itself, so listing it in
38318
+ * the root rule's reference section (toon/explicit discovery modes) would
38319
+ * double-load the content.
38320
+ */
38321
+ isExcludedFromRootReferences() {
38322
+ return this.appendSystemPrompt;
38323
+ }
38324
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
38325
+ const { root, appendSystemPrompt } = this.getSettablePaths({ global });
38326
+ if (relativeFilePath === "APPEND_SYSTEM.md") {
38327
+ const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(appendSystemPrompt.relativeDirPath, appendSystemPrompt.relativeFilePath)));
38328
+ return new PiRule({
38329
+ outputRoot,
38330
+ relativeDirPath: appendSystemPrompt.relativeDirPath,
38331
+ relativeFilePath: appendSystemPrompt.relativeFilePath,
38332
+ fileContent,
38333
+ validate,
38334
+ root: false,
38335
+ appendSystemPrompt: true
38336
+ });
38337
+ }
37526
38338
  const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(root.relativeDirPath, root.relativeFilePath)));
37527
38339
  return new PiRule({
37528
38340
  outputRoot,
@@ -37534,8 +38346,18 @@ var PiRule = class PiRule extends ToolRule {
37534
38346
  });
37535
38347
  }
37536
38348
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
37537
- const { root } = this.getSettablePaths({ global });
37538
- const isRoot = rulesyncRule.getFrontmatter().root ?? false;
38349
+ const { root, appendSystemPrompt } = this.getSettablePaths({ global });
38350
+ const frontmatter = rulesyncRule.getFrontmatter();
38351
+ if (!frontmatter.root && frontmatter.pi?.systemPrompt === "append") return new PiRule({
38352
+ outputRoot,
38353
+ relativeDirPath: appendSystemPrompt.relativeDirPath,
38354
+ relativeFilePath: appendSystemPrompt.relativeFilePath,
38355
+ fileContent: rulesyncRule.getBody(),
38356
+ validate,
38357
+ root: false,
38358
+ appendSystemPrompt: true
38359
+ });
38360
+ const isRoot = frontmatter.root ?? false;
37539
38361
  return new PiRule({
37540
38362
  outputRoot,
37541
38363
  relativeDirPath: root.relativeDirPath,
@@ -37546,6 +38368,17 @@ var PiRule = class PiRule extends ToolRule {
37546
38368
  });
37547
38369
  }
37548
38370
  toRulesyncRule() {
38371
+ if (this.appendSystemPrompt) return new RulesyncRule({
38372
+ outputRoot: process.cwd(),
38373
+ relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
38374
+ relativeFilePath: PI_APPEND_SYSTEM_FILE_NAME,
38375
+ frontmatter: {
38376
+ root: false,
38377
+ targets: ["pi"],
38378
+ pi: { systemPrompt: "append" }
38379
+ },
38380
+ body: this.getFileContent()
38381
+ });
37549
38382
  return this.toRulesyncRuleDefault();
37550
38383
  }
37551
38384
  validate() {
@@ -37556,6 +38389,15 @@ var PiRule = class PiRule extends ToolRule {
37556
38389
  }
37557
38390
  static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
37558
38391
  const { root } = this.getSettablePaths({ global });
38392
+ if (relativeFilePath === "APPEND_SYSTEM.md") return new PiRule({
38393
+ outputRoot,
38394
+ relativeDirPath,
38395
+ relativeFilePath,
38396
+ fileContent: "",
38397
+ validate: false,
38398
+ root: false,
38399
+ appendSystemPrompt: true
38400
+ });
37559
38401
  const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === root.relativeDirPath);
37560
38402
  return new PiRule({
37561
38403
  outputRoot,
@@ -38715,7 +39557,8 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
38715
39557
  meta: {
38716
39558
  extension: "md",
38717
39559
  supportsGlobal: true,
38718
- ruleDiscoveryMode: "toon"
39560
+ ruleDiscoveryMode: "auto",
39561
+ foldsNonRootIntoRoot: true
38719
39562
  }
38720
39563
  }],
38721
39564
  ["kilo", {
@@ -38981,7 +39824,7 @@ var RulesProcessor = class extends FeatureProcessor {
38981
39824
  */
38982
39825
  async buildMcpInstructionFiles({ toolRules, meta }) {
38983
39826
  if (!meta.mcpInstructionsRegistrar || this.global) return [];
38984
- const instructionPaths = toolRules.filter((rule) => !rule.isRoot()).map((rule) => toPosixPath((0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath())));
39827
+ const instructionPaths = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences()).map((rule) => toPosixPath((0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath())));
38985
39828
  if (instructionPaths.length === 0) return [];
38986
39829
  return [await meta.mcpInstructionsRegistrar.fromInstructions({
38987
39830
  outputRoot: this.outputRoot,
@@ -39013,7 +39856,7 @@ var RulesProcessor = class extends FeatureProcessor {
39013
39856
  const toolRelativeDirPath = skillClass.getSettablePaths({ global: this.global }).relativeDirPath;
39014
39857
  return this.skills.filter((skill) => skillClass.isTargetedByRulesyncSkill(skill)).map((skill) => {
39015
39858
  const frontmatter = skill.getFrontmatter();
39016
- const relativePath = (0, node_path.join)(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME);
39859
+ const relativePath = (0, node_path.join)(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME$1);
39017
39860
  return {
39018
39861
  name: frontmatter.name,
39019
39862
  description: frontmatter.description,
@@ -39037,11 +39880,25 @@ var RulesProcessor = class extends FeatureProcessor {
39037
39880
  */
39038
39881
  foldNonRootRulesIntoRootRule(toolRules) {
39039
39882
  if (toolRules.length <= 1) return;
39040
- const target = toolRules.find((rule) => rule.isRoot()) ?? toolRules[0];
39041
- if (!target) return;
39042
- const mergedContent = [target, ...toolRules.filter((rule) => rule !== target)].map((rule) => rule.getFileContent().trim()).filter((content) => content.length > 0).join("\n\n");
39043
- target.setFileContent(mergedContent);
39044
- for (let i = toolRules.length - 1; i >= 0; i--) if (toolRules[i] !== target) toolRules.splice(i, 1);
39883
+ const groups = /* @__PURE__ */ new Map();
39884
+ for (const rule of toolRules) {
39885
+ const path = (0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath());
39886
+ const group = groups.get(path);
39887
+ if (group) group.push(rule);
39888
+ else groups.set(path, [rule]);
39889
+ }
39890
+ const survivors = /* @__PURE__ */ new Set();
39891
+ for (const group of groups.values()) {
39892
+ const target = group.find((rule) => rule.isRoot()) ?? group[0];
39893
+ if (!target) continue;
39894
+ const mergedContent = [target, ...group.filter((rule) => rule !== target)].map((rule) => rule.getFileContent().trim()).filter((content) => content.length > 0).join("\n\n");
39895
+ target.setFileContent(mergedContent);
39896
+ survivors.add(target);
39897
+ }
39898
+ for (let i = toolRules.length - 1; i >= 0; i--) {
39899
+ const rule = toolRules[i];
39900
+ if (rule && !survivors.has(rule)) toolRules.splice(i, 1);
39901
+ }
39045
39902
  }
39046
39903
  /**
39047
39904
  * Handle localRoot rule generation based on tool target.
@@ -39251,6 +40108,27 @@ As this project's AI coding tool, you must follow the additional conventions bel
39251
40108
  const mirrorPaths = await findFilesByGlobs(mirrorGlob);
39252
40109
  return buildDeletionRulesFromPaths(mirrorPaths);
39253
40110
  })();
40111
+ const extraFixedToolRules = await (async () => {
40112
+ const extraFiles = factory.class.getExtraFixedFiles?.({ global: this.global });
40113
+ if (!extraFiles || extraFiles.length === 0) return [];
40114
+ const filePaths = await findFilesByGlobs(extraFiles.map((file) => (0, node_path.join)(this.outputRoot, file.relativeDirPath, file.relativeFilePath)));
40115
+ if (filePaths.length === 0) return [];
40116
+ if (forDeletion) return buildDeletionRulesFromPaths(filePaths);
40117
+ return await Promise.all(filePaths.map((filePath) => {
40118
+ const relativeDirPath = resolveRelativeDirPath(filePath);
40119
+ checkPathTraversal({
40120
+ relativePath: relativeDirPath,
40121
+ intendedRootDir: this.outputRoot
40122
+ });
40123
+ return factory.class.fromFile({
40124
+ outputRoot: this.outputRoot,
40125
+ relativeDirPath,
40126
+ relativeFilePath: (0, node_path.basename)(filePath),
40127
+ global: this.global
40128
+ });
40129
+ }));
40130
+ })();
40131
+ this.logger.debug(`Found ${extraFixedToolRules.length} extra fixed tool rule files`);
39254
40132
  const nonRootToolRules = await (async () => {
39255
40133
  if (!settablePaths.nonRoot) return [];
39256
40134
  const nonRootOutputRoot = (0, node_path.join)(this.outputRoot, settablePaths.nonRoot.relativeDirPath);
@@ -39286,6 +40164,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39286
40164
  ...rootToolRules,
39287
40165
  ...localRootToolRules,
39288
40166
  ...rootMirrorDeletionRules,
40167
+ ...extraFixedToolRules,
39289
40168
  ...nonRootToolRules
39290
40169
  ];
39291
40170
  } catch (error) {
@@ -39313,7 +40192,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39313
40192
  return toolRuleFactories.get(result.data);
39314
40193
  }
39315
40194
  generateToonReferencesSection(toolRules) {
39316
- const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot());
40195
+ const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences());
39317
40196
  if (toolRulesWithoutRoot.length === 0) return "";
39318
40197
  const lines = [];
39319
40198
  lines.push("Please also reference the following rules as needed. The list below is provided in TOON format, and `@` stands for the project root directory.");
@@ -39329,7 +40208,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39329
40208
  return lines.join("\n") + "\n\n";
39330
40209
  }
39331
40210
  generateReferencesSection(toolRules) {
39332
- const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot());
40211
+ const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences());
39333
40212
  if (toolRulesWithoutRoot.length === 0) return "";
39334
40213
  const lines = [];
39335
40214
  lines.push("Please also reference the following rules as needed:");
@@ -39350,7 +40229,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39350
40229
  */
39351
40230
  async function convertFromTool(params) {
39352
40231
  const ctx = params;
39353
- const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount] = [
40232
+ const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount, checksCount] = [
39354
40233
  await runFeatureConvert(ctx, buildRulesStrategy(ctx)),
39355
40234
  await runFeatureConvert(ctx, buildIgnoreStrategy(ctx)),
39356
40235
  await runFeatureConvert(ctx, buildMcpStrategy(ctx)),
@@ -39358,7 +40237,8 @@ async function convertFromTool(params) {
39358
40237
  await runFeatureConvert(ctx, buildSubagentsStrategy(ctx)),
39359
40238
  await runFeatureConvert(ctx, buildSkillsStrategy(ctx)),
39360
40239
  await runFeatureConvert(ctx, buildHooksStrategy(ctx)),
39361
- await runFeatureConvert(ctx, buildPermissionsStrategy(ctx))
40240
+ await runFeatureConvert(ctx, buildPermissionsStrategy(ctx)),
40241
+ await runFeatureConvert(ctx, buildChecksStrategy(ctx))
39362
40242
  ];
39363
40243
  return {
39364
40244
  rulesCount,
@@ -39368,7 +40248,8 @@ async function convertFromTool(params) {
39368
40248
  subagentsCount,
39369
40249
  skillsCount,
39370
40250
  hooksCount,
39371
- permissionsCount
40251
+ permissionsCount,
40252
+ checksCount
39372
40253
  };
39373
40254
  }
39374
40255
  async function runFeatureConvert(ctx, strategy) {
@@ -39601,6 +40482,27 @@ function buildPermissionsStrategy(ctx) {
39601
40482
  write: (p, files) => p.writeAiFiles(files)
39602
40483
  };
39603
40484
  }
40485
+ function buildChecksStrategy(ctx) {
40486
+ const { config, logger } = ctx;
40487
+ const global = config.getGlobal();
40488
+ const outputRoot = getOutputRoot(config);
40489
+ return {
40490
+ feature: "checks",
40491
+ itemLabel: "check file(s)",
40492
+ allTargets: ChecksProcessor.getToolTargets({ global }),
40493
+ createProcessor: ({ toolTarget, dryRun }) => new ChecksProcessor({
40494
+ outputRoot,
40495
+ toolTarget,
40496
+ global,
40497
+ dryRun,
40498
+ logger
40499
+ }),
40500
+ loadSource: (p) => p.loadToolFiles(),
40501
+ toRulesync: (p, files) => p.convertToolFilesToRulesyncFiles(files),
40502
+ fromRulesync: (p, files) => p.convertRulesyncFilesToToolFiles(files),
40503
+ write: (p, files) => p.writeAiFiles(files)
40504
+ };
40505
+ }
39604
40506
  //#endregion
39605
40507
  //#region src/types/processor-registry.ts
39606
40508
  const PROCESSOR_REGISTRY = [
@@ -39651,6 +40553,12 @@ const PROCESSOR_REGISTRY = [
39651
40553
  processor: PermissionsProcessor,
39652
40554
  schema: PermissionsProcessorToolTargetSchema,
39653
40555
  factory: toolPermissionsFactories
40556
+ },
40557
+ {
40558
+ feature: "checks",
40559
+ processor: ChecksProcessor,
40560
+ schema: ChecksProcessorToolTargetSchema,
40561
+ factory: toolCheckFactories
39654
40562
  }
39655
40563
  ];
39656
40564
  const getProcessorRegistryEntry = (feature) => {
@@ -40000,6 +40908,7 @@ const GENERATION_STEP_GRAPH = [
40000
40908
  id: "permissions",
40001
40909
  ...sharedWriteMeta("permissions")
40002
40910
  },
40911
+ { id: "checks" },
40003
40912
  {
40004
40913
  id: "rules",
40005
40914
  ...sharedWriteMeta("rules"),
@@ -40085,6 +40994,10 @@ async function generate(params) {
40085
40994
  config,
40086
40995
  logger
40087
40996
  }),
40997
+ checks: () => generateChecksCore({
40998
+ config,
40999
+ logger
41000
+ }),
40088
41001
  rules: () => generateRulesCore({
40089
41002
  config,
40090
41003
  logger,
@@ -40121,6 +41034,8 @@ async function generate(params) {
40121
41034
  hooksPaths: get("hooks").paths,
40122
41035
  permissionsCount: get("permissions").count,
40123
41036
  permissionsPaths: get("permissions").paths,
41037
+ checksCount: get("checks").count,
41038
+ checksPaths: get("checks").paths,
40124
41039
  skills: skillsResult.skills,
40125
41040
  hasDiff
40126
41041
  };
@@ -40489,6 +41404,44 @@ async function generatePermissionsCore(params) {
40489
41404
  hasDiff
40490
41405
  };
40491
41406
  }
41407
+ async function generateChecksCore(params) {
41408
+ const { config, logger } = params;
41409
+ let totalCount = 0;
41410
+ const allPaths = [];
41411
+ let hasDiff = false;
41412
+ const supportedChecksTargets = ChecksProcessor.getToolTargets({ global: config.getGlobal() });
41413
+ const toolTargets = (0, es_toolkit.intersection)(config.getTargets(), supportedChecksTargets);
41414
+ warnUnsupportedTargets({
41415
+ config,
41416
+ supportedTargets: supportedChecksTargets,
41417
+ featureName: "checks",
41418
+ logger
41419
+ });
41420
+ for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
41421
+ if (!config.getFeatures(toolTarget).includes("checks")) continue;
41422
+ const processor = new ChecksProcessor({
41423
+ outputRoot,
41424
+ inputRoot: config.getInputRoot(),
41425
+ toolTarget,
41426
+ global: config.getGlobal(),
41427
+ dryRun: config.isPreviewMode(),
41428
+ logger
41429
+ });
41430
+ const result = await processFeatureWithRulesyncFiles({
41431
+ config,
41432
+ processor,
41433
+ rulesyncFiles: await processor.loadRulesyncFiles()
41434
+ });
41435
+ totalCount += result.count;
41436
+ allPaths.push(...result.paths);
41437
+ if (result.hasDiff) hasDiff = true;
41438
+ }
41439
+ return {
41440
+ count: totalCount,
41441
+ paths: allPaths,
41442
+ hasDiff
41443
+ };
41444
+ }
40492
41445
  //#endregion
40493
41446
  //#region src/lib/import.ts
40494
41447
  /**
@@ -40546,6 +41499,11 @@ async function importFromTool(params) {
40546
41499
  config,
40547
41500
  tool,
40548
41501
  logger
41502
+ }),
41503
+ checksCount: await importChecksCore({
41504
+ config,
41505
+ tool,
41506
+ logger
40549
41507
  })
40550
41508
  };
40551
41509
  }
@@ -40762,6 +41720,27 @@ async function importPermissionsCore(params) {
40762
41720
  if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} permissions file(s)`);
40763
41721
  return writtenCount;
40764
41722
  }
41723
+ async function importChecksCore(params) {
41724
+ const { config, tool, logger } = params;
41725
+ if (!config.getFeatures(tool).includes("checks")) return 0;
41726
+ const global = config.getGlobal();
41727
+ if (!ChecksProcessor.getToolTargets({ global }).includes(tool)) return 0;
41728
+ const checksProcessor = new ChecksProcessor({
41729
+ outputRoot: config.getOutputRoots()[0] ?? ".",
41730
+ toolTarget: tool,
41731
+ global,
41732
+ logger
41733
+ });
41734
+ const toolFiles = await checksProcessor.loadToolFiles();
41735
+ if (toolFiles.length === 0) {
41736
+ logger.warn(`No check files found for ${tool}. Skipping import.`);
41737
+ return 0;
41738
+ }
41739
+ const rulesyncFiles = await checksProcessor.convertToolFilesToRulesyncFiles(toolFiles);
41740
+ const { count: writtenCount } = await checksProcessor.writeAiFiles(rulesyncFiles);
41741
+ if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} check files`);
41742
+ return writtenCount;
41743
+ }
40765
41744
  //#endregion
40766
41745
  Object.defineProperty(exports, "ALL_FEATURES", {
40767
41746
  enumerable: true,
@@ -40823,12 +41802,24 @@ Object.defineProperty(exports, "CLIError", {
40823
41802
  return CLIError;
40824
41803
  }
40825
41804
  });
41805
+ Object.defineProperty(exports, "CODEXCLI_BASH_RULES_FILE_NAME", {
41806
+ enumerable: true,
41807
+ get: function() {
41808
+ return CODEXCLI_BASH_RULES_FILE_NAME;
41809
+ }
41810
+ });
40826
41811
  Object.defineProperty(exports, "CODEXCLI_DIR", {
40827
41812
  enumerable: true,
40828
41813
  get: function() {
40829
41814
  return CODEXCLI_DIR;
40830
41815
  }
40831
41816
  });
41817
+ Object.defineProperty(exports, "ChecksProcessor", {
41818
+ enumerable: true,
41819
+ get: function() {
41820
+ return ChecksProcessor;
41821
+ }
41822
+ });
40832
41823
  Object.defineProperty(exports, "CommandsProcessor", {
40833
41824
  enumerable: true,
40834
41825
  get: function() {
@@ -40895,6 +41886,12 @@ Object.defineProperty(exports, "RULESYNC_AIIGNORE_RELATIVE_FILE_PATH", {
40895
41886
  return RULESYNC_AIIGNORE_RELATIVE_FILE_PATH;
40896
41887
  }
40897
41888
  });
41889
+ Object.defineProperty(exports, "RULESYNC_CHECKS_RELATIVE_DIR_PATH", {
41890
+ enumerable: true,
41891
+ get: function() {
41892
+ return RULESYNC_CHECKS_RELATIVE_DIR_PATH;
41893
+ }
41894
+ });
40898
41895
  Object.defineProperty(exports, "RULESYNC_COMMANDS_RELATIVE_DIR_PATH", {
40899
41896
  enumerable: true,
40900
41897
  get: function() {
@@ -41039,6 +42036,18 @@ Object.defineProperty(exports, "RulesProcessor", {
41039
42036
  return RulesProcessor;
41040
42037
  }
41041
42038
  });
42039
+ Object.defineProperty(exports, "RulesyncCheck", {
42040
+ enumerable: true,
42041
+ get: function() {
42042
+ return RulesyncCheck;
42043
+ }
42044
+ });
42045
+ Object.defineProperty(exports, "RulesyncCheckFrontmatterSchema", {
42046
+ enumerable: true,
42047
+ get: function() {
42048
+ return RulesyncCheckFrontmatterSchema;
42049
+ }
42050
+ });
41042
42051
  Object.defineProperty(exports, "RulesyncCommand", {
41043
42052
  enumerable: true,
41044
42053
  get: function() {
@@ -41114,7 +42123,7 @@ Object.defineProperty(exports, "RulesyncSubagentFrontmatterSchema", {
41114
42123
  Object.defineProperty(exports, "SKILL_FILE_NAME", {
41115
42124
  enumerable: true,
41116
42125
  get: function() {
41117
- return SKILL_FILE_NAME;
42126
+ return SKILL_FILE_NAME$1;
41118
42127
  }
41119
42128
  });
41120
42129
  Object.defineProperty(exports, "SkillsProcessor", {