rulesync 12.0.0 → 14.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,26 +116,28 @@ 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$257 } = 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$257(RULESYNC_RELATIVE_DIR_PATH, "rules");
124
+ const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "commands");
125
+ const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "subagents");
126
+ const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "checks");
127
+ const RULESYNC_MCP_RELATIVE_FILE_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
128
+ const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
129
+ const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
130
+ const RULESYNC_MCP_JSONC_RELATIVE_FILE_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
131
+ const RULESYNC_HOOKS_JSONC_RELATIVE_FILE_PATH = join$257(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
132
+ const RULESYNC_PERMISSIONS_JSONC_RELATIVE_FILE_PATH = join$257(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$257(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$257(RULESYNC_RELATIVE_DIR_PATH, "skills");
138
+ const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$257(RULESYNC_SKILLS_RELATIVE_DIR_PATH, ".curated");
136
139
  const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
140
+ const RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync-npm.lock.json";
137
141
  const RULESYNC_MCP_FILE_NAME = "mcp.json";
138
142
  const RULESYNC_HOOKS_FILE_NAME = "hooks.json";
139
143
  const RULESYNC_PERMISSIONS_FILE_NAME = "permissions.json";
@@ -366,6 +370,7 @@ const permissionsProcessorToolTargetTuple = [
366
370
  "claudecode",
367
371
  "cline",
368
372
  "codexcli",
373
+ "copilot",
369
374
  "cursor",
370
375
  "devin",
371
376
  "factorydroid",
@@ -386,6 +391,7 @@ const permissionsProcessorToolTargetTuple = [
386
391
  "warp",
387
392
  "zed"
388
393
  ];
394
+ const checksProcessorToolTargetTuple = ["amp"];
389
395
  //#endregion
390
396
  //#region src/types/tool-targets.ts
391
397
  const ALL_TOOL_TARGETS = [...new Set([
@@ -396,7 +402,8 @@ const ALL_TOOL_TARGETS = [...new Set([
396
402
  subagentsProcessorToolTargetTuple,
397
403
  skillsProcessorToolTargetTuple,
398
404
  hooksProcessorToolTargetTuple,
399
- permissionsProcessorToolTargetTuple
405
+ permissionsProcessorToolTargetTuple,
406
+ checksProcessorToolTargetTuple
400
407
  ].flat())];
401
408
  const ALL_TOOL_TARGETS_WITH_WILDCARD = [...ALL_TOOL_TARGETS, "*"];
402
409
  const ToolTargetSchema = zod_mini.z.enum(ALL_TOOL_TARGETS);
@@ -875,9 +882,15 @@ const GITIGNORE_DESTINATION_KEY = "gitignoreDestination";
875
882
  const SourceEntrySchema = zod_mini.z.object({
876
883
  source: zod_mini.z.string().check((0, zod_mini.minLength)(1, "source must be a non-empty string")),
877
884
  skills: (0, zod_mini.optional)(zod_mini.z.array(zod_mini.z.string())),
878
- transport: (0, zod_mini.optional)(zod_mini.z.enum(["github", "git"])),
885
+ transport: (0, zod_mini.optional)(zod_mini.z.enum([
886
+ "github",
887
+ "git",
888
+ "npm"
889
+ ])),
879
890
  ref: (0, zod_mini.optional)(zod_mini.z.string().check((0, zod_mini.refine)((v) => !v.startsWith("-"), "ref must not start with \"-\""), (0, zod_mini.refine)((v) => !hasControlCharacters(v), "ref must not contain control characters"))),
880
891
  path: (0, zod_mini.optional)(zod_mini.z.string().check((0, zod_mini.refine)((v) => !v.includes(".."), "path must not contain \"..\""), (0, zod_mini.refine)((v) => !(0, node_path.isAbsolute)(v), "path must not be absolute"), (0, zod_mini.refine)((v) => !hasControlCharacters(v), "path must not contain control characters"))),
892
+ registry: (0, zod_mini.optional)(zod_mini.z.string().check((0, zod_mini.refine)((v) => v.startsWith("https://") || v.startsWith("http://"), "registry must be an http(s) URL"), (0, zod_mini.refine)((v) => !hasControlCharacters(v), "registry must not contain control characters"))),
893
+ tokenEnv: (0, zod_mini.optional)(zod_mini.z.string().check((0, zod_mini.refine)((v) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(v), "tokenEnv must be a valid environment variable name"))),
881
894
  agent: (0, zod_mini.optional)(zod_mini.z.enum([
882
895
  "github-copilot",
883
896
  "claude-code",
@@ -887,7 +900,7 @@ const SourceEntrySchema = zod_mini.z.object({
887
900
  "antigravity"
888
901
  ])),
889
902
  scope: (0, zod_mini.optional)(zod_mini.z.enum(["project", "user"]))
890
- });
903
+ }).check((0, zod_mini.refine)((entry) => entry.registry === void 0 && entry.tokenEnv === void 0 || entry.transport === "npm", "\"registry\" and \"tokenEnv\" are only valid with transport \"npm\""));
891
904
  const ConfigParamsSchema = zod_mini.z.object({
892
905
  outputRoots: zod_mini.z.union([zod_mini.z.array(zod_mini.z.string()), zod_mini.z.record(zod_mini.z.string(), zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))]),
893
906
  targets: RulesyncConfigTargetsSchema,
@@ -1470,7 +1483,7 @@ function isPlainObject$1(value) {
1470
1483
  /**
1471
1484
  * Type guard to check if a value is an array of strings.
1472
1485
  */
1473
- function isStringArray(value) {
1486
+ function isStringArray$1(value) {
1474
1487
  return Array.isArray(value) && value.every((item) => typeof item === "string");
1475
1488
  }
1476
1489
  //#endregion
@@ -1718,13 +1731,17 @@ var FeatureProcessor = class {
1718
1731
  }
1719
1732
  };
1720
1733
  //#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";
1734
+ //#region src/constants/amp-paths.ts
1735
+ const AMP_DIR = ".amp";
1736
+ const AMP_GLOBAL_DIR = (0, node_path.join)(".config", "amp");
1737
+ const AMP_AGENTS_DIR = ".agents";
1738
+ const AMP_SKILLS_PROJECT_DIR = (0, node_path.join)(AMP_AGENTS_DIR, "skills");
1739
+ const AMP_SKILLS_GLOBAL_DIR = (0, node_path.join)(".config", "agents", "skills");
1740
+ const AMP_CHECKS_PROJECT_DIR = (0, node_path.join)(AMP_AGENTS_DIR, "checks");
1741
+ const AMP_CHECKS_GLOBAL_DIR = (0, node_path.join)(AMP_GLOBAL_DIR, "checks");
1742
+ const AMP_RULE_FILE_NAME = "AGENTS.md";
1743
+ const AMP_SETTINGS_FILE_NAME = "settings.json";
1744
+ const AMP_SETTINGS_JSONC_FILE_NAME = "settings.jsonc";
1728
1745
  //#endregion
1729
1746
  //#region src/types/ai-file.ts
1730
1747
  var AiFile = class {
@@ -1798,6 +1815,414 @@ var AiFile = class {
1798
1815
  }
1799
1816
  };
1800
1817
  //#endregion
1818
+ //#region src/types/rulesync-file.ts
1819
+ var RulesyncFile = class extends AiFile {
1820
+ static async fromFile(_params) {
1821
+ throw new Error("Please implement this method in the subclass.");
1822
+ }
1823
+ };
1824
+ //#endregion
1825
+ //#region src/features/checks/rulesync-check.ts
1826
+ const RulesyncCheckFrontmatterSchema = zod_mini.z.looseObject({
1827
+ targets: zod_mini.z._default(RulesyncTargetsSchema, ["*"]),
1828
+ description: zod_mini.z.optional(zod_mini.z.string()),
1829
+ severity: zod_mini.z.optional(zod_mini.z.enum([
1830
+ "low",
1831
+ "medium",
1832
+ "high",
1833
+ "critical"
1834
+ ])),
1835
+ tools: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string()))
1836
+ });
1837
+ var RulesyncCheck = class RulesyncCheck extends RulesyncFile {
1838
+ frontmatter;
1839
+ body;
1840
+ constructor({ frontmatter, body, ...rest }) {
1841
+ const parseResult = RulesyncCheckFrontmatterSchema.safeParse(frontmatter);
1842
+ if (!parseResult.success && rest.validate !== false) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`);
1843
+ const parsedFrontmatter = parseResult.success ? {
1844
+ ...frontmatter,
1845
+ ...parseResult.data
1846
+ } : {
1847
+ ...frontmatter,
1848
+ targets: frontmatter?.targets ?? ["*"]
1849
+ };
1850
+ super({
1851
+ ...rest,
1852
+ fileContent: stringifyFrontmatter(body, parsedFrontmatter)
1853
+ });
1854
+ this.frontmatter = parsedFrontmatter;
1855
+ this.body = body;
1856
+ }
1857
+ static getSettablePaths() {
1858
+ return { relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH };
1859
+ }
1860
+ getFrontmatter() {
1861
+ return this.frontmatter;
1862
+ }
1863
+ getBody() {
1864
+ return this.body;
1865
+ }
1866
+ validate() {
1867
+ if (!this.frontmatter) return {
1868
+ success: true,
1869
+ error: null
1870
+ };
1871
+ const result = RulesyncCheckFrontmatterSchema.safeParse(this.frontmatter);
1872
+ if (result.success) return {
1873
+ success: true,
1874
+ error: null
1875
+ };
1876
+ else return {
1877
+ success: false,
1878
+ error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
1879
+ };
1880
+ }
1881
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath }) {
1882
+ const filePath = (0, node_path.join)(outputRoot, RULESYNC_CHECKS_RELATIVE_DIR_PATH, relativeFilePath);
1883
+ const { frontmatter, body: content, hasFrontmatter } = parseFrontmatter(await readFileContent(filePath), filePath);
1884
+ if (!hasFrontmatter) throw new Error(`Missing frontmatter in ${filePath}. Rulesync files must begin with a YAML frontmatter block delimited by '---'.`);
1885
+ const result = RulesyncCheckFrontmatterSchema.safeParse(frontmatter);
1886
+ if (!result.success) throw new Error(`Invalid frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
1887
+ const filename = (0, node_path.basename)(relativeFilePath);
1888
+ return new RulesyncCheck({
1889
+ outputRoot,
1890
+ relativeDirPath: this.getSettablePaths().relativeDirPath,
1891
+ relativeFilePath: filename,
1892
+ frontmatter: result.data,
1893
+ body: content.trim()
1894
+ });
1895
+ }
1896
+ };
1897
+ //#endregion
1898
+ //#region src/types/tool-file.ts
1899
+ var ToolFile = class extends AiFile {};
1900
+ //#endregion
1901
+ //#region src/features/checks/tool-check.ts
1902
+ var ToolCheck = class extends ToolFile {
1903
+ static getSettablePaths(_options = {}) {
1904
+ throw new Error("Please implement this method in the subclass.");
1905
+ }
1906
+ static async fromFile(_params) {
1907
+ throw new Error("Please implement this method in the subclass.");
1908
+ }
1909
+ /**
1910
+ * Create a minimal instance for deletion purposes.
1911
+ * This method does not read or parse file content, making it safe to use
1912
+ * even when files have old/incompatible formats.
1913
+ */
1914
+ static forDeletion(_params) {
1915
+ throw new Error("Please implement this method in the subclass.");
1916
+ }
1917
+ static fromRulesyncCheck(_params) {
1918
+ throw new Error("Please implement this method in the subclass.");
1919
+ }
1920
+ static isTargetedByRulesyncCheck(_rulesyncCheck) {
1921
+ throw new Error("Please implement this method in the subclass.");
1922
+ }
1923
+ static isTargetedByRulesyncCheckDefault({ rulesyncCheck, toolTarget }) {
1924
+ const targets = rulesyncCheck.getFrontmatter().targets;
1925
+ if (!targets) return true;
1926
+ if (targets.includes("*")) return true;
1927
+ if (targets.includes(toolTarget)) return true;
1928
+ return false;
1929
+ }
1930
+ static filterToolSpecificSection(rawSection, excludeFields) {
1931
+ const filtered = {};
1932
+ for (const [key, value] of Object.entries(rawSection)) if (!excludeFields.includes(key)) filtered[key] = value;
1933
+ return filtered;
1934
+ }
1935
+ };
1936
+ //#endregion
1937
+ //#region src/features/checks/amp-check.ts
1938
+ const AmpCheckFrontmatterSchema = zod_mini.z.looseObject({
1939
+ name: zod_mini.z.string(),
1940
+ description: zod_mini.z.optional(zod_mini.z.string()),
1941
+ "severity-default": zod_mini.z.optional(zod_mini.z.enum([
1942
+ "low",
1943
+ "medium",
1944
+ "high",
1945
+ "critical"
1946
+ ])),
1947
+ tools: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string()))
1948
+ });
1949
+ /**
1950
+ * Represents an Amp code review check.
1951
+ *
1952
+ * Amp natively reads code review checks as Markdown files with YAML frontmatter,
1953
+ * scoped to the project (`.agents/checks/`) and user-wide (`~/.config/amp/checks/`).
1954
+ * Each check runs as a per-check subagent during code review.
1955
+ * @see https://ampcode.com/manual
1956
+ */
1957
+ var AmpCheck = class AmpCheck extends ToolCheck {
1958
+ frontmatter;
1959
+ body;
1960
+ constructor({ frontmatter, body, fileContent, ...rest }) {
1961
+ if (rest.validate !== false) {
1962
+ const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
1963
+ if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
1964
+ }
1965
+ super({
1966
+ ...rest,
1967
+ fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
1968
+ });
1969
+ this.frontmatter = frontmatter;
1970
+ this.body = body;
1971
+ }
1972
+ static getSettablePaths({ global = false } = {}) {
1973
+ return { relativeDirPath: global ? AMP_CHECKS_GLOBAL_DIR : AMP_CHECKS_PROJECT_DIR };
1974
+ }
1975
+ getFrontmatter() {
1976
+ return this.frontmatter;
1977
+ }
1978
+ getBody() {
1979
+ return this.body;
1980
+ }
1981
+ toRulesyncCheck() {
1982
+ const { name: _name, description, "severity-default": severityDefault, tools, ...restFields } = this.frontmatter;
1983
+ return new RulesyncCheck({
1984
+ outputRoot: ".",
1985
+ frontmatter: {
1986
+ targets: ["*"],
1987
+ ...description !== void 0 && { description },
1988
+ ...severityDefault !== void 0 && { severity: severityDefault },
1989
+ ...tools !== void 0 && { tools },
1990
+ ...restFields
1991
+ },
1992
+ body: this.body,
1993
+ relativeDirPath: RULESYNC_CHECKS_RELATIVE_DIR_PATH,
1994
+ relativeFilePath: this.getRelativeFilePath(),
1995
+ validate: true
1996
+ });
1997
+ }
1998
+ static fromRulesyncCheck({ outputRoot = process.cwd(), rulesyncCheck, validate = true, global = false }) {
1999
+ const rulesyncFrontmatter = rulesyncCheck.getFrontmatter();
2000
+ const relativeFilePath = rulesyncCheck.getRelativeFilePath();
2001
+ const name = (0, node_path.basename)(relativeFilePath, ".md");
2002
+ const { targets: _targets, description, severity, tools, ...restFields } = rulesyncFrontmatter;
2003
+ const toolTargetKeys = new Set(ALL_TOOL_TARGETS);
2004
+ const passthroughFields = Object.fromEntries(Object.entries(restFields).filter(([key]) => !toolTargetKeys.has(key) && key !== "name"));
2005
+ const ampSection = this.filterToolSpecificSection(rulesyncFrontmatter.amp ?? {}, ["name"]);
2006
+ const rawAmpFrontmatter = {
2007
+ name,
2008
+ ...description !== void 0 && { description },
2009
+ ...severity !== void 0 && { "severity-default": severity },
2010
+ ...tools !== void 0 && { tools },
2011
+ ...passthroughFields,
2012
+ ...ampSection
2013
+ };
2014
+ const result = AmpCheckFrontmatterSchema.safeParse(rawAmpFrontmatter);
2015
+ if (!result.success) throw new Error(`Invalid amp check frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
2016
+ const ampFrontmatter = result.data;
2017
+ const body = rulesyncCheck.getBody();
2018
+ const fileContent = stringifyFrontmatter(body, ampFrontmatter);
2019
+ const paths = this.getSettablePaths({ global });
2020
+ return new AmpCheck({
2021
+ outputRoot,
2022
+ frontmatter: ampFrontmatter,
2023
+ body,
2024
+ relativeDirPath: paths.relativeDirPath,
2025
+ relativeFilePath,
2026
+ fileContent,
2027
+ validate
2028
+ });
2029
+ }
2030
+ validate() {
2031
+ if (!this.frontmatter) return {
2032
+ success: true,
2033
+ error: null
2034
+ };
2035
+ const result = AmpCheckFrontmatterSchema.safeParse(this.frontmatter);
2036
+ if (result.success) return {
2037
+ success: true,
2038
+ error: null
2039
+ };
2040
+ else return {
2041
+ success: false,
2042
+ error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
2043
+ };
2044
+ }
2045
+ static isTargetedByRulesyncCheck(rulesyncCheck) {
2046
+ return this.isTargetedByRulesyncCheckDefault({
2047
+ rulesyncCheck,
2048
+ toolTarget: "amp"
2049
+ });
2050
+ }
2051
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
2052
+ const paths = this.getSettablePaths({ global });
2053
+ const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
2054
+ const fileContent = await readFileContent(filePath);
2055
+ const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
2056
+ const result = AmpCheckFrontmatterSchema.safeParse(frontmatter);
2057
+ if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
2058
+ return new AmpCheck({
2059
+ outputRoot,
2060
+ relativeDirPath: paths.relativeDirPath,
2061
+ relativeFilePath,
2062
+ frontmatter: result.data,
2063
+ body: content.trim(),
2064
+ fileContent,
2065
+ validate
2066
+ });
2067
+ }
2068
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
2069
+ return new AmpCheck({
2070
+ outputRoot,
2071
+ relativeDirPath,
2072
+ relativeFilePath,
2073
+ frontmatter: { name: "" },
2074
+ body: "",
2075
+ fileContent: "",
2076
+ validate: false
2077
+ });
2078
+ }
2079
+ };
2080
+ //#endregion
2081
+ //#region src/features/checks/checks-processor.ts
2082
+ const ChecksProcessorToolTargetSchema = zod_mini.z.enum(checksProcessorToolTargetTuple);
2083
+ /**
2084
+ * Factory Map mapping tool targets to their check factories.
2085
+ * Using Map to preserve insertion order for consistent iteration.
2086
+ */
2087
+ const toolCheckFactories = /* @__PURE__ */ new Map([["amp", {
2088
+ class: AmpCheck,
2089
+ meta: {
2090
+ supportsGlobal: true,
2091
+ filePattern: "*.md"
2092
+ }
2093
+ }]]);
2094
+ const defaultGetFactory$6 = (target) => {
2095
+ const factory = toolCheckFactories.get(target);
2096
+ if (!factory) throw new Error(`Unsupported tool target: ${target}`);
2097
+ return factory;
2098
+ };
2099
+ const allToolTargetKeys$5 = [...toolCheckFactories.keys()];
2100
+ const checksProcessorToolTargets = allToolTargetKeys$5;
2101
+ const checksProcessorToolTargetsGlobal = allToolTargetKeys$5.filter((target) => {
2102
+ return toolCheckFactories.get(target)?.meta.supportsGlobal ?? false;
2103
+ });
2104
+ var ChecksProcessor = class extends FeatureProcessor {
2105
+ toolTarget;
2106
+ global;
2107
+ getFactory;
2108
+ constructor({ outputRoot = process.cwd(), inputRoot = process.cwd(), toolTarget, global = false, getFactory = defaultGetFactory$6, dryRun = false, logger }) {
2109
+ super({
2110
+ outputRoot,
2111
+ inputRoot,
2112
+ dryRun,
2113
+ logger
2114
+ });
2115
+ const result = ChecksProcessorToolTargetSchema.safeParse(toolTarget);
2116
+ if (!result.success) throw new Error(`Invalid tool target for ChecksProcessor: ${toolTarget}. ${formatError(result.error)}`);
2117
+ this.toolTarget = result.data;
2118
+ this.global = global;
2119
+ this.getFactory = getFactory;
2120
+ }
2121
+ async convertRulesyncFilesToToolFiles(rulesyncFiles) {
2122
+ const rulesyncChecks = rulesyncFiles.filter((file) => file instanceof RulesyncCheck);
2123
+ const factory = this.getFactory(this.toolTarget);
2124
+ return rulesyncChecks.filter((rulesyncCheck) => factory.class.isTargetedByRulesyncCheck(rulesyncCheck)).map((rulesyncCheck) => factory.class.fromRulesyncCheck({
2125
+ outputRoot: this.outputRoot,
2126
+ relativeDirPath: RulesyncCheck.getSettablePaths().relativeDirPath,
2127
+ rulesyncCheck,
2128
+ global: this.global
2129
+ }));
2130
+ }
2131
+ async convertToolFilesToRulesyncFiles(toolFiles) {
2132
+ return toolFiles.filter((file) => file instanceof ToolCheck).map((toolCheck) => toolCheck.toRulesyncCheck());
2133
+ }
2134
+ /**
2135
+ * Implementation of abstract method from Processor
2136
+ * Load and parse rulesync check files from .rulesync/checks/ directory
2137
+ */
2138
+ async loadRulesyncFiles() {
2139
+ const checksDir = (0, node_path.join)(this.inputRoot, RulesyncCheck.getSettablePaths().relativeDirPath);
2140
+ if (!await directoryExists(checksDir)) {
2141
+ this.logger.debug(`Rulesync checks directory not found: ${checksDir}`);
2142
+ return [];
2143
+ }
2144
+ const mdFiles = (await listDirectoryFiles(checksDir)).filter((file) => file.endsWith(".md"));
2145
+ if (mdFiles.length === 0) {
2146
+ this.logger.debug(`No markdown files found in rulesync checks directory: ${checksDir}`);
2147
+ return [];
2148
+ }
2149
+ this.logger.debug(`Found ${mdFiles.length} check files in ${checksDir}`);
2150
+ const rulesyncChecks = [];
2151
+ for (const mdFile of mdFiles) {
2152
+ const filepath = (0, node_path.join)(checksDir, mdFile);
2153
+ try {
2154
+ const rulesyncCheck = await RulesyncCheck.fromFile({
2155
+ outputRoot: this.inputRoot,
2156
+ relativeFilePath: mdFile,
2157
+ validate: true
2158
+ });
2159
+ rulesyncChecks.push(rulesyncCheck);
2160
+ this.logger.debug(`Successfully loaded check: ${mdFile}`);
2161
+ } catch (error) {
2162
+ this.logger.warn(`Failed to load check file ${filepath}: ${formatError(error)}`);
2163
+ continue;
2164
+ }
2165
+ }
2166
+ this.logger.debug(`Successfully loaded ${rulesyncChecks.length} rulesync checks`);
2167
+ return rulesyncChecks;
2168
+ }
2169
+ /**
2170
+ * Implementation of abstract method from Processor
2171
+ * Load tool-specific check files and parse them into ToolCheck instances
2172
+ */
2173
+ async loadToolFiles({ forDeletion = false } = {}) {
2174
+ const factory = this.getFactory(this.toolTarget);
2175
+ const paths = factory.class.getSettablePaths({ global: this.global });
2176
+ const baseDir = (0, node_path.join)(this.outputRoot, paths.relativeDirPath);
2177
+ const checkFilePaths = await findFilesByGlobs((0, node_path.join)(baseDir, factory.meta.filePattern));
2178
+ const toRelativeFilePath = (path) => (0, node_path.relative)(baseDir, path);
2179
+ if (forDeletion) return checkFilePaths.map((path) => factory.class.forDeletion({
2180
+ outputRoot: this.outputRoot,
2181
+ relativeDirPath: paths.relativeDirPath,
2182
+ relativeFilePath: toRelativeFilePath(path),
2183
+ global: this.global
2184
+ })).filter((check) => check.isDeletable());
2185
+ const loaded = await Promise.all(checkFilePaths.map((path) => factory.class.fromFile({
2186
+ outputRoot: this.outputRoot,
2187
+ relativeDirPath: paths.relativeDirPath,
2188
+ relativeFilePath: toRelativeFilePath(path),
2189
+ global: this.global
2190
+ })));
2191
+ this.logger.debug(`Successfully loaded ${loaded.length} ${this.toolTarget} checks from ${paths.relativeDirPath}`);
2192
+ return loaded;
2193
+ }
2194
+ /**
2195
+ * Implementation of abstract method from FeatureProcessor
2196
+ * Return the tool targets that this processor supports
2197
+ */
2198
+ static getToolTargets({ global = false } = {}) {
2199
+ if (global) return [...checksProcessorToolTargetsGlobal];
2200
+ return [...checksProcessorToolTargets];
2201
+ }
2202
+ static getToolTargetsSimulated() {
2203
+ return [];
2204
+ }
2205
+ /**
2206
+ * Get the factory for a specific tool target.
2207
+ * This is a static version of the internal getFactory for external use.
2208
+ * @param target - The tool target. Must be a valid ChecksProcessorToolTarget.
2209
+ * @returns The factory for the target, or undefined if not found.
2210
+ */
2211
+ static getFactory(target) {
2212
+ const result = ChecksProcessorToolTargetSchema.safeParse(target);
2213
+ if (!result.success) return;
2214
+ return toolCheckFactories.get(result.data);
2215
+ }
2216
+ };
2217
+ //#endregion
2218
+ //#region src/constants/agentsmd-paths.ts
2219
+ const AGENTSMD_DIR = ".agents";
2220
+ const AGENTSMD_MEMORIES_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "memories");
2221
+ const AGENTSMD_COMMANDS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "commands");
2222
+ const AGENTSMD_SKILLS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "skills");
2223
+ const AGENTSMD_SUBAGENTS_DIR_PATH = (0, node_path.join)(AGENTSMD_DIR, "subagents");
2224
+ const AGENTSMD_RULE_FILE_NAME = "AGENTS.md";
2225
+ //#endregion
1801
2226
  //#region src/features/commands/tool-command.ts
1802
2227
  /**
1803
2228
  * Abstract base class for AI development tool-specific command formats.
@@ -2045,13 +2470,6 @@ const AntigravityCommandFrontmatterSchema = zod_mini.z.looseObject({
2045
2470
  ...AntigravityWorkflowFrontmatterSchema.shape
2046
2471
  });
2047
2472
  //#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
2473
  //#region src/features/commands/rulesync-command.ts
2056
2474
  const RulesyncCommandFrontmatterSchema = zod_mini.z.looseObject({
2057
2475
  targets: zod_mini.z._default(RulesyncTargetsSchema, ["*"]),
@@ -2854,6 +3272,7 @@ const COPILOT_HOOKS_DIR_PATH = (0, node_path.join)(GITHUB_DIR, "hooks");
2854
3272
  const COPILOT_HOOKS_FILE_NAME = "copilot-hooks.json";
2855
3273
  const COPILOT_MCP_DIR = ".vscode";
2856
3274
  const COPILOT_MCP_FILE_NAME = "mcp.json";
3275
+ const COPILOT_VSCODE_SETTINGS_FILE_NAME = "settings.json";
2857
3276
  const COPILOTCLI_MCP_FILE_NAME = "mcp-config.json";
2858
3277
  const COPILOTCLI_PROJECT_MCP_FILE_NAME = "mcp.json";
2859
3278
  const COPILOTCLI_AGENTS_DIR_PATH = (0, node_path.join)(COPILOT_DIR, "agents");
@@ -3112,135 +3531,118 @@ var CursorCommand = class CursorCommand extends ToolCommand {
3112
3531
  //#endregion
3113
3532
  //#region src/constants/devin-paths.ts
3114
3533
  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
3534
  const DEVIN_SKILLS_DIR_PATH = (0, node_path.join)(DEVIN_DIR, "skills");
3118
3535
  const DEVIN_AGENTS_DIR_PATH = (0, node_path.join)(DEVIN_DIR, "agents");
3119
3536
  const DEVIN_GLOBAL_CONFIG_DIR_PATH = (0, node_path.join)(".config", "devin");
3120
3537
  const DEVIN_GLOBAL_AGENTS_DIR_PATH = (0, node_path.join)(DEVIN_GLOBAL_CONFIG_DIR_PATH, "agents");
3121
3538
  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
3539
  const DEVIN_CONFIG_FILE_NAME = "config.json";
3124
3540
  const DEVIN_HOOKS_V1_FILE_NAME = "hooks.v1.json";
3125
3541
  const DEVIN_GLOBAL_AGENTS_FILE_NAME = "AGENTS.md";
3126
3542
  const DEVIN_IGNORE_FILE_NAME = ".devinignore";
3127
3543
  const DEVIN_LEGACY_IGNORE_FILE_NAME = ".codeiumignore";
3128
3544
  //#endregion
3545
+ //#region src/constants/general.ts
3546
+ const SKILL_FILE_NAME$1 = "SKILL.md";
3547
+ //#endregion
3548
+ //#region src/features/commands/command-skill-ownership.ts
3549
+ /**
3550
+ * Slug used for the per-command `<slug>/SKILL.md` directory when a tool's
3551
+ * commands are emitted onto its skills surface (Devin, Hermes Agent).
3552
+ */
3553
+ function commandSlug(relativeFilePath) {
3554
+ return (0, node_path.basename)(relativeFilePath, ".md").replace(/[^a-zA-Z0-9_-]/g, "-");
3555
+ }
3556
+ /**
3557
+ * Whether a rulesync command exists whose slug matches `dirName`.
3558
+ *
3559
+ * Used by the skills-surface `isDirOwned` hooks of tools whose commands are
3560
+ * emitted as `<slug>/SKILL.md` into the skills tree: a directory matching a
3561
+ * current command slug is owned by the commands feature, so the skills
3562
+ * feature must neither import it as a skill nor delete it as an orphan
3563
+ * skill. Once the command is removed from `.rulesync/commands/`, the
3564
+ * directory stops matching and the skills feature cleans it up as a regular
3565
+ * orphan.
3566
+ */
3567
+ async function rulesyncCommandSlugExists({ inputRoot, dirName }) {
3568
+ return (await findFilesByGlobs((0, node_path.join)(inputRoot, RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "**", "*.md"))).some((filePath) => commandSlug((0, node_path.basename)(filePath)) === dirName);
3569
+ }
3570
+ //#endregion
3129
3571
  //#region src/features/commands/devin-command.ts
3130
- const DevinCommandFrontmatterSchema = zod_mini.z.looseObject({ description: zod_mini.z.optional(zod_mini.z.string()) });
3572
+ function commandSkillContent$1(rulesyncCommand) {
3573
+ const slug = commandSlug(rulesyncCommand.getRelativeFilePath());
3574
+ const description = rulesyncCommand.getFrontmatter().description ?? `${slug} command`;
3575
+ return stringifyFrontmatter(rulesyncCommand.getBody().trim(), {
3576
+ name: slug,
3577
+ description
3578
+ });
3579
+ }
3131
3580
  /**
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).
3581
+ * Represents a Devin slash command, emitted as a Devin Skill.
3582
+ *
3583
+ * Devin's extensibility docs no longer document a standalone
3584
+ * workflows/commands component reusable prompts invoked as slash commands
3585
+ * are Skills (`/name`). Commands are therefore emitted onto the native skills
3586
+ * surface, one `SKILL.md` per command: `.devin/skills/<slug>/SKILL.md`
3587
+ * (project) and `~/.config/devin/skills/<slug>/SKILL.md` (global). The legacy
3588
+ * Windsurf/Cascade-era `.devin/workflows/` and
3589
+ * `~/.codeium/windsurf/global_workflows/` locations are no longer emitted.
3590
+ *
3591
+ * Import and deletion are intentionally no-ops for this target: the skills
3592
+ * feature owns the `.devin/skills/` tree, so importing it as commands would
3593
+ * double-import every skill (mirrors the Hermes Agent commands target).
3594
+ *
3595
+ * @see https://docs.devin.ai/cli/extensibility
3596
+ * @see https://docs.devin.ai/cli/extensibility/skills/overview
3137
3597
  */
3138
3598
  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)
3599
+ static isTargetedByRulesyncCommand(rulesyncCommand) {
3600
+ return this.isTargetedByRulesyncCommandDefault({
3601
+ rulesyncCommand,
3602
+ toolTarget: "devin"
3149
3603
  });
3150
- this.frontmatter = frontmatter;
3151
- this.body = body;
3152
3604
  }
3153
3605
  static getSettablePaths({ global = false } = {}) {
3154
- if (global) return { relativeDirPath: CODEIUM_WINDSURF_GLOBAL_WORKFLOWS_DIR_PATH };
3155
- return { relativeDirPath: DEVIN_WORKFLOWS_DIR_PATH };
3606
+ return { relativeDirPath: global ? DEVIN_GLOBAL_SKILLS_DIR_PATH : DEVIN_SKILLS_DIR_PATH };
3156
3607
  }
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
- });
3180
- }
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
3608
+ constructor({ slug, ...params }) {
3609
+ super({
3610
+ ...params,
3611
+ ...slug !== void 0 && {
3612
+ relativeDirPath: (0, node_path.join)(params.relativeDirPath, slug),
3613
+ relativeFilePath: "SKILL.md"
3614
+ }
3197
3615
  });
3198
3616
  }
3199
3617
  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 {
3618
+ return {
3206
3619
  success: true,
3207
3620
  error: null
3208
3621
  };
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
3622
  }
3214
- static isTargetedByRulesyncCommand(rulesyncCommand) {
3215
- return this.isTargetedByRulesyncCommandDefault({
3216
- rulesyncCommand,
3217
- toolTarget: "devin"
3623
+ toRulesyncCommand() {
3624
+ const slug = (0, node_path.basename)((0, node_path.dirname)(this.getRelativePathFromCwd()));
3625
+ const { frontmatter, body } = parseFrontmatter(this.getFileContent(), this.getFilePath());
3626
+ const description = typeof frontmatter.description === "string" ? frontmatter.description : void 0;
3627
+ return new RulesyncCommand({
3628
+ relativeDirPath: RULESYNC_COMMANDS_RELATIVE_DIR_PATH,
3629
+ relativeFilePath: `${slug}.md`,
3630
+ frontmatter: { description },
3631
+ body: body.trimStart()
3218
3632
  });
3219
3633
  }
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)}`);
3634
+ static fromRulesyncCommand({ outputRoot, rulesyncCommand, global = false }) {
3635
+ const paths = DevinCommand.getSettablePaths({ global });
3226
3636
  return new DevinCommand({
3227
3637
  outputRoot,
3228
3638
  relativeDirPath: paths.relativeDirPath,
3229
- relativeFilePath,
3230
- frontmatter: result.data,
3231
- body: content.trim(),
3232
- validate
3639
+ relativeFilePath: SKILL_FILE_NAME$1,
3640
+ slug: commandSlug(rulesyncCommand.getRelativeFilePath()),
3641
+ fileContent: commandSkillContent$1(rulesyncCommand)
3233
3642
  });
3234
3643
  }
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
- });
3644
+ getFileContent() {
3645
+ return this.fileContent;
3244
3646
  }
3245
3647
  };
3246
3648
  //#endregion
@@ -3569,10 +3971,7 @@ const HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_MANIFEST_PATH = (0, node_path.join)(
3569
3971
  const HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_INIT_PATH = (0, node_path.join)(HERMESAGENT_RULESYNC_SUBAGENTS_PLUGIN_DIR_PATH, "__init__.py");
3570
3972
  //#endregion
3571
3973
  //#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
- }
3974
+ const SKILL_FILE_NAME = "SKILL.md";
3576
3975
  function commandSkillContent(rulesyncCommand) {
3577
3976
  const slug = commandSlug(rulesyncCommand.getRelativeFilePath());
3578
3977
  const description = rulesyncCommand.getFrontmatter().description ?? `${slug} command`;
@@ -3589,7 +3988,7 @@ var HermesagentCommand = class HermesagentCommand extends ToolCommand {
3589
3988
  static getSettablePaths({ slug = "command" } = {}) {
3590
3989
  return {
3591
3990
  relativeDirPath: (0, node_path.join)(HERMESAGENT_SKILLS_DIR_PATH, slug),
3592
- relativeFilePath: SKILL_FILE_NAME$1
3991
+ relativeFilePath: SKILL_FILE_NAME
3593
3992
  };
3594
3993
  }
3595
3994
  constructor({ slug, ...params }) {
@@ -3620,7 +4019,7 @@ var HermesagentCommand = class HermesagentCommand extends ToolCommand {
3620
4019
  return new HermesagentCommand({
3621
4020
  outputRoot,
3622
4021
  relativeDirPath: "",
3623
- relativeFilePath: SKILL_FILE_NAME$1,
4022
+ relativeFilePath: SKILL_FILE_NAME,
3624
4023
  slug: commandSlug(rulesyncCommand.getRelativeFilePath()),
3625
4024
  fileContent: commandSkillContent(rulesyncCommand)
3626
4025
  });
@@ -4280,6 +4679,15 @@ const SHARED_CONFIG_OWNERSHIP = {
4280
4679
  }
4281
4680
  }
4282
4681
  },
4682
+ ".vscode/settings.json": {
4683
+ format: "jsonc",
4684
+ invalidRootPolicy: "error",
4685
+ jsoncParseErrors: "error",
4686
+ features: { permissions: {
4687
+ kind: "replace-owned-keys",
4688
+ ownedKeys: ["chat.tools.terminal.autoApprove"]
4689
+ } }
4690
+ },
4283
4691
  ".qwen/settings.json": {
4284
4692
  format: "json",
4285
4693
  features: {
@@ -4855,6 +5263,7 @@ const PI_AGENT_SKILLS_DIR_PATH = (0, node_path.join)(PI_AGENT_DIR, "skills");
4855
5263
  const PI_PROMPTS_DIR_PATH = (0, node_path.join)(".pi", "prompts");
4856
5264
  const PI_SKILLS_DIR_PATH = (0, node_path.join)(".pi", "skills");
4857
5265
  const PI_RULE_FILE_NAME = "AGENTS.md";
5266
+ const PI_APPEND_SYSTEM_FILE_NAME = "APPEND_SYSTEM.md";
4858
5267
  //#endregion
4859
5268
  //#region src/features/commands/pi-command.ts
4860
5269
  /**
@@ -5389,9 +5798,6 @@ const ROVODEV_AGENTS_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
5389
5798
  const ROVODEV_PROMPTS_FILE_NAME = "prompts.yml";
5390
5799
  const ROVODEV_PROMPTS_DIR_PATH = (0, node_path.join)(ROVODEV_DIR, "prompts");
5391
5800
  //#endregion
5392
- //#region src/types/tool-file.ts
5393
- var ToolFile = class extends AiFile {};
5394
- //#endregion
5395
5801
  //#region src/features/commands/rovodev-command.ts
5396
5802
  /**
5397
5803
  * Rovo Dev CLI "saved prompts": a file-based custom-command surface made of a
@@ -5880,7 +6286,8 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
5880
6286
  supportsProject: false,
5881
6287
  supportsGlobal: true,
5882
6288
  isSimulated: false,
5883
- supportsSubdirectory: true
6289
+ supportsSubdirectory: true,
6290
+ skipToolFileScan: true
5884
6291
  }
5885
6292
  }],
5886
6293
  ["junie", {
@@ -6010,7 +6417,8 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
6010
6417
  supportsProject: true,
6011
6418
  supportsGlobal: true,
6012
6419
  isSimulated: false,
6013
- supportsSubdirectory: false
6420
+ supportsSubdirectory: false,
6421
+ skipToolFileScan: true
6014
6422
  }
6015
6423
  }]
6016
6424
  ]);
@@ -6113,6 +6521,7 @@ var CommandsProcessor = class extends FeatureProcessor {
6113
6521
  */
6114
6522
  async loadToolFiles({ forDeletion = false } = {}) {
6115
6523
  const factory = this.getFactory(this.toolTarget);
6524
+ if (factory.meta.skipToolFileScan) return [];
6116
6525
  const paths = factory.class.getSettablePaths({ global: this.global });
6117
6526
  const outputRootFull = (0, node_path.join)(this.outputRoot, paths.relativeDirPath);
6118
6527
  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 +6631,10 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
6222
6631
  type: zod_mini.z.optional(zod_mini.z.enum([
6223
6632
  "command",
6224
6633
  "prompt",
6225
- "http"
6634
+ "http",
6635
+ "agent",
6636
+ "mcp_tool",
6637
+ "function"
6226
6638
  ])),
6227
6639
  url: zod_mini.z.optional(safeString),
6228
6640
  timeout: zod_mini.z.optional(zod_mini.z.number()),
@@ -6235,12 +6647,72 @@ const HookDefinitionSchema = zod_mini.z.looseObject({
6235
6647
  sequential: zod_mini.z.optional(zod_mini.z.boolean()),
6236
6648
  async: zod_mini.z.optional(zod_mini.z.boolean()),
6237
6649
  env: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), safeString)),
6238
- shell: zod_mini.z.optional(safeString),
6650
+ shell: zod_mini.z.optional(zod_mini.z.enum(["bash", "powershell"])),
6239
6651
  statusMessage: zod_mini.z.optional(safeString),
6240
6652
  headers: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), safeString)),
6241
6653
  allowedEnvVars: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
6242
- once: zod_mini.z.optional(zod_mini.z.boolean())
6654
+ once: zod_mini.z.optional(zod_mini.z.boolean()),
6655
+ server: zod_mini.z.optional(safeString),
6656
+ tool: zod_mini.z.optional(safeString),
6657
+ input: zod_mini.z.optional(zod_mini.z.looseObject({})),
6658
+ model: zod_mini.z.optional(safeString),
6659
+ if: zod_mini.z.optional(safeString)
6243
6660
  });
6661
+ /**
6662
+ * All canonical hook event names.
6663
+ * Each tool supports a subset of these events.
6664
+ */
6665
+ const HOOK_EVENTS = [
6666
+ "sessionStart",
6667
+ "sessionEnd",
6668
+ "preToolUse",
6669
+ "postToolUse",
6670
+ "preModelInvocation",
6671
+ "postModelInvocation",
6672
+ "beforeSubmitPrompt",
6673
+ "stop",
6674
+ "subagentStop",
6675
+ "preCompact",
6676
+ "postCompact",
6677
+ "contextOffload",
6678
+ "postToolUseFailure",
6679
+ "subagentStart",
6680
+ "beforeShellExecution",
6681
+ "afterShellExecution",
6682
+ "beforeMCPExecution",
6683
+ "afterMCPExecution",
6684
+ "beforeReadFile",
6685
+ "afterFileEdit",
6686
+ "beforeAgentResponse",
6687
+ "afterAgentResponse",
6688
+ "afterAgentThought",
6689
+ "beforeTabFileRead",
6690
+ "afterTabFileEdit",
6691
+ "permissionRequest",
6692
+ "notification",
6693
+ "setup",
6694
+ "afterError",
6695
+ "beforeToolSelection",
6696
+ "worktreeCreate",
6697
+ "worktreeRemove",
6698
+ "workspaceOpen",
6699
+ "messageDisplay",
6700
+ "todoCreated",
6701
+ "todoCompleted",
6702
+ "stopFailure",
6703
+ "instructionsLoaded",
6704
+ "userPromptExpansion",
6705
+ "postToolBatch",
6706
+ "permissionDenied",
6707
+ "taskCreated",
6708
+ "taskCompleted",
6709
+ "teammateIdle",
6710
+ "configChange",
6711
+ "cwdChanged",
6712
+ "fileChanged",
6713
+ "elicitation",
6714
+ "elicitationResult"
6715
+ ];
6244
6716
  /** Hook events supported by Cursor. */
6245
6717
  const CURSOR_HOOK_EVENTS = [
6246
6718
  "sessionStart",
@@ -6715,12 +7187,28 @@ const CANONICAL_TO_HERMESAGENT_EVENT_NAMES = {
6715
7187
  */
6716
7188
  const HERMESAGENT_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_HERMESAGENT_EVENT_NAMES).map(([k, v]) => [v, k]));
6717
7189
  const hooksRecordSchema = zod_mini.z.record(zod_mini.z.string(), zod_mini.z.array(HookDefinitionSchema));
7190
+ const HOOK_EVENT_SET = new Set(HOOK_EVENTS);
7191
+ /** Whether `value` is a canonical hook event name. */
7192
+ const isHookEvent = (value) => HOOK_EVENT_SET.has(value);
7193
+ /**
7194
+ * Top-level `hooks` record whose keys must be canonical event names, so typos
7195
+ * are rejected at parse time. Keys are validated with a refinement (instead of
7196
+ * an enum key schema) to keep the inferred type a plain string record.
7197
+ *
7198
+ * The per-tool override blocks below deliberately keep the lenient
7199
+ * `hooksRecordSchema`: some are documented to pass tool-native event keys
7200
+ * through verbatim (e.g. kiro-ide's IDE-only `PostFileSave`/`PreTaskExec`
7201
+ * triggers), which the canonical enum would reject.
7202
+ */
7203
+ 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) => {
7204
+ return `unknown hook event name(s): ${Object.keys(issue.input ?? {}).filter((key) => !HOOK_EVENT_SET.has(key)).join(", ")}`;
7205
+ } }));
6718
7206
  /**
6719
7207
  * Canonical hooks config (canonical event names in camelCase).
6720
7208
  */
6721
7209
  const HooksConfigSchema = zod_mini.z.looseObject({
6722
7210
  version: zod_mini.z.optional(zod_mini.z.number()),
6723
- hooks: hooksRecordSchema,
7211
+ hooks: canonicalHooksRecordSchema,
6724
7212
  cursor: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
6725
7213
  claudecode: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
6726
7214
  copilot: zod_mini.z.optional(zod_mini.z.looseObject({ hooks: zod_mini.z.optional(hooksRecordSchema) })),
@@ -7157,6 +7645,20 @@ const CANONICAL_TO_GROKCLI_EVENT_NAMES = {
7157
7645
  */
7158
7646
  const GROKCLI_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_GROKCLI_EVENT_NAMES).map(([k, v]) => [v, k]));
7159
7647
  //#endregion
7648
+ //#region src/utils/object.ts
7649
+ /**
7650
+ * Return a shallow copy of `obj` keeping only the entries whose value is
7651
+ * neither `undefined` nor `null`.
7652
+ *
7653
+ * Used to assemble generated config objects without one conditional spread per
7654
+ * optional field (which would otherwise exceed the lint complexity budget).
7655
+ */
7656
+ function compact(obj) {
7657
+ const result = {};
7658
+ for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== null) result[key] = value;
7659
+ return result;
7660
+ }
7661
+ //#endregion
7160
7662
  //#region src/features/hooks/tool-hooks-converter.ts
7161
7663
  function isToolMatcherEntry(x) {
7162
7664
  if (x === null || typeof x !== "object") return false;
@@ -7214,6 +7716,41 @@ function importBooleanPassthroughFields({ h, converterConfig }) {
7214
7716
  return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ tool }) => typeof h[tool] === "boolean").map(({ canonical, tool }) => [canonical, h[tool]]));
7215
7717
  }
7216
7718
  /**
7719
+ * Emit the configured string passthrough fields on the tool side, mapping each
7720
+ * canonical field name to its (possibly renamed) tool field name. Only non-empty
7721
+ * string values are carried through.
7722
+ */
7723
+ function emitStringPassthroughFields({ def, converterConfig }) {
7724
+ return Object.fromEntries((converterConfig.stringPassthroughFields ?? []).filter(({ canonical }) => typeof def[canonical] === "string" && def[canonical] !== "").map(({ canonical, tool }) => [tool, def[canonical]]));
7725
+ }
7726
+ /**
7727
+ * Import the configured string passthrough fields back into canonical fields,
7728
+ * reversing {@link emitStringPassthroughFields}. Only non-empty string values
7729
+ * are read.
7730
+ */
7731
+ function importStringPassthroughFields({ h, converterConfig }) {
7732
+ return Object.fromEntries((converterConfig.stringPassthroughFields ?? []).filter(({ tool }) => typeof h[tool] === "string" && h[tool] !== "").map(({ canonical, tool }) => [canonical, h[tool]]));
7733
+ }
7734
+ /**
7735
+ * Emit the payload fields specific to a hook type — `url`/`headers`/
7736
+ * `allowedEnvVars` for http, `server`/`tool`/`input` for mcp_tool, `model`
7737
+ * for prompt/agent. https://code.claude.com/docs/en/hooks
7738
+ */
7739
+ function emitTypePayloadFields({ def, hookType, converterConfig }) {
7740
+ if (hookType === "http") return compact({
7741
+ url: def.url,
7742
+ headers: def.headers,
7743
+ allowedEnvVars: def.allowedEnvVars
7744
+ });
7745
+ if (hookType === "mcp_tool") return compact({
7746
+ server: def.server,
7747
+ tool: def.tool,
7748
+ input: def.input
7749
+ });
7750
+ if ((hookType === "prompt" || hookType === "agent") && converterConfig.emitsPromptModel) return compact({ model: def.model });
7751
+ return {};
7752
+ }
7753
+ /**
7217
7754
  * Convert the definitions of a single matcher group into tool hook entries,
7218
7755
  * honoring supported hook types and passthrough fields.
7219
7756
  */
@@ -7231,10 +7768,19 @@ function buildToolHooks({ defs, converterConfig }) {
7231
7768
  def,
7232
7769
  converterConfig
7233
7770
  }),
7771
+ ...emitStringPassthroughFields({
7772
+ def,
7773
+ converterConfig
7774
+ }),
7234
7775
  type: hookType,
7235
7776
  ...command !== void 0 && command !== null && { command },
7236
7777
  ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
7237
7778
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
7779
+ ...emitTypePayloadFields({
7780
+ def,
7781
+ hookType,
7782
+ converterConfig
7783
+ }),
7238
7784
  ...converterConfig.passthroughFields?.includes("name") && def.name !== void 0 && def.name !== null && { name: def.name },
7239
7785
  ...converterConfig.passthroughFields?.includes("description") && def.description !== void 0 && def.description !== null && { description: def.description }
7240
7786
  });
@@ -7301,6 +7847,46 @@ function stripCommandPrefix({ command, converterConfig }) {
7301
7847
  return cmd;
7302
7848
  }
7303
7849
  /**
7850
+ * Hook types preserved verbatim on import — Claude Code's five documented
7851
+ * handler types. Anything else is coerced to `command` as before.
7852
+ * https://code.claude.com/docs/en/hooks
7853
+ */
7854
+ const IMPORTED_HOOK_TYPES = /* @__PURE__ */ new Set([
7855
+ "command",
7856
+ "prompt",
7857
+ "http",
7858
+ "mcp_tool",
7859
+ "agent"
7860
+ ]);
7861
+ function isImportedHookType(value) {
7862
+ return typeof value === "string" && IMPORTED_HOOK_TYPES.has(value);
7863
+ }
7864
+ function isStringRecord(value) {
7865
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
7866
+ return Object.values(value).every((v) => typeof v === "string");
7867
+ }
7868
+ function isStringArray(value) {
7869
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
7870
+ }
7871
+ /**
7872
+ * Import the payload fields specific to a hook type, type-checking each raw
7873
+ * value before it enters the canonical definition.
7874
+ */
7875
+ function importTypePayloadFields({ h, hookType }) {
7876
+ if (hookType === "http") return {
7877
+ ...typeof h.url === "string" && { url: h.url },
7878
+ ...isStringRecord(h.headers) && { headers: h.headers },
7879
+ ...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
7880
+ };
7881
+ if (hookType === "mcp_tool") return {
7882
+ ...typeof h.server === "string" && { server: h.server },
7883
+ ...typeof h.tool === "string" && { tool: h.tool },
7884
+ ...h.input !== null && typeof h.input === "object" && !Array.isArray(h.input) && { input: h.input }
7885
+ };
7886
+ if (hookType === "prompt" || hookType === "agent") return typeof h.model === "string" ? { model: h.model } : {};
7887
+ return {};
7888
+ }
7889
+ /**
7304
7890
  * Convert a single tool hook record into a canonical hook definition.
7305
7891
  */
7306
7892
  function toolHookToCanonical({ h, rawEntry, converterConfig }) {
@@ -7308,7 +7894,7 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
7308
7894
  command: h.command,
7309
7895
  converterConfig
7310
7896
  });
7311
- const hookType = h.type === "command" || h.type === "prompt" ? h.type : "command";
7897
+ const hookType = isImportedHookType(h.type) ? h.type : "command";
7312
7898
  const timeout = typeof h.timeout === "number" ? h.timeout : void 0;
7313
7899
  const prompt = typeof h.prompt === "string" ? h.prompt : void 0;
7314
7900
  return {
@@ -7316,12 +7902,20 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
7316
7902
  ...command !== void 0 && command !== null && { command },
7317
7903
  ...timeout !== void 0 && timeout !== null && { timeout },
7318
7904
  ...prompt !== void 0 && prompt !== null && { prompt },
7905
+ ...importTypePayloadFields({
7906
+ h,
7907
+ hookType
7908
+ }),
7319
7909
  ...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
7320
7910
  ...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
7321
7911
  ...importBooleanPassthroughFields({
7322
7912
  h,
7323
7913
  converterConfig
7324
7914
  }),
7915
+ ...importStringPassthroughFields({
7916
+ h,
7917
+ converterConfig
7918
+ }),
7325
7919
  ...rawEntry.matcher !== void 0 && rawEntry.matcher !== null && rawEntry.matcher !== "" && { matcher: rawEntry.matcher }
7326
7920
  };
7327
7921
  }
@@ -7335,6 +7929,32 @@ function toolMatcherEntryToCanonical({ rawEntry, converterConfig }) {
7335
7929
  converterConfig
7336
7930
  }));
7337
7931
  }
7932
+ /**
7933
+ * Assemble the canonical hooks config a tool importer writes to
7934
+ * `.rulesync/hooks.json`.
7935
+ *
7936
+ * The top-level `hooks` record only accepts canonical event names, so any
7937
+ * imported native event key without a canonical mapping is moved under the
7938
+ * importing tool's own override block (`<overrideKey>.hooks`), whose keys stay
7939
+ * lenient. This mirrors the generate direction — override blocks pass
7940
+ * tool-native keys through verbatim — so documented native triggers (e.g.
7941
+ * kiro-ide's `PostFileSave`) survive an import → generate round-trip instead
7942
+ * of failing canonical validation.
7943
+ */
7944
+ function buildImportedHooksConfig({ hooks, overrideKey, version = 1, extraOverride }) {
7945
+ const canonical = {};
7946
+ const native = {};
7947
+ for (const [event, defs] of Object.entries(hooks)) if (isHookEvent(event)) canonical[event] = defs;
7948
+ else native[event] = defs;
7949
+ const override = { ...extraOverride };
7950
+ if (Object.keys(native).length > 0) override.hooks = native;
7951
+ const config = {
7952
+ version,
7953
+ hooks: canonical
7954
+ };
7955
+ if (Object.keys(override).length > 0) config[overrideKey] = override;
7956
+ return config;
7957
+ }
7338
7958
  function toolHooksToCanonical({ hooks, converterConfig }) {
7339
7959
  if (hooks === null || hooks === void 0 || typeof hooks !== "object") return {};
7340
7960
  const canonical = {};
@@ -7505,7 +8125,8 @@ const ANTIGRAVITY_CONVERTER_CONFIG = {
7505
8125
  "preModelInvocation",
7506
8126
  "postModelInvocation",
7507
8127
  "stop"
7508
- ])
8128
+ ]),
8129
+ supportedHookTypes: /* @__PURE__ */ new Set(["command"])
7509
8130
  };
7510
8131
  /**
7511
8132
  * Antigravity's `hooks.json` is keyed by a named hook whose value holds the
@@ -7612,10 +8233,11 @@ var AntigravityHooks = class extends ToolHooks {
7612
8233
  hooks: flattenAntigravityHooks(parsed),
7613
8234
  converterConfig: ANTIGRAVITY_CONVERTER_CONFIG
7614
8235
  });
7615
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7616
- version: 1,
7617
- hooks
7618
- }, null, 2) });
8236
+ const overrideKey = this.constructor.getOverrideKey();
8237
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8238
+ hooks,
8239
+ overrideKey
8240
+ }), null, 2) });
7619
8241
  }
7620
8242
  validate() {
7621
8243
  return {
@@ -7736,7 +8358,8 @@ const AUGMENTCODE_CONVERTER_CONFIG = {
7736
8358
  "sessionEnd",
7737
8359
  "stop",
7738
8360
  "notification"
7739
- ])
8361
+ ]),
8362
+ supportedHookTypes: /* @__PURE__ */ new Set(["command"])
7740
8363
  };
7741
8364
  /**
7742
8365
  * AugmentCode (Auggie CLI) lifecycle hooks.
@@ -7818,10 +8441,10 @@ var AugmentcodeHooks = class AugmentcodeHooks extends ToolHooks {
7818
8441
  hooks: settings.hooks,
7819
8442
  converterConfig: AUGMENTCODE_CONVERTER_CONFIG
7820
8443
  });
7821
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7822
- version: 1,
7823
- hooks
7824
- }, null, 2) });
8444
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8445
+ hooks,
8446
+ overrideKey: "augmentcode"
8447
+ }), null, 2) });
7825
8448
  }
7826
8449
  validate() {
7827
8450
  return {
@@ -7856,7 +8479,19 @@ const CLAUDE_CONVERTER_CONFIG = {
7856
8479
  "taskCompleted",
7857
8480
  "teammateIdle",
7858
8481
  "cwdChanged"
7859
- ])
8482
+ ]),
8483
+ supportedHookTypes: /* @__PURE__ */ new Set([
8484
+ "command",
8485
+ "prompt",
8486
+ "http",
8487
+ "mcp_tool",
8488
+ "agent"
8489
+ ]),
8490
+ emitsPromptModel: true,
8491
+ stringPassthroughFields: [{
8492
+ canonical: "if",
8493
+ tool: "if"
8494
+ }]
7860
8495
  };
7861
8496
  var ClaudecodeHooks = class ClaudecodeHooks extends ToolHooks {
7862
8497
  constructor(params) {
@@ -7921,10 +8556,10 @@ var ClaudecodeHooks = class ClaudecodeHooks extends ToolHooks {
7921
8556
  hooks: settings.hooks,
7922
8557
  converterConfig: CLAUDE_CONVERTER_CONFIG
7923
8558
  });
7924
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
7925
- version: 1,
7926
- hooks
7927
- }, null, 2) });
8559
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8560
+ hooks,
8561
+ overrideKey: "claudecode"
8562
+ }), null, 2) });
7928
8563
  }
7929
8564
  validate() {
7930
8565
  return {
@@ -8067,10 +8702,10 @@ var CodexcliHooks = class CodexcliHooks extends ToolHooks {
8067
8702
  hooks: parsed.hooks,
8068
8703
  converterConfig: CODEXCLI_CONVERTER_CONFIG
8069
8704
  });
8070
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8071
- version: 1,
8072
- hooks
8073
- }, null, 2) });
8705
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8706
+ hooks,
8707
+ overrideKey: "codexcli"
8708
+ }), null, 2) });
8074
8709
  }
8075
8710
  validate() {
8076
8711
  return {
@@ -8238,10 +8873,10 @@ var CopilotHooks = class CopilotHooks extends ToolHooks {
8238
8873
  throw new Error(`Failed to parse Copilot hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8239
8874
  }
8240
8875
  const hooks = copilotHooksToCanonical(parsed.hooks, options?.logger);
8241
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8242
- version: 1,
8243
- hooks
8244
- }, null, 2) });
8876
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
8877
+ hooks,
8878
+ overrideKey: "copilot"
8879
+ }), null, 2) });
8245
8880
  }
8246
8881
  validate() {
8247
8882
  return {
@@ -8260,20 +8895,6 @@ var CopilotHooks = class CopilotHooks extends ToolHooks {
8260
8895
  }
8261
8896
  };
8262
8897
  //#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
8898
  //#region src/features/hooks/copilotcli-hooks.ts
8278
8899
  /**
8279
8900
  * GitHub Copilot CLI hooks.
@@ -8406,7 +9027,7 @@ function buildCopilotCliEntriesForEvent({ eventName, definitions, canonicalSchem
8406
9027
  ...timeoutPart,
8407
9028
  ...rest
8408
9029
  });
8409
- else entries.push({
9030
+ else if (hookType === "command") entries.push({
8410
9031
  type: "command",
8411
9032
  ...matcherPart,
8412
9033
  ...compact({
@@ -8558,10 +9179,10 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
8558
9179
  throw new Error(`Failed to parse Copilot CLI hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8559
9180
  }
8560
9181
  const hooks = copilotCliHooksToCanonical(parsed.hooks, options?.logger);
8561
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8562
- version: 1,
8563
- hooks
8564
- }, null, 2) });
9182
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9183
+ hooks,
9184
+ overrideKey: "copilotcli"
9185
+ }), null, 2) });
8565
9186
  }
8566
9187
  validate() {
8567
9188
  return {
@@ -8616,9 +9237,10 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8616
9237
  ...config.cursor?.hooks
8617
9238
  };
8618
9239
  const mappedHooks = {};
9240
+ const cursorSupportedTypes = /* @__PURE__ */ new Set(["command", "prompt"]);
8619
9241
  for (const [eventName, defs] of Object.entries(mergedHooks)) {
8620
9242
  const cursorEventName = CANONICAL_TO_CURSOR_EVENT_NAMES[eventName] ?? eventName;
8621
- mappedHooks[cursorEventName] = defs.map((def) => ({
9243
+ const mappedDefs = defs.filter((def) => cursorSupportedTypes.has(def.type ?? "command")).map((def) => ({
8622
9244
  ...def.type !== void 0 && def.type !== null && { type: def.type },
8623
9245
  ...def.command !== void 0 && def.command !== null && { command: def.command },
8624
9246
  ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
@@ -8627,6 +9249,7 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8627
9249
  ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
8628
9250
  ...def.failClosed !== void 0 && def.failClosed !== null && { failClosed: def.failClosed }
8629
9251
  }));
9252
+ if (mappedDefs.length > 0) mappedHooks[cursorEventName] = mappedDefs;
8630
9253
  }
8631
9254
  const cursorConfig = {
8632
9255
  version: config.version ?? 1,
@@ -8653,10 +9276,11 @@ var CursorHooks = class CursorHooks extends ToolHooks {
8653
9276
  canonicalHooks[eventName] = defs;
8654
9277
  }
8655
9278
  const version = parsed.version ?? 1;
8656
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8657
- version,
8658
- hooks: canonicalHooks
8659
- }, null, 2) });
9279
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9280
+ hooks: canonicalHooks,
9281
+ overrideKey: "cursor",
9282
+ version
9283
+ }), null, 2) });
8660
9284
  }
8661
9285
  validate() {
8662
9286
  return {
@@ -8711,7 +9335,7 @@ function canonicalToDeepagentsHooks(config) {
8711
9335
  const deepagentsEvent = CANONICAL_TO_DEEPAGENTS_EVENT_NAMES[canonicalEvent];
8712
9336
  if (!deepagentsEvent) continue;
8713
9337
  for (const def of definitions) {
8714
- if (def.type === "prompt") continue;
9338
+ if ((def.type ?? "command") !== "command") continue;
8715
9339
  if (!def.command) continue;
8716
9340
  if (def.matcher) continue;
8717
9341
  entries.push({
@@ -8801,10 +9425,10 @@ var DeepagentsHooks = class DeepagentsHooks extends ToolHooks {
8801
9425
  throw new Error(`Failed to parse deepagents hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
8802
9426
  }
8803
9427
  const hooks = deepagentsToCanonicalHooks(isDeepagentsHooksFile(parsed) ? parsed.hooks : []);
8804
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8805
- version: 1,
8806
- hooks
8807
- }, null, 2) });
9428
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9429
+ hooks,
9430
+ overrideKey: "deepagents"
9431
+ }), null, 2) });
8808
9432
  }
8809
9433
  validate() {
8810
9434
  return {
@@ -8933,10 +9557,10 @@ var DevinHooks = class DevinHooks extends ToolHooks {
8933
9557
  hooks: this.getRelativeFilePath() === "config.json" ? isRecord(parsed) && isRecord(parsed.hooks) ? parsed.hooks : {} : parsed,
8934
9558
  converterConfig: DEVIN_CONVERTER_CONFIG
8935
9559
  });
8936
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
8937
- version: 1,
8938
- hooks
8939
- }, null, 2) });
9560
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9561
+ hooks,
9562
+ overrideKey: "devin"
9563
+ }), null, 2) });
8940
9564
  }
8941
9565
  validate() {
8942
9566
  return {
@@ -8960,7 +9584,8 @@ const FACTORYDROID_CONVERTER_CONFIG = {
8960
9584
  supportedEvents: FACTORYDROID_HOOK_EVENTS,
8961
9585
  canonicalToToolEventNames: CANONICAL_TO_FACTORYDROID_EVENT_NAMES,
8962
9586
  toolToCanonicalEventNames: FACTORYDROID_TO_CANONICAL_EVENT_NAMES,
8963
- projectDirVar: "$FACTORY_PROJECT_DIR"
9587
+ projectDirVar: "$FACTORY_PROJECT_DIR",
9588
+ supportedHookTypes: /* @__PURE__ */ new Set(["command", "prompt"])
8964
9589
  };
8965
9590
  var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
8966
9591
  constructor(params) {
@@ -9028,10 +9653,10 @@ var FactorydroidHooks = class FactorydroidHooks extends ToolHooks {
9028
9653
  hooks: settings.hooks,
9029
9654
  converterConfig: FACTORYDROID_CONVERTER_CONFIG
9030
9655
  });
9031
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9032
- version: 1,
9033
- hooks
9034
- }, null, 2) });
9656
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9657
+ hooks,
9658
+ overrideKey: "factorydroid"
9659
+ }), null, 2) });
9035
9660
  }
9036
9661
  validate() {
9037
9662
  return {
@@ -9122,10 +9747,10 @@ var GooseHooks = class GooseHooks extends ToolHooks {
9122
9747
  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
9748
  converterConfig: GOOSE_CONVERTER_CONFIG
9124
9749
  });
9125
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9126
- version: 1,
9127
- hooks
9128
- }, null, 2) });
9750
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9751
+ hooks,
9752
+ overrideKey: "goose"
9753
+ }), null, 2) });
9129
9754
  }
9130
9755
  validate() {
9131
9756
  return {
@@ -9283,10 +9908,10 @@ var GrokcliHooks = class GrokcliHooks extends ToolHooks {
9283
9908
  hooks: isRecord(parsed) && isRecord(parsed.hooks) ? parsed.hooks : {},
9284
9909
  converterConfig: GROKCLI_CONVERTER_CONFIG
9285
9910
  });
9286
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9287
- version: 1,
9288
- hooks
9289
- }, null, 2) });
9911
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
9912
+ hooks,
9913
+ overrideKey: "grokcli"
9914
+ }), null, 2) });
9290
9915
  }
9291
9916
  validate() {
9292
9917
  return {
@@ -9447,10 +10072,10 @@ var HermesagentHooks = class HermesagentHooks extends ToolHooks {
9447
10072
  format: "yaml",
9448
10073
  fileContent: this.getFileContent()
9449
10074
  }).hooks);
9450
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9451
- version: 1,
9452
- hooks
9453
- }, null, 2) });
10075
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10076
+ hooks,
10077
+ overrideKey: "hermesagent"
10078
+ }), null, 2) });
9454
10079
  }
9455
10080
  static fromRulesyncHooks({ outputRoot, rulesyncHooks, logger }) {
9456
10081
  const config = rulesyncHooks.getJson();
@@ -9553,10 +10178,10 @@ var JunieHooks = class JunieHooks extends ToolHooks {
9553
10178
  hooks: settings.hooks,
9554
10179
  converterConfig: JUNIE_CONVERTER_CONFIG
9555
10180
  });
9556
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9557
- version: 1,
9558
- hooks
9559
- }, null, 2) });
10181
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10182
+ hooks,
10183
+ overrideKey: "junie"
10184
+ }), null, 2) });
9560
10185
  }
9561
10186
  validate() {
9562
10187
  return {
@@ -9601,7 +10226,7 @@ function collectOpencodeStyleHandlers({ effectiveHooks, eventMap, namedEventHand
9601
10226
  if (!toolEvent) continue;
9602
10227
  const handlers = [];
9603
10228
  for (const def of definitions) {
9604
- if (def.type === "prompt") continue;
10229
+ if ((def.type ?? "command") !== "command") continue;
9605
10230
  if (!def.command) continue;
9606
10231
  handlers.push({
9607
10232
  command: def.command,
@@ -9906,10 +10531,11 @@ var KiroHooks = class KiroHooks extends ToolHooks {
9906
10531
  throw new Error(`Failed to parse Kiro hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
9907
10532
  }
9908
10533
  const hooks = kiroHooksToCanonical(agentConfig.hooks);
9909
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
9910
- version: 1,
9911
- hooks
9912
- }, null, 2) });
10534
+ const overrideKey = this.constructor.getOverrideKey();
10535
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10536
+ hooks,
10537
+ overrideKey
10538
+ }), null, 2) });
9913
10539
  }
9914
10540
  validate() {
9915
10541
  return {
@@ -10118,10 +10744,10 @@ var KiroIdeHooks = class KiroIdeHooks extends ToolHooks {
10118
10744
  throw new Error(`Failed to parse Kiro IDE hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10119
10745
  }
10120
10746
  const hooks = kiroIdeHooksToCanonical(parsed.hooks ?? []);
10121
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10122
- version: 1,
10123
- hooks
10124
- }, null, 2) });
10747
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
10748
+ hooks,
10749
+ overrideKey: "kiro-ide"
10750
+ }), null, 2) });
10125
10751
  }
10126
10752
  validate() {
10127
10753
  return {
@@ -10245,11 +10871,18 @@ function canonicalToQwencodeHooks(config) {
10245
10871
  ...sharedHooks,
10246
10872
  ...config.qwencode?.hooks
10247
10873
  };
10874
+ const qwencodeSupportedTypes = /* @__PURE__ */ new Set([
10875
+ "command",
10876
+ "prompt",
10877
+ "http",
10878
+ "function"
10879
+ ]);
10248
10880
  const qwencode = {};
10249
10881
  for (const [eventName, definitions] of Object.entries(effectiveHooks)) {
10250
10882
  const qwencodeEventName = CANONICAL_TO_QWENCODE_EVENT_NAMES[eventName] ?? eventName;
10251
10883
  const byMatcher = /* @__PURE__ */ new Map();
10252
10884
  for (const def of definitions) {
10885
+ if (!qwencodeSupportedTypes.has(def.type ?? "command")) continue;
10253
10886
  const key = def.matcher ?? "";
10254
10887
  const list = byMatcher.get(key);
10255
10888
  if (list) list.push(def);
@@ -10309,9 +10942,10 @@ function qwencodeMatcherEntryToCanonical(entry) {
10309
10942
  const sequential = entry.sequential === true;
10310
10943
  const matcher = entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "" ? entry.matcher : void 0;
10311
10944
  for (const h of hooks) {
10312
- const hookType = h.type === "command" || h.type === "prompt" || h.type === "http" ? h.type : "command";
10945
+ const hookType = h.type === "command" || h.type === "prompt" || h.type === "http" || h.type === "function" ? h.type : "command";
10313
10946
  const isHttp = hookType === "http";
10314
10947
  const isCommand = hookType === "command";
10948
+ const shell = h.shell === "bash" || h.shell === "powershell" ? h.shell : void 0;
10315
10949
  defs.push({
10316
10950
  type: hookType,
10317
10951
  ...compact({
@@ -10323,7 +10957,7 @@ function qwencodeMatcherEntryToCanonical(entry) {
10323
10957
  statusMessage: h.statusMessage,
10324
10958
  async: isCommand ? h.async : void 0,
10325
10959
  env: isCommand ? h.env : void 0,
10326
- shell: isCommand ? h.shell : void 0,
10960
+ shell: isCommand ? shell : void 0,
10327
10961
  headers: isHttp ? h.headers : void 0,
10328
10962
  allowedEnvVars: isHttp ? h.allowedEnvVars : void 0,
10329
10963
  once: isHttp ? h.once : void 0,
@@ -10410,15 +11044,11 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
10410
11044
  } catch (error) {
10411
11045
  throw new Error(`Failed to parse Qwen Code hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10412
11046
  }
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
- };
11047
+ const canonical = buildImportedHooksConfig({
11048
+ hooks: qwencodeHooksToCanonical(settings.hooks),
11049
+ overrideKey: "qwencode",
11050
+ ...typeof settings.disableAllHooks === "boolean" && { extraOverride: { disableAllHooks: settings.disableAllHooks } }
11051
+ });
10422
11052
  return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(canonical, null, 2) });
10423
11053
  }
10424
11054
  validate() {
@@ -10580,10 +11210,10 @@ var ReasonixHooks = class ReasonixHooks extends ToolHooks {
10580
11210
  throw new Error(`Failed to parse Reasonix hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10581
11211
  }
10582
11212
  const hooks = reasonixHooksToCanonical(settings.hooks);
10583
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10584
- version: 1,
10585
- hooks
10586
- }, null, 2) });
11213
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
11214
+ hooks,
11215
+ overrideKey: "reasonix"
11216
+ }), null, 2) });
10587
11217
  }
10588
11218
  validate() {
10589
11219
  return {
@@ -10806,10 +11436,10 @@ var VibeHooks = class VibeHooks extends ToolHooks {
10806
11436
  throw new Error(`Failed to parse Vibe hooks content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
10807
11437
  }
10808
11438
  const hooks = vibeHooksToCanonical(parsed);
10809
- return this.toRulesyncHooksDefault({ fileContent: JSON.stringify({
10810
- version: 1,
10811
- hooks
10812
- }, null, 2) });
11439
+ return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
11440
+ hooks,
11441
+ overrideKey: "vibe"
11442
+ }), null, 2) });
10813
11443
  }
10814
11444
  validate() {
10815
11445
  try {
@@ -10896,7 +11526,13 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
10896
11526
  supportsImport: true
10897
11527
  },
10898
11528
  supportedEvents: CLAUDE_HOOK_EVENTS,
10899
- supportedHookTypes: ["command", "prompt"],
11529
+ supportedHookTypes: [
11530
+ "command",
11531
+ "prompt",
11532
+ "http",
11533
+ "mcp_tool",
11534
+ "agent"
11535
+ ],
10900
11536
  supportsMatcher: true
10901
11537
  }],
10902
11538
  ["codexcli", {
@@ -11088,7 +11724,12 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
11088
11724
  supportsImport: true
11089
11725
  },
11090
11726
  supportedEvents: QWENCODE_HOOK_EVENTS,
11091
- supportedHookTypes: ["command"],
11727
+ supportedHookTypes: [
11728
+ "command",
11729
+ "prompt",
11730
+ "http",
11731
+ "function"
11732
+ ],
11092
11733
  supportsMatcher: true
11093
11734
  }],
11094
11735
  ["reasonix", {
@@ -12390,16 +13031,6 @@ var IgnoreProcessor = class extends FeatureProcessor {
12390
13031
  }
12391
13032
  };
12392
13033
  //#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
13034
  //#region src/types/mcp.ts
12404
13035
  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
13036
  const McpServerSchema = zod_mini.z.looseObject({
@@ -13491,10 +14122,19 @@ function mapOauthFromCodex(oauth) {
13491
14122
  }
13492
14123
  const CODEX_MCP_SERVER_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
13493
14124
  function normalizeCodexMcpServerName(name) {
13494
- if (PROTOTYPE_POLLUTION_KEYS.has(name)) return null;
13495
- if (CODEX_MCP_SERVER_NAME_PATTERN.test(name)) return name;
14125
+ if (!PROTOTYPE_POLLUTION_KEYS.has(name) && CODEX_MCP_SERVER_NAME_PATTERN.test(name)) return {
14126
+ codexName: name,
14127
+ usedFallback: false
14128
+ };
13496
14129
  const normalizedName = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
13497
- return normalizedName && !PROTOTYPE_POLLUTION_KEYS.has(normalizedName) ? normalizedName : null;
14130
+ if (normalizedName && !PROTOTYPE_POLLUTION_KEYS.has(normalizedName)) return {
14131
+ codexName: normalizedName,
14132
+ usedFallback: false
14133
+ };
14134
+ return {
14135
+ codexName: `mcp_${(0, node_crypto.createHash)("sha256").update(name).digest("hex").slice(0, 8)}`,
14136
+ usedFallback: true
14137
+ };
13498
14138
  }
13499
14139
  function convertFromCodexFormat(codexMcp) {
13500
14140
  const result = {};
@@ -13508,7 +14148,7 @@ function convertFromCodexFormat(codexMcp) {
13508
14148
  } else if (key === "oauth" && isRecord(value)) converted[key] = mapOauthFromCodex(value);
13509
14149
  else if (Object.hasOwn(CODEX_TO_RULESYNC_FIELD_MAP, key)) {
13510
14150
  const mappedKey = CODEX_TO_RULESYNC_FIELD_MAP[key];
13511
- if (mappedKey) if (isStringArray(value)) converted[mappedKey] = value;
14151
+ if (mappedKey) if (isStringArray$1(value)) converted[mappedKey] = value;
13512
14152
  else warnWithFallback(void 0, `Ignored malformed array for ${key} in MCP server ${name}`);
13513
14153
  } else converted[key] = value;
13514
14154
  }
@@ -13520,12 +14160,9 @@ function convertToCodexFormat(mcpServers) {
13520
14160
  const result = {};
13521
14161
  const originalNames = /* @__PURE__ */ new Map();
13522
14162
  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
14163
  if (!isRecord(config)) continue;
14164
+ const { codexName, usedFallback } = normalizeCodexMcpServerName(name);
14165
+ 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
14166
  const converted = {};
13530
14167
  for (const [key, value] of Object.entries(config)) {
13531
14168
  if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
@@ -13534,12 +14171,12 @@ function convertToCodexFormat(mcpServers) {
13534
14171
  } else if (key === "oauth" && isRecord(value)) converted[key] = mapOauthToCodex(value);
13535
14172
  else if (Object.hasOwn(RULESYNC_TO_CODEX_FIELD_MAP, key)) {
13536
14173
  const mappedKey = RULESYNC_TO_CODEX_FIELD_MAP[key];
13537
- if (mappedKey) if (isStringArray(value)) converted[mappedKey] = value;
14174
+ if (mappedKey) if (isStringArray$1(value)) converted[mappedKey] = value;
13538
14175
  else warnWithFallback(void 0, `[CodexCliMcp] Skipping invalid value type for mapped key '${key}': expected string array, got ${typeof value}`);
13539
14176
  } else converted[key] = value;
13540
14177
  }
13541
14178
  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.`);
14179
+ 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
14180
  originalNames.set(codexName, name);
13544
14181
  result[codexName] = converted;
13545
14182
  }
@@ -13606,7 +14243,7 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
13606
14243
  const rawServer = isRecord(rawMcpServers) ? rawMcpServers[serverName] : void 0;
13607
14244
  return [serverName, {
13608
14245
  ...serverConfig,
13609
- ...isRecord(rawServer) && isStringArray(rawServer.envVars) ? { envVars: rawServer.envVars } : {}
14246
+ ...isRecord(rawServer) && isStringArray$1(rawServer.envVars) ? { envVars: rawServer.envVars } : {}
13610
14247
  }];
13611
14248
  })));
13612
14249
  const filteredMcpServers = this.removeEmptyEntries(converted);
@@ -14343,11 +14980,11 @@ function applyGooseStdioFields(ext, config) {
14343
14980
  if (Array.isArray(command)) {
14344
14981
  if (typeof command[0] === "string") ext.cmd = command[0];
14345
14982
  const rest = command.slice(1).filter((c) => typeof c === "string");
14346
- const args = isStringArray(config.args) ? config.args : [];
14983
+ const args = isStringArray$1(config.args) ? config.args : [];
14347
14984
  if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
14348
14985
  } else if (typeof command === "string") {
14349
14986
  ext.cmd = command;
14350
- if (isStringArray(config.args)) ext.args = config.args;
14987
+ if (isStringArray$1(config.args)) ext.args = config.args;
14351
14988
  }
14352
14989
  if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
14353
14990
  }
@@ -14405,7 +15042,7 @@ function convertFromGooseFormat(extensions) {
14405
15042
  else if (type === "streamable_http") server.type = "http";
14406
15043
  else if (type === "stdio") server.type = "stdio";
14407
15044
  if (typeof ext.cmd === "string") server.command = ext.cmd;
14408
- if (isStringArray(ext.args)) server.args = ext.args;
15045
+ if (isStringArray$1(ext.args)) server.args = ext.args;
14409
15046
  if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
14410
15047
  if (typeof ext.uri === "string") server.url = ext.uri;
14411
15048
  if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
@@ -14425,11 +15062,11 @@ function buildGoosePluginStdioServer(config) {
14425
15062
  if (Array.isArray(command)) {
14426
15063
  if (typeof command[0] === "string") server.command = command[0];
14427
15064
  const rest = command.slice(1).filter((c) => typeof c === "string");
14428
- const args = isStringArray(config.args) ? config.args : [];
15065
+ const args = isStringArray$1(config.args) ? config.args : [];
14429
15066
  if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
14430
15067
  } else if (typeof command === "string") {
14431
15068
  server.command = command;
14432
- if (isStringArray(config.args)) server.args = config.args;
15069
+ if (isStringArray$1(config.args)) server.args = config.args;
14433
15070
  }
14434
15071
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
14435
15072
  if (typeof config.cwd === "string") server.cwd = config.cwd;
@@ -14742,7 +15379,7 @@ function resolveHermesTimeout(config) {
14742
15379
  */
14743
15380
  function copyHermesAdvancedFields(source, target) {
14744
15381
  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;
15382
+ if (typeof source.client_cert === "string" || isStringArray$1(source.client_cert)) target.client_cert = source.client_cert;
14746
15383
  if (typeof source.client_key === "string") target.client_key = source.client_key;
14747
15384
  if (typeof source.connect_timeout === "number") target.connect_timeout = source.connect_timeout;
14748
15385
  if (typeof source.supports_parallel_tool_calls === "boolean") target.supports_parallel_tool_calls = source.supports_parallel_tool_calls;
@@ -14761,8 +15398,8 @@ function copyHermesAdvancedFields(source, target) {
14761
15398
  */
14762
15399
  function buildHermesToolsBlock(config) {
14763
15400
  const tools = {};
14764
- if (isStringArray(config.enabledTools)) tools.include = config.enabledTools;
14765
- if (isStringArray(config.disabledTools)) tools.exclude = config.disabledTools;
15401
+ if (isStringArray$1(config.enabledTools)) tools.include = config.enabledTools;
15402
+ if (isStringArray$1(config.disabledTools)) tools.exclude = config.disabledTools;
14766
15403
  if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
14767
15404
  if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
14768
15405
  return tools;
@@ -14774,8 +15411,8 @@ function buildHermesToolsBlock(config) {
14774
15411
  * `promptsEnabled`/`resourcesEnabled` top-level toggles.
14775
15412
  */
14776
15413
  function applyHermesToolsBlock(hermesTools, server) {
14777
- if (isStringArray(hermesTools.include)) server.enabledTools = hermesTools.include;
14778
- if (isStringArray(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
15414
+ if (isStringArray$1(hermesTools.include)) server.enabledTools = hermesTools.include;
15415
+ if (isStringArray$1(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
14779
15416
  if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
14780
15417
  if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
14781
15418
  }
@@ -14799,11 +15436,11 @@ function convertServerToHermes(config) {
14799
15436
  if (Array.isArray(command)) {
14800
15437
  if (typeof command[0] === "string") out.command = command[0];
14801
15438
  const rest = command.slice(1).filter((c) => typeof c === "string");
14802
- const args = isStringArray(config.args) ? config.args : [];
15439
+ const args = isStringArray$1(config.args) ? config.args : [];
14803
15440
  if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
14804
15441
  } else if (typeof command === "string") {
14805
15442
  out.command = command;
14806
- if (isStringArray(config.args)) out.args = config.args;
15443
+ if (isStringArray$1(config.args)) out.args = config.args;
14807
15444
  }
14808
15445
  if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
14809
15446
  } else if (url !== void 0) {
@@ -14851,7 +15488,7 @@ function convertFromHermesFormat(mcpServers) {
14851
15488
  if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord(config)) continue;
14852
15489
  const server = {};
14853
15490
  if (typeof config.command === "string") server.command = config.command;
14854
- if (isStringArray(config.args)) server.args = config.args;
15491
+ if (isStringArray$1(config.args)) server.args = config.args;
14855
15492
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
14856
15493
  if (typeof config.url === "string") server.url = config.url;
14857
15494
  if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
@@ -17176,7 +17813,11 @@ const VibePermissionsOverrideSchema = zod_mini.z.looseObject({ permission: zod_m
17176
17813
  */
17177
17814
  const CursorPermissionsOverrideSchema = zod_mini.z.looseObject({
17178
17815
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17179
- approvalMode: zod_mini.z.optional(zod_mini.z.string()),
17816
+ approvalMode: zod_mini.z.optional(zod_mini.z.enum([
17817
+ "allowlist",
17818
+ "auto-review",
17819
+ "unrestricted"
17820
+ ])),
17180
17821
  sandbox: zod_mini.z.optional(zod_mini.z.looseObject({}))
17181
17822
  });
17182
17823
  /**
@@ -17264,7 +17905,11 @@ const FactorydroidPermissionsOverrideSchema = zod_mini.z.looseObject({
17264
17905
  */
17265
17906
  const WarpPermissionsOverrideSchema = zod_mini.z.looseObject({
17266
17907
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17267
- agent_mode_coding_permissions: zod_mini.z.optional(zod_mini.z.string()),
17908
+ agent_mode_coding_permissions: zod_mini.z.optional(zod_mini.z.enum([
17909
+ "always_ask_before_reading",
17910
+ "always_allow_reading",
17911
+ "allow_reading_specific_files"
17912
+ ])),
17268
17913
  agent_mode_coding_file_read_allowlist: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
17269
17914
  agent_mode_execute_readonly_commands: zod_mini.z.optional(zod_mini.z.boolean())
17270
17915
  });
@@ -17310,7 +17955,11 @@ const JuniePermissionsOverrideSchema = zod_mini.z.looseObject({
17310
17955
  */
17311
17956
  const TaktPermissionsOverrideSchema = zod_mini.z.looseObject({
17312
17957
  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())),
17958
+ step_permission_overrides: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.enum([
17959
+ "readonly",
17960
+ "edit",
17961
+ "full"
17962
+ ]))),
17314
17963
  provider_options: zod_mini.z.optional(zod_mini.z.looseObject({}))
17315
17964
  });
17316
17965
  /**
@@ -17340,9 +17989,15 @@ const AmpPermissionsOverrideSchema = zod_mini.z.looseObject({
17340
17989
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17341
17990
  permissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({
17342
17991
  tool: zod_mini.z.string(),
17343
- action: zod_mini.z.string()
17992
+ action: zod_mini.z.enum([
17993
+ "allow",
17994
+ "ask",
17995
+ "reject",
17996
+ "delegate"
17997
+ ]),
17998
+ context: zod_mini.z.optional(zod_mini.z.enum(["thread", "subagent"]))
17344
17999
  }))),
17345
- mcpPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({}))),
18000
+ mcpPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({ action: zod_mini.z.enum(["allow", "reject"]) }))),
17346
18001
  guardedFiles: zod_mini.z.optional(zod_mini.z.looseObject({ allowlist: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())) })),
17347
18002
  dangerouslyAllowAll: zod_mini.z.optional(zod_mini.z.boolean())
17348
18003
  });
@@ -17367,7 +18022,12 @@ const AmpPermissionsOverrideSchema = zod_mini.z.looseObject({
17367
18022
  */
17368
18023
  const AntigravityCliPermissionsOverrideSchema = zod_mini.z.looseObject({
17369
18024
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17370
- toolPermission: zod_mini.z.optional(zod_mini.z.string()),
18025
+ toolPermission: zod_mini.z.optional(zod_mini.z.enum([
18026
+ "request-review",
18027
+ "proceed-in-sandbox",
18028
+ "always-proceed",
18029
+ "strict"
18030
+ ])),
17371
18031
  enableTerminalSandbox: zod_mini.z.optional(zod_mini.z.boolean())
17372
18032
  });
17373
18033
  /**
@@ -17396,7 +18056,14 @@ const AugmentcodePermissionsOverrideSchema = zod_mini.z.looseObject({
17396
18056
  permission: zod_mini.z.optional(ToolScopedPermissionSchema),
17397
18057
  toolPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({
17398
18058
  toolName: zod_mini.z.string(),
17399
- permission: zod_mini.z.looseObject({ type: zod_mini.z.string() })
18059
+ eventType: zod_mini.z.optional(zod_mini.z.enum(["tool-call", "tool-response"])),
18060
+ permission: zod_mini.z.looseObject({ type: zod_mini.z.enum([
18061
+ "allow",
18062
+ "deny",
18063
+ "ask-user",
18064
+ "webhook-policy",
18065
+ "script-policy"
18066
+ ]) })
17400
18067
  })))
17401
18068
  });
17402
18069
  /**
@@ -17486,12 +18153,20 @@ const CodexApprovalsReviewerSchema = zod_mini.z.enum([
17486
18153
  * `[permissions.rulesync]` profile may extend. Codex ships three built-in
17487
18154
  * profiles (`:read-only`, `:workspace`, `:danger-full-access`; the leading
17488
18155
  * 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.
18156
+ * `:danger-full-access` at config load time, so only these two are valid
18157
+ * `extends` parents. The value list is exported so the Codex CLI translator
18158
+ * derives its import-side baseline check from the same source.
17492
18159
  * @see https://learn.chatgpt.com/docs/permissions
17493
18160
  */
17494
- const CODEX_BASE_PERMISSION_PROFILES = [":read-only", ":workspace"];
18161
+ const CODEX_EXTENDABLE_BASELINE_PROFILES = [":read-only", ":workspace"];
18162
+ /**
18163
+ * All accepted `codexcli.base_permission_profile` values. `:danger-full-access`
18164
+ * cannot be an `extends` parent, so selecting it makes rulesync emit
18165
+ * `default_permissions = ":danger-full-access"` directly and skip the managed
18166
+ * `[permissions.rulesync]` profile entirely (there is no sandbox for
18167
+ * filesystem/network rules to refine in that mode).
18168
+ */
18169
+ const CODEX_BASE_PERMISSION_PROFILES = [...CODEX_EXTENDABLE_BASELINE_PROFILES, ":danger-full-access"];
17495
18170
  const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_PROFILES);
17496
18171
  /**
17497
18172
  * Codex CLI-scoped permission override.
@@ -17502,10 +18177,14 @@ const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_P
17502
18177
  * override whose fields are written verbatim as top-level `.codex/config.toml`
17503
18178
  * keys (the override wins per key; existing sibling keys the user set directly
17504
18179
  * 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.
18180
+ * - `base_permission_profile` — the built-in baseline profile (`:read-only` |
18181
+ * `:workspace` | `:danger-full-access`). Unlike the other keys it is not a
18182
+ * top-level config key: the extendable baselines are emitted as the managed
18183
+ * `[permissions.rulesync]` profile's `extends` value, while
18184
+ * `:danger-full-access` (which Codex rejects as an `extends` parent) is
18185
+ * selected directly via `default_permissions` and skips the managed profile
18186
+ * entirely — canonical filesystem/network rules are ignored in that mode.
18187
+ * Defaults to `:workspace` when unspecified.
17509
18188
  * - `approval_policy` — `untrusted` | `on-request` (legacy alias `on-failure`) |
17510
18189
  * `never`, or a `{ granular = { … } }` table (kept verbatim; the granular
17511
18190
  * schema has required fields that are brittle to model as typed keys).
@@ -17526,13 +18205,14 @@ const CodexBasePermissionProfileSchema = zod_mini.z.enum(CODEX_BASE_PERMISSION_P
17526
18205
  * Defaults to `auto_review` when neither the override nor the existing
17527
18206
  * config sets it.
17528
18207
  * - `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
18208
+ * emits the default `.git` carve-out (`".git/**" = "write"`). Codex's
17531
18209
  * `:workspace` baseline makes `.git` read-only, which denies basic git
17532
18210
  * 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.
18211
+ * logs; everyday commands like `git remote add` or `git push -u` write to
18212
+ * `.git/config`), so the carve-out is emitted by default. Defaults to
18213
+ * `true`; only an explicit `false` suppresses it. Like
18214
+ * `base_permission_profile` it is consumed by the profile builder, not
18215
+ * written as a top-level config key.
17536
18216
  *
17537
18217
  * Two surfaces are deliberately NOT authorable here so the override can never
17538
18218
  * clobber a feature-owned key: `mcp_servers.*` per-MCP gating is owned by the
@@ -17609,6 +18289,7 @@ const PermissionsConfigSchema = zod_mini.z.looseObject({
17609
18289
  kiro: zod_mini.z.optional(KiroPermissionsOverrideSchema),
17610
18290
  codexcli: zod_mini.z.optional(CodexcliPermissionsOverrideSchema),
17611
18291
  "antigravity-ide": zod_mini.z.optional(CanonicalPermissionsOverrideSchema),
18292
+ copilot: zod_mini.z.optional(CanonicalPermissionsOverrideSchema),
17612
18293
  devin: zod_mini.z.optional(CanonicalPermissionsOverrideSchema),
17613
18294
  goose: zod_mini.z.optional(CanonicalPermissionsOverrideSchema),
17614
18295
  grokcli: zod_mini.z.optional(CanonicalPermissionsOverrideSchema),
@@ -19529,15 +20210,13 @@ const RULESYNC_PROFILE_NAME = "rulesync";
19529
20210
  const CODEX_WORKSPACE_ROOTS_KEY = ":workspace_roots";
19530
20211
  const CODEX_WORKSPACE_BASELINE = ":workspace";
19531
20212
  const CODEX_READ_ONLY_BASELINE = ":read-only";
19532
- const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_BASE_PERMISSION_PROFILES);
20213
+ const CODEX_DANGER_FULL_ACCESS_BASELINE = ":danger-full-access";
20214
+ const CODEX_EXTENDABLE_BASELINES = new Set(CODEX_EXTENDABLE_BASELINE_PROFILES);
19533
20215
  const CODEX_DEFAULT_APPROVAL_POLICY = "on-request";
19534
20216
  const CODEX_DEFAULT_APPROVALS_REVIEWER = "auto_review";
19535
20217
  const CODEX_GLOB_SCAN_MAX_DEPTH = 8;
19536
20218
  const CODEX_MINIMAL_KEY = ":minimal";
19537
- const CODEX_GIT_WRITE_RULES = {
19538
- ".git/**": "write",
19539
- ".git/config": "read"
19540
- };
20219
+ const CODEX_GIT_WRITE_RULES = { ".git/**": "write" };
19541
20220
  const GLOBAL_WILDCARD_DOMAIN = "*";
19542
20221
  var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19543
20222
  static getSettablePaths(_options = {}) {
@@ -19565,8 +20244,40 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19565
20244
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
19566
20245
  const existingContent = await readFileContentOrNull(filePath) ?? "";
19567
20246
  const existing = toMutableTable(smol_toml.parse(existingContent || smol_toml.stringify({})));
20247
+ const canonicalConfig = rulesyncPermissions.getJson();
20248
+ if (canonicalConfig.codexcli?.base_permission_profile === CODEX_DANGER_FULL_ACCESS_BASELINE) {
20249
+ const ignoredCategories = Object.entries(canonicalConfig.permission).filter(([category, rules]) => category !== "bash" && Object.keys(rules).length > 0).map(([category]) => category);
20250
+ 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.`);
20251
+ const permissionsTable = toMutableTable(existing.permissions);
20252
+ if (permissionsTable[RULESYNC_PROFILE_NAME] !== void 0) {
20253
+ 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.`);
20254
+ delete permissionsTable[RULESYNC_PROFILE_NAME];
20255
+ }
20256
+ const overridePatch = computeCodexcliOverridePatch({
20257
+ existing,
20258
+ override: canonicalConfig.codexcli,
20259
+ logger
20260
+ });
20261
+ return new CodexcliPermissions({
20262
+ outputRoot,
20263
+ relativeDirPath: paths.relativeDirPath,
20264
+ relativeFilePath: paths.relativeFilePath,
20265
+ fileContent: applySharedConfigPatch({
20266
+ fileKey: sharedConfigFileKey(paths),
20267
+ feature: "permissions",
20268
+ existingContent,
20269
+ patch: {
20270
+ permissions: Object.keys(permissionsTable).length > 0 ? permissionsTable : void 0,
20271
+ default_permissions: CODEX_DANGER_FULL_ACCESS_BASELINE,
20272
+ ...overridePatch
20273
+ },
20274
+ filePath
20275
+ }),
20276
+ validate
20277
+ });
20278
+ }
19568
20279
  const newProfile = convertRulesyncToCodexProfile({
19569
- config: rulesyncPermissions.getJson(),
20280
+ config: canonicalConfig,
19570
20281
  logger
19571
20282
  });
19572
20283
  const permissionsTable = toMutableTable(existing.permissions);
@@ -19627,6 +20338,7 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
19627
20338
  });
19628
20339
  const override = extractCodexcliOverride(table);
19629
20340
  if (typeof profile?.extends === "string" && CODEX_EXTENDABLE_BASELINES.has(profile.extends)) override.base_permission_profile = profile.extends;
20341
+ if (defaultProfile === CODEX_DANGER_FULL_ACCESS_BASELINE) override.base_permission_profile = CODEX_DANGER_FULL_ACCESS_BASELINE;
19630
20342
  const result = Object.keys(override).length > 0 ? {
19631
20343
  ...config,
19632
20344
  codexcli: override
@@ -19682,16 +20394,10 @@ function convertRulesyncToCodexProfile({ config, logger }) {
19682
20394
  const filesystem = { [CODEX_MINIMAL_KEY]: "read" };
19683
20395
  const workspaceRootFilesystem = {};
19684
20396
  const domains = {};
20397
+ const filesystemCategoryRules = {};
19685
20398
  for (const [toolName, rules] of Object.entries(config.permission)) {
19686
20399
  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
- });
20400
+ filesystemCategoryRules[toolName] = rules;
19695
20401
  continue;
19696
20402
  }
19697
20403
  if (toolName === "webfetch") {
@@ -19704,6 +20410,16 @@ function convertRulesyncToCodexProfile({ config, logger }) {
19704
20410
  }
19705
20411
  logger?.warn(`Codex CLI permissions support only read/edit/write/webfetch categories. Skipping: ${toolName}`);
19706
20412
  }
20413
+ for (const [pattern, access] of mergeFilesystemCategoryRules({
20414
+ categoryRules: filesystemCategoryRules,
20415
+ logger
20416
+ })) addFilesystemRule({
20417
+ filesystem,
20418
+ workspaceRootFilesystem,
20419
+ pattern,
20420
+ access,
20421
+ logger
20422
+ });
19707
20423
  applyDefaultGitWriteRules({
19708
20424
  config,
19709
20425
  filesystem,
@@ -19984,6 +20700,67 @@ function mapReadAction(action) {
19984
20700
  function mapWriteAction(action) {
19985
20701
  return action === "allow" ? "write" : "deny";
19986
20702
  }
20703
+ /**
20704
+ * Merge the canonical read/edit/write category rules into one Codex access
20705
+ * level per path pattern (Codex models a single `deny` < `read` < `write`
20706
+ * level, with no `ask`).
20707
+ *
20708
+ * - `edit` and `write` collapse onto Codex's write side; when both carry the
20709
+ * same pattern, the more restrictive action wins (`deny` > `ask` > `allow`).
20710
+ * - `read: allow` + write-side `allow` → `"write"`.
20711
+ * - `read: allow` + write-side non-allow → `"read"` (readable but not
20712
+ * writable — exactly what Codex's `"read"` level expresses).
20713
+ * - `read` non-allow → `"deny"` regardless of the write side; a contradictory
20714
+ * write-side `allow` (unreadable but writable is not expressible in Codex)
20715
+ * is warned about.
20716
+ * - Single-category patterns keep the existing mapReadAction/mapWriteAction
20717
+ * mappings.
20718
+ *
20719
+ * Iteration order is read → edit → write with first-seen pattern order, so
20720
+ * the emitted table is stable regardless of the authored category order.
20721
+ * Note the merge is one-way: `"{path}" = "read"` imports back as
20722
+ * `read: allow` only (the explicit write-side deny is implied by Codex's
20723
+ * access level and not re-materialized).
20724
+ */
20725
+ function mergeFilesystemCategoryRules({ categoryRules, logger }) {
20726
+ const readRules = categoryRules.read ?? {};
20727
+ const writeSideRestrictiveness = {
20728
+ deny: 2,
20729
+ ask: 1,
20730
+ allow: 0
20731
+ };
20732
+ const writeSideRules = {};
20733
+ for (const category of ["edit", "write"]) for (const [pattern, action] of Object.entries(categoryRules[category] ?? {})) {
20734
+ const existing = writeSideRules[pattern];
20735
+ if (existing === void 0 || writeSideRestrictiveness[action] > writeSideRestrictiveness[existing]) writeSideRules[pattern] = action;
20736
+ }
20737
+ const patterns = [];
20738
+ const seen = /* @__PURE__ */ new Set();
20739
+ for (const pattern of [...Object.keys(readRules), ...Object.keys(writeSideRules)]) if (!seen.has(pattern)) {
20740
+ seen.add(pattern);
20741
+ patterns.push(pattern);
20742
+ }
20743
+ const merged = [];
20744
+ for (const pattern of patterns) {
20745
+ const readAction = readRules[pattern];
20746
+ const writeAction = writeSideRules[pattern];
20747
+ if (readAction === void 0) {
20748
+ merged.push([pattern, mapWriteAction(writeAction)]);
20749
+ continue;
20750
+ }
20751
+ if (writeAction === void 0) {
20752
+ merged.push([pattern, mapReadAction(readAction)]);
20753
+ continue;
20754
+ }
20755
+ if (readAction === "allow") {
20756
+ merged.push([pattern, writeAction === "allow" ? "write" : "read"]);
20757
+ continue;
20758
+ }
20759
+ 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".`);
20760
+ merged.push([pattern, "deny"]);
20761
+ }
20762
+ return merged;
20763
+ }
19987
20764
  function buildCodexBashRulesContent(config) {
19988
20765
  const bashRules = config.permission.bash ?? {};
19989
20766
  const entries = Object.entries(bashRules);
@@ -20016,6 +20793,135 @@ function mapBashActionToDecision(action) {
20016
20793
  return "forbidden";
20017
20794
  }
20018
20795
  //#endregion
20796
+ //#region src/features/permissions/copilot-permissions.ts
20797
+ /**
20798
+ * The flat, dotted VS Code setting key this adapter manages. VS Code stores
20799
+ * settings with dotted keys flat at the document top level, so this is a single
20800
+ * literal key — not a nested `chat.tools.terminal` path.
20801
+ * @see https://code.visualstudio.com/docs/agents/approvals
20802
+ */
20803
+ const AUTO_APPROVE_KEY = "chat.tools.terminal.autoApprove";
20804
+ /**
20805
+ * The canonical permission category this adapter maps. Only shell/terminal
20806
+ * commands (`bash`) have a clean, high-fidelity representation in VS Code's
20807
+ * `chat.tools.terminal.autoApprove` map; other categories (read/edit/webfetch/
20808
+ * …) have no terminal-command equivalent and are intentionally not mapped.
20809
+ */
20810
+ const TERMINAL_CATEGORY = "bash";
20811
+ function asAutoApproveMap(value) {
20812
+ if (!isPlainObject$1(value)) return {};
20813
+ const result = {};
20814
+ for (const [pattern, flag] of Object.entries(value)) if (typeof flag === "boolean") result[pattern] = flag;
20815
+ return result;
20816
+ }
20817
+ /**
20818
+ * Permissions generator for GitHub Copilot Chat in VS Code.
20819
+ *
20820
+ * VS Code has no standalone, environment-agnostic Copilot policy file (like
20821
+ * `.claude/settings.json`); project-level terminal auto-approvals are managed
20822
+ * through the workspace `chat.tools.terminal.autoApprove` map inside
20823
+ * `.vscode/settings.json`. That file is a general workspace settings file with
20824
+ * many unrelated keys, so reads and writes merge into the existing JSON
20825
+ * (touching only the one managed key) and the file is never deleted.
20826
+ *
20827
+ * Scope is deliberately limited to `chat.tools.terminal.autoApprove` — the one
20828
+ * clean, non-lossy mapping. The canonical `bash` category's per-pattern rules
20829
+ * map as: `allow` → `true` (auto-approve), `deny` → `false` (never approve),
20830
+ * and `ask` → the entry is OMITTED (VS Code then falls through to its default
20831
+ * in-chat approval prompt, i.e. "ask"). Only project scope is modeled: VS Code's
20832
+ * user-scope settings.json lives at a platform-dependent path outside rulesync's
20833
+ * home-relative global model.
20834
+ */
20835
+ var CopilotPermissions = class CopilotPermissions extends ToolPermissions {
20836
+ constructor(params) {
20837
+ super({
20838
+ ...params,
20839
+ fileContent: params.fileContent ?? "{}"
20840
+ });
20841
+ }
20842
+ /**
20843
+ * `.vscode/settings.json` is a user-managed workspace file with unrelated
20844
+ * settings, so it must not be deleted.
20845
+ */
20846
+ isDeletable() {
20847
+ return false;
20848
+ }
20849
+ static getSettablePaths(_options = {}) {
20850
+ return {
20851
+ relativeDirPath: COPILOT_MCP_DIR,
20852
+ relativeFilePath: COPILOT_VSCODE_SETTINGS_FILE_NAME
20853
+ };
20854
+ }
20855
+ static async fromFile({ outputRoot = process.cwd(), validate = true }) {
20856
+ const paths = CopilotPermissions.getSettablePaths();
20857
+ const fileContent = await readFileContentOrNull((0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
20858
+ return new CopilotPermissions({
20859
+ outputRoot,
20860
+ relativeDirPath: paths.relativeDirPath,
20861
+ relativeFilePath: paths.relativeFilePath,
20862
+ fileContent,
20863
+ validate
20864
+ });
20865
+ }
20866
+ static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions }) {
20867
+ const paths = CopilotPermissions.getSettablePaths();
20868
+ const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
20869
+ const existingContent = await readFileContentOrNull(filePath) ?? "{}";
20870
+ const rules = rulesyncPermissions.getJson().permission[TERMINAL_CATEGORY] ?? {};
20871
+ const autoApprove = {};
20872
+ for (const [pattern, action] of Object.entries(rules)) if (action === "allow") autoApprove[pattern] = true;
20873
+ else if (action === "deny") autoApprove[pattern] = false;
20874
+ const patchValue = Object.keys(autoApprove).length > 0 ? autoApprove : void 0;
20875
+ return new CopilotPermissions({
20876
+ outputRoot,
20877
+ relativeDirPath: paths.relativeDirPath,
20878
+ relativeFilePath: paths.relativeFilePath,
20879
+ fileContent: applySharedConfigPatch({
20880
+ fileKey: sharedConfigFileKey(paths),
20881
+ feature: "permissions",
20882
+ existingContent,
20883
+ patch: { [AUTO_APPROVE_KEY]: patchValue },
20884
+ filePath
20885
+ }),
20886
+ validate: true
20887
+ });
20888
+ }
20889
+ toRulesyncPermissions() {
20890
+ let settings;
20891
+ try {
20892
+ settings = parseSharedConfig({
20893
+ format: "jsonc",
20894
+ fileContent: this.getFileContent() || "{}",
20895
+ filePath: (0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath()),
20896
+ invalidRootPolicy: "error",
20897
+ jsoncParseErrors: "error"
20898
+ });
20899
+ } catch (error) {
20900
+ throw new Error(`Failed to parse Copilot VS Code settings in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
20901
+ }
20902
+ const autoApprove = asAutoApproveMap(settings[AUTO_APPROVE_KEY]);
20903
+ const rules = {};
20904
+ for (const [pattern, flag] of Object.entries(autoApprove)) rules[pattern] = flag ? "allow" : "deny";
20905
+ const permission = Object.keys(rules).length > 0 ? { [TERMINAL_CATEGORY]: rules } : {};
20906
+ return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission }, null, 2) });
20907
+ }
20908
+ validate() {
20909
+ return {
20910
+ success: true,
20911
+ error: null
20912
+ };
20913
+ }
20914
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
20915
+ return new CopilotPermissions({
20916
+ outputRoot,
20917
+ relativeDirPath,
20918
+ relativeFilePath,
20919
+ fileContent: "{}",
20920
+ validate: false
20921
+ });
20922
+ }
20923
+ };
20924
+ //#endregion
20019
20925
  //#region src/features/permissions/cursor-permissions.ts
20020
20926
  /**
20021
20927
  * Mapping from rulesync canonical tool category names (lowercase) to Cursor CLI
@@ -20935,7 +21841,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
20935
21841
  function convertGoosePermissionConfigToRulesync(userPermission) {
20936
21842
  const permission = {};
20937
21843
  for (const key of GOOSE_PERMISSION_LIST_KEYS) {
20938
- const toolNames = isStringArray(userPermission[key]) ? userPermission[key] : [];
21844
+ const toolNames = isStringArray$1(userPermission[key]) ? userPermission[key] : [];
20939
21845
  const action = GOOSE_LIST_TO_ACTION[key];
20940
21846
  for (const toolName of toolNames) {
20941
21847
  const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
@@ -21175,7 +22081,7 @@ const ACTION_RANK = {
21175
22081
  * (mirrors the Cursor adapter's preservation of unmanaged types).
21176
22082
  */
21177
22083
  function unmanagedEntries(existingPermission, key) {
21178
- return (isStringArray(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
22084
+ return (isStringArray$1(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
21179
22085
  }
21180
22086
  /**
21181
22087
  * Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
@@ -21216,9 +22122,9 @@ function buildGrokPermissionArrays(config, existingPermission, logger) {
21216
22122
  * arrays resolves to the strictest action.
21217
22123
  */
21218
22124
  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;
22125
+ const allow = isStringArray$1(permission.allow) ? permission.allow : void 0;
22126
+ const deny = isStringArray$1(permission.deny) ? permission.deny : void 0;
22127
+ const ask = isStringArray$1(permission.ask) ? permission.ask : void 0;
21222
22128
  if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) === 0) return null;
21223
22129
  const result = {};
21224
22130
  const apply = (entries, action) => {
@@ -22999,7 +23905,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
22999
23905
  permission[category][CATCH_ALL_PATTERN$1] = value;
23000
23906
  }
23001
23907
  }
23002
- if (isStringArray(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
23908
+ if (isStringArray$1(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
23003
23909
  permission.read ??= {};
23004
23910
  permission.read[path] = "allow";
23005
23911
  }
@@ -23559,8 +24465,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
23559
24465
  const agents = isRecord(settings.agents) ? settings.agents : {};
23560
24466
  const profiles = isRecord(agents.profiles) ? agents.profiles : {};
23561
24467
  const config = convertWarpToRulesyncPermissions({
23562
- allow: isStringArray(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
23563
- deny: isStringArray(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
24468
+ allow: isStringArray$1(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
24469
+ deny: isStringArray$1(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
23564
24470
  });
23565
24471
  const warpOverride = {};
23566
24472
  for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
@@ -23906,6 +24812,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
23906
24812
  supportsImport: true
23907
24813
  }
23908
24814
  }],
24815
+ ["copilot", {
24816
+ class: CopilotPermissions,
24817
+ meta: {
24818
+ supportsProject: true,
24819
+ supportsGlobal: false,
24820
+ supportsImport: true
24821
+ }
24822
+ }],
23909
24823
  ["cursor", {
23910
24824
  class: CursorPermissions,
23911
24825
  meta: {
@@ -24140,9 +25054,6 @@ var PermissionsProcessor = class extends FeatureProcessor {
24140
25054
  }
24141
25055
  };
24142
25056
  //#endregion
24143
- //#region src/constants/general.ts
24144
- const SKILL_FILE_NAME = "SKILL.md";
24145
- //#endregion
24146
25057
  //#region src/types/ai-dir.ts
24147
25058
  var AiDir = class {
24148
25059
  /**
@@ -24343,10 +25254,10 @@ var ToolSkill = class extends AiDir {
24343
25254
  const settablePaths = getSettablePaths({ global });
24344
25255
  const actualRelativeDirPath = relativeDirPath ?? settablePaths.relativeDirPath;
24345
25256
  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}`);
25257
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25258
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24348
25259
  const { frontmatter, body: content } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24349
- const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME);
25260
+ const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME$1);
24350
25261
  return {
24351
25262
  outputRoot,
24352
25263
  relativeDirPath: actualRelativeDirPath,
@@ -24388,7 +25299,7 @@ var SimulatedSkill = class extends ToolSkill {
24388
25299
  relativeDirPath,
24389
25300
  dirName,
24390
25301
  mainFile: {
24391
- name: SKILL_FILE_NAME,
25302
+ name: SKILL_FILE_NAME$1,
24392
25303
  body,
24393
25304
  frontmatter: { ...frontmatter }
24394
25305
  },
@@ -24446,12 +25357,12 @@ var SimulatedSkill = class extends ToolSkill {
24446
25357
  const settablePaths = this.getSettablePaths();
24447
25358
  const actualRelativeDirPath = relativeDirPath ?? settablePaths.relativeDirPath;
24448
25359
  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}`);
25360
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25361
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24451
25362
  const { frontmatter, body: content } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24452
25363
  const result = SimulatedSkillFrontmatterSchema.safeParse(frontmatter);
24453
25364
  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);
25365
+ const otherFiles = await this.collectOtherFiles(outputRoot, actualRelativeDirPath, dirName, SKILL_FILE_NAME$1);
24455
25366
  return {
24456
25367
  outputRoot,
24457
25368
  relativeDirPath: actualRelativeDirPath,
@@ -24661,7 +25572,7 @@ var RulesyncSkill = class RulesyncSkill extends AiDir {
24661
25572
  relativeDirPath,
24662
25573
  dirName,
24663
25574
  mainFile: {
24664
- name: SKILL_FILE_NAME,
25575
+ name: SKILL_FILE_NAME$1,
24665
25576
  body,
24666
25577
  frontmatter: { ...frontmatter }
24667
25578
  },
@@ -24696,13 +25607,13 @@ var RulesyncSkill = class RulesyncSkill extends AiDir {
24696
25607
  }
24697
25608
  static async fromDir({ outputRoot = process.cwd(), relativeDirPath = RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName, global = false }) {
24698
25609
  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}`);
25610
+ const skillFilePath = (0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1);
25611
+ if (!await fileExists(skillFilePath)) throw new Error(`${SKILL_FILE_NAME$1} not found in ${skillDirPath}`);
24701
25612
  const { frontmatter, body: content, hasFrontmatter } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
24702
25613
  if (!hasFrontmatter) throw new Error(`Missing frontmatter in ${skillFilePath}. Rulesync files must begin with a YAML frontmatter block delimited by '---'.`);
24703
25614
  const result = RulesyncSkillFrontmatterSchema.safeParse(frontmatter);
24704
25615
  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);
25616
+ const otherFiles = await this.collectOtherFiles(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME$1);
24706
25617
  return new RulesyncSkill({
24707
25618
  outputRoot,
24708
25619
  relativeDirPath,
@@ -24742,7 +25653,7 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24742
25653
  relativeDirPath,
24743
25654
  dirName,
24744
25655
  mainFile: {
24745
- name: SKILL_FILE_NAME,
25656
+ name: SKILL_FILE_NAME$1,
24746
25657
  body,
24747
25658
  frontmatter: { ...frontmatter }
24748
25659
  },
@@ -24769,7 +25680,7 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24769
25680
  validate() {
24770
25681
  if (!this.mainFile) return {
24771
25682
  success: false,
24772
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
25683
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
24773
25684
  };
24774
25685
  const result = RovodevSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
24775
25686
  if (!result.success) return {
@@ -24845,10 +25756,10 @@ var RovodevSkill = class RovodevSkill extends ToolSkill {
24845
25756
  const result = RovodevSkillFrontmatterSchema.safeParse(loaded.frontmatter);
24846
25757
  if (!result.success) {
24847
25758
  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)}`);
25759
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
24849
25760
  }
24850
25761
  if (result.data.name !== loaded.dirName) {
24851
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
25762
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
24852
25763
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
24853
25764
  }
24854
25765
  return new RovodevSkill({
@@ -25013,7 +25924,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25013
25924
  relativeDirPath,
25014
25925
  dirName,
25015
25926
  mainFile: {
25016
- name: SKILL_FILE_NAME,
25927
+ name: SKILL_FILE_NAME$1,
25017
25928
  body,
25018
25929
  frontmatter: { ...frontmatter }
25019
25930
  },
@@ -25037,7 +25948,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25037
25948
  validate() {
25038
25949
  if (!this.mainFile) return {
25039
25950
  success: false,
25040
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
25951
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25041
25952
  };
25042
25953
  const result = AgentsSkillsSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25043
25954
  if (!result.success) return {
@@ -25109,7 +26020,7 @@ var AgentsSkillsSkill = class AgentsSkillsSkill extends ToolSkill {
25109
26020
  const result = AgentsSkillsSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25110
26021
  if (!result.success) {
25111
26022
  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)}`);
26023
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25113
26024
  }
25114
26025
  return new this({
25115
26026
  outputRoot: loaded.outputRoot,
@@ -25166,7 +26077,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25166
26077
  relativeDirPath,
25167
26078
  dirName,
25168
26079
  mainFile: {
25169
- name: SKILL_FILE_NAME,
26080
+ name: SKILL_FILE_NAME$1,
25170
26081
  body,
25171
26082
  frontmatter: { ...frontmatter }
25172
26083
  },
@@ -25191,7 +26102,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25191
26102
  validate() {
25192
26103
  if (!this.mainFile) return {
25193
26104
  success: false,
25194
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26105
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25195
26106
  };
25196
26107
  const result = AiassistantSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25197
26108
  if (!result.success) return {
@@ -25251,7 +26162,7 @@ var AiassistantSkill = class AiassistantSkill extends ToolSkill {
25251
26162
  const result = AiassistantSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25252
26163
  if (!result.success) {
25253
26164
  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)}`);
26165
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25255
26166
  }
25256
26167
  return new AiassistantSkill({
25257
26168
  outputRoot: loaded.outputRoot,
@@ -25302,7 +26213,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25302
26213
  relativeDirPath,
25303
26214
  dirName,
25304
26215
  mainFile: {
25305
- name: SKILL_FILE_NAME,
26216
+ name: SKILL_FILE_NAME$1,
25306
26217
  body,
25307
26218
  frontmatter: { ...frontmatter }
25308
26219
  },
@@ -25326,7 +26237,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25326
26237
  validate() {
25327
26238
  if (!this.mainFile) return {
25328
26239
  success: false,
25329
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26240
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25330
26241
  };
25331
26242
  const result = AmpSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25332
26243
  if (!result.success) return {
@@ -25386,7 +26297,7 @@ var AmpSkill = class AmpSkill extends ToolSkill {
25386
26297
  const result = AmpSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25387
26298
  if (!result.success) {
25388
26299
  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)}`);
26300
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25390
26301
  }
25391
26302
  return new AmpSkill({
25392
26303
  outputRoot: loaded.outputRoot,
@@ -25442,7 +26353,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25442
26353
  relativeDirPath,
25443
26354
  dirName,
25444
26355
  mainFile: {
25445
- name: SKILL_FILE_NAME,
26356
+ name: SKILL_FILE_NAME$1,
25446
26357
  body,
25447
26358
  frontmatter: { ...frontmatter }
25448
26359
  },
@@ -25475,7 +26386,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25475
26386
  validate() {
25476
26387
  if (this.mainFile === void 0) return {
25477
26388
  success: false,
25478
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26389
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25479
26390
  };
25480
26391
  const result = AntigravitySkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25481
26392
  if (!result.success) return {
@@ -25535,7 +26446,7 @@ var AntigravitySharedSkill = class extends ToolSkill {
25535
26446
  const result = AntigravitySkillFrontmatterSchema.safeParse(loaded.frontmatter);
25536
26447
  if (!result.success) {
25537
26448
  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)}`);
26449
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25539
26450
  }
25540
26451
  return new this({
25541
26452
  outputRoot: loaded.outputRoot,
@@ -25619,7 +26530,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25619
26530
  relativeDirPath,
25620
26531
  dirName,
25621
26532
  mainFile: {
25622
- name: SKILL_FILE_NAME,
26533
+ name: SKILL_FILE_NAME$1,
25623
26534
  body,
25624
26535
  frontmatter: { ...frontmatter }
25625
26536
  },
@@ -25646,7 +26557,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25646
26557
  validate() {
25647
26558
  if (this.mainFile === void 0) return {
25648
26559
  success: false,
25649
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26560
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25650
26561
  };
25651
26562
  const result = AugmentcodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25652
26563
  if (!result.success) return {
@@ -25706,7 +26617,7 @@ var AugmentcodeSkill = class AugmentcodeSkill extends ToolSkill {
25706
26617
  const result = AugmentcodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25707
26618
  if (!result.success) {
25708
26619
  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)}`);
26620
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25710
26621
  }
25711
26622
  return new AugmentcodeSkill({
25712
26623
  outputRoot: loaded.outputRoot,
@@ -25847,7 +26758,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25847
26758
  relativeDirPath,
25848
26759
  dirName,
25849
26760
  mainFile: {
25850
- name: SKILL_FILE_NAME,
26761
+ name: SKILL_FILE_NAME$1,
25851
26762
  body,
25852
26763
  frontmatter: { ...frontmatter }
25853
26764
  },
@@ -25874,7 +26785,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25874
26785
  validate() {
25875
26786
  if (this.mainFile === void 0) return {
25876
26787
  success: false,
25877
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26788
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
25878
26789
  };
25879
26790
  const result = ClaudecodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
25880
26791
  if (!result.success) return {
@@ -25962,7 +26873,7 @@ var ClaudecodeSkill = class ClaudecodeSkill extends ToolSkill {
25962
26873
  const result = ClaudecodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
25963
26874
  if (!result.success) {
25964
26875
  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)}`);
26876
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
25966
26877
  }
25967
26878
  return new ClaudecodeSkill({
25968
26879
  outputRoot: loaded.outputRoot,
@@ -26008,7 +26919,7 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26008
26919
  relativeDirPath,
26009
26920
  dirName,
26010
26921
  mainFile: {
26011
- name: SKILL_FILE_NAME,
26922
+ name: SKILL_FILE_NAME$1,
26012
26923
  body,
26013
26924
  frontmatter: { ...frontmatter }
26014
26925
  },
@@ -26032,7 +26943,7 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26032
26943
  validate() {
26033
26944
  if (!this.mainFile) return {
26034
26945
  success: false,
26035
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
26946
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26036
26947
  };
26037
26948
  const result = ClineSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26038
26949
  if (!result.success) return {
@@ -26096,10 +27007,10 @@ var ClineSkill = class ClineSkill extends ToolSkill {
26096
27007
  const result = ClineSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26097
27008
  if (!result.success) {
26098
27009
  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)}`);
27010
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26100
27011
  }
26101
27012
  if (result.data.name !== loaded.dirName) {
26102
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
27013
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
26103
27014
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
26104
27015
  }
26105
27016
  return new ClineSkill({
@@ -26213,7 +27124,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26213
27124
  relativeDirPath,
26214
27125
  dirName,
26215
27126
  mainFile: {
26216
- name: SKILL_FILE_NAME,
27127
+ name: SKILL_FILE_NAME$1,
26217
27128
  body,
26218
27129
  frontmatter: { ...frontmatter }
26219
27130
  },
@@ -26237,7 +27148,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26237
27148
  validate() {
26238
27149
  if (!this.mainFile) return {
26239
27150
  success: false,
26240
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27151
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26241
27152
  };
26242
27153
  const result = CodexCliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26243
27154
  if (!result.success) return {
@@ -26315,7 +27226,7 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
26315
27226
  const result = CodexCliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26316
27227
  if (!result.success) {
26317
27228
  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)}`);
27229
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26319
27230
  }
26320
27231
  return new CodexCliSkill({
26321
27232
  outputRoot: loaded.outputRoot,
@@ -26367,7 +27278,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26367
27278
  relativeDirPath,
26368
27279
  dirName,
26369
27280
  mainFile: {
26370
- name: SKILL_FILE_NAME,
27281
+ name: SKILL_FILE_NAME$1,
26371
27282
  body,
26372
27283
  frontmatter: { ...frontmatter }
26373
27284
  },
@@ -26392,7 +27303,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26392
27303
  validate() {
26393
27304
  if (!this.mainFile) return {
26394
27305
  success: false,
26395
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27306
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26396
27307
  };
26397
27308
  const result = CopilotSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26398
27309
  if (!result.success) return {
@@ -26459,7 +27370,7 @@ var CopilotSkill = class CopilotSkill extends ToolSkill {
26459
27370
  const result = CopilotSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26460
27371
  if (!result.success) {
26461
27372
  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)}`);
27373
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26463
27374
  }
26464
27375
  return new CopilotSkill({
26465
27376
  outputRoot: loaded.outputRoot,
@@ -26513,7 +27424,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26513
27424
  relativeDirPath,
26514
27425
  dirName,
26515
27426
  mainFile: {
26516
- name: SKILL_FILE_NAME,
27427
+ name: SKILL_FILE_NAME$1,
26517
27428
  body,
26518
27429
  frontmatter: { ...frontmatter }
26519
27430
  },
@@ -26538,7 +27449,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26538
27449
  validate() {
26539
27450
  if (!this.mainFile) return {
26540
27451
  success: false,
26541
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27452
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26542
27453
  };
26543
27454
  const result = CopilotcliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26544
27455
  if (!result.success) return {
@@ -26607,7 +27518,7 @@ var CopilotcliSkill = class CopilotcliSkill extends ToolSkill {
26607
27518
  const result = CopilotcliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26608
27519
  if (!result.success) {
26609
27520
  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)}`);
27521
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26611
27522
  }
26612
27523
  return new CopilotcliSkill({
26613
27524
  outputRoot: loaded.outputRoot,
@@ -26657,7 +27568,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26657
27568
  relativeDirPath,
26658
27569
  dirName,
26659
27570
  mainFile: {
26660
- name: SKILL_FILE_NAME,
27571
+ name: SKILL_FILE_NAME$1,
26661
27572
  body,
26662
27573
  frontmatter: { ...frontmatter }
26663
27574
  },
@@ -26681,7 +27592,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26681
27592
  validate() {
26682
27593
  if (!this.mainFile) return {
26683
27594
  success: false,
26684
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27595
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26685
27596
  };
26686
27597
  const result = CursorSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26687
27598
  if (!result.success) return {
@@ -26755,7 +27666,7 @@ var CursorSkill = class CursorSkill extends ToolSkill {
26755
27666
  const result = CursorSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26756
27667
  if (!result.success) {
26757
27668
  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)}`);
27669
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26759
27670
  }
26760
27671
  return new CursorSkill({
26761
27672
  outputRoot: loaded.outputRoot,
@@ -26802,7 +27713,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26802
27713
  relativeDirPath,
26803
27714
  dirName,
26804
27715
  mainFile: {
26805
- name: SKILL_FILE_NAME,
27716
+ name: SKILL_FILE_NAME$1,
26806
27717
  body,
26807
27718
  frontmatter: { ...frontmatter }
26808
27719
  },
@@ -26826,7 +27737,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26826
27737
  validate() {
26827
27738
  if (!this.mainFile) return {
26828
27739
  success: false,
26829
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27740
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26830
27741
  };
26831
27742
  const result = DeepagentsSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26832
27743
  if (!result.success) return {
@@ -26902,7 +27813,7 @@ var DeepagentsSkill = class DeepagentsSkill extends ToolSkill {
26902
27813
  const result = DeepagentsSkillFrontmatterSchema.safeParse(loaded.frontmatter);
26903
27814
  if (!result.success) {
26904
27815
  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)}`);
27816
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
26906
27817
  }
26907
27818
  return new DeepagentsSkill({
26908
27819
  outputRoot: loaded.outputRoot,
@@ -26953,7 +27864,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
26953
27864
  relativeDirPath,
26954
27865
  dirName,
26955
27866
  mainFile: {
26956
- name: SKILL_FILE_NAME,
27867
+ name: SKILL_FILE_NAME$1,
26957
27868
  body,
26958
27869
  frontmatter: { ...frontmatter }
26959
27870
  },
@@ -26978,7 +27889,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
26978
27889
  validate() {
26979
27890
  if (!this.mainFile) return {
26980
27891
  success: false,
26981
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
27892
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
26982
27893
  };
26983
27894
  const result = DevinSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
26984
27895
  if (!result.success) return {
@@ -27030,6 +27941,18 @@ var DevinSkill = class DevinSkill extends ToolSkill {
27030
27941
  const targets = rulesyncSkill.getFrontmatter().targets;
27031
27942
  return targets.includes("*") || targets.includes("devin");
27032
27943
  }
27944
+ /**
27945
+ * Commands are emitted into this same skills tree as `<slug>/SKILL.md`
27946
+ * (see `DevinCommand`), so a directory matching a current rulesync command
27947
+ * slug is owned by the commands feature: it must not be imported as a
27948
+ * skill nor deleted as an orphan skill.
27949
+ */
27950
+ static async isDirOwned({ dirName, inputRoot }) {
27951
+ return !await rulesyncCommandSlugExists({
27952
+ inputRoot,
27953
+ dirName
27954
+ });
27955
+ }
27033
27956
  static async fromDir(params) {
27034
27957
  const loaded = await this.loadSkillDirContent({
27035
27958
  ...params,
@@ -27038,7 +27961,7 @@ var DevinSkill = class DevinSkill extends ToolSkill {
27038
27961
  const result = DevinSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27039
27962
  if (!result.success) {
27040
27963
  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)}`);
27964
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27042
27965
  }
27043
27966
  return new DevinSkill({
27044
27967
  outputRoot: loaded.outputRoot,
@@ -27090,7 +28013,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27090
28013
  relativeDirPath,
27091
28014
  dirName,
27092
28015
  mainFile: {
27093
- name: SKILL_FILE_NAME,
28016
+ name: SKILL_FILE_NAME$1,
27094
28017
  body,
27095
28018
  frontmatter: { ...frontmatter }
27096
28019
  },
@@ -27114,7 +28037,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27114
28037
  validate() {
27115
28038
  if (this.mainFile === void 0) return {
27116
28039
  success: false,
27117
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28040
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27118
28041
  };
27119
28042
  const result = FactorydroidSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27120
28043
  if (!result.success) return {
@@ -27189,7 +28112,7 @@ var FactorydroidSkill = class FactorydroidSkill extends ToolSkill {
27189
28112
  const result = FactorydroidSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27190
28113
  if (!result.success) {
27191
28114
  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)}`);
28115
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27193
28116
  }
27194
28117
  return new FactorydroidSkill({
27195
28118
  outputRoot: loaded.outputRoot,
@@ -27236,7 +28159,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27236
28159
  relativeDirPath,
27237
28160
  dirName,
27238
28161
  mainFile: {
27239
- name: SKILL_FILE_NAME,
28162
+ name: SKILL_FILE_NAME$1,
27240
28163
  body,
27241
28164
  frontmatter: { ...frontmatter }
27242
28165
  },
@@ -27261,7 +28184,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27261
28184
  validate() {
27262
28185
  if (!this.mainFile) return {
27263
28186
  success: false,
27264
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28187
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27265
28188
  };
27266
28189
  const result = GooseSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27267
28190
  if (!result.success) return {
@@ -27321,7 +28244,7 @@ var GooseSkill = class GooseSkill extends ToolSkill {
27321
28244
  const result = GooseSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27322
28245
  if (!result.success) {
27323
28246
  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)}`);
28247
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27325
28248
  }
27326
28249
  return new GooseSkill({
27327
28250
  outputRoot: loaded.outputRoot,
@@ -27374,7 +28297,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27374
28297
  relativeDirPath,
27375
28298
  dirName,
27376
28299
  mainFile: {
27377
- name: SKILL_FILE_NAME,
28300
+ name: SKILL_FILE_NAME$1,
27378
28301
  body,
27379
28302
  frontmatter: { ...frontmatter }
27380
28303
  },
@@ -27398,7 +28321,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27398
28321
  validate() {
27399
28322
  if (this.mainFile === void 0) return {
27400
28323
  success: false,
27401
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28324
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27402
28325
  };
27403
28326
  const result = GrokcliSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27404
28327
  if (!result.success) return {
@@ -27458,7 +28381,7 @@ var GrokcliSkill = class GrokcliSkill extends ToolSkill {
27458
28381
  const result = GrokcliSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27459
28382
  if (!result.success) {
27460
28383
  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)}`);
28384
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27462
28385
  }
27463
28386
  return new GrokcliSkill({
27464
28387
  outputRoot: loaded.outputRoot,
@@ -27493,6 +28416,18 @@ var HermesagentSkill = class extends AgentsSkillsSkill {
27493
28416
  static getSettablePaths() {
27494
28417
  return { relativeDirPath: HERMESAGENT_SKILLS_DIR_PATH };
27495
28418
  }
28419
+ /**
28420
+ * Commands are emitted into this same skills tree as `<slug>/SKILL.md`
28421
+ * (see `HermesagentCommand`), so a directory matching a current rulesync
28422
+ * command slug is owned by the commands feature: it must not be imported
28423
+ * as a skill nor deleted as an orphan skill.
28424
+ */
28425
+ static async isDirOwned({ dirName, inputRoot }) {
28426
+ return !await rulesyncCommandSlugExists({
28427
+ inputRoot,
28428
+ dirName
28429
+ });
28430
+ }
27496
28431
  constructor(params) {
27497
28432
  super({
27498
28433
  ...params,
@@ -27517,7 +28452,7 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27517
28452
  relativeDirPath,
27518
28453
  dirName,
27519
28454
  mainFile: {
27520
- name: SKILL_FILE_NAME,
28455
+ name: SKILL_FILE_NAME$1,
27521
28456
  body,
27522
28457
  frontmatter: { ...frontmatter }
27523
28458
  },
@@ -27541,7 +28476,7 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27541
28476
  validate() {
27542
28477
  if (!this.mainFile) return {
27543
28478
  success: false,
27544
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28479
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27545
28480
  };
27546
28481
  const result = JunieSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27547
28482
  if (!result.success) return {
@@ -27605,10 +28540,10 @@ var JunieSkill = class JunieSkill extends ToolSkill {
27605
28540
  const result = JunieSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27606
28541
  if (!result.success) {
27607
28542
  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)}`);
28543
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27609
28544
  }
27610
28545
  if (result.data.name !== loaded.dirName) {
27611
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
28546
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
27612
28547
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
27613
28548
  }
27614
28549
  return new JunieSkill({
@@ -27656,7 +28591,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27656
28591
  relativeDirPath,
27657
28592
  dirName,
27658
28593
  mainFile: {
27659
- name: SKILL_FILE_NAME,
28594
+ name: SKILL_FILE_NAME$1,
27660
28595
  body,
27661
28596
  frontmatter: { ...frontmatter }
27662
28597
  },
@@ -27680,7 +28615,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27680
28615
  validate() {
27681
28616
  if (this.mainFile === void 0) return {
27682
28617
  success: false,
27683
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28618
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27684
28619
  };
27685
28620
  const result = KiloSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27686
28621
  if (!result.success) return {
@@ -27752,7 +28687,7 @@ var KiloSkill = class KiloSkill extends ToolSkill {
27752
28687
  const result = KiloSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27753
28688
  if (!result.success) {
27754
28689
  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)}`);
28690
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27756
28691
  }
27757
28692
  return new KiloSkill({
27758
28693
  outputRoot: loaded.outputRoot,
@@ -27798,7 +28733,7 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27798
28733
  relativeDirPath,
27799
28734
  dirName,
27800
28735
  mainFile: {
27801
- name: SKILL_FILE_NAME,
28736
+ name: SKILL_FILE_NAME$1,
27802
28737
  body,
27803
28738
  frontmatter: { ...frontmatter }
27804
28739
  },
@@ -27822,7 +28757,7 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27822
28757
  validate() {
27823
28758
  if (!this.mainFile) return {
27824
28759
  success: false,
27825
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28760
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
27826
28761
  };
27827
28762
  const result = KiroSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
27828
28763
  if (!result.success) return {
@@ -27886,10 +28821,10 @@ var KiroSkill = class KiroSkill extends ToolSkill {
27886
28821
  const result = KiroSkillFrontmatterSchema.safeParse(loaded.frontmatter);
27887
28822
  if (!result.success) {
27888
28823
  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)}`);
28824
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
27890
28825
  }
27891
28826
  if (result.data.name !== loaded.dirName) {
27892
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
28827
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
27893
28828
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
27894
28829
  }
27895
28830
  return new KiroSkill({
@@ -27972,7 +28907,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
27972
28907
  relativeDirPath,
27973
28908
  dirName,
27974
28909
  mainFile: {
27975
- name: SKILL_FILE_NAME,
28910
+ name: SKILL_FILE_NAME$1,
27976
28911
  body,
27977
28912
  frontmatter: { ...frontmatter }
27978
28913
  },
@@ -27999,7 +28934,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
27999
28934
  validate() {
28000
28935
  if (this.mainFile === void 0) return {
28001
28936
  success: false,
28002
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
28937
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28003
28938
  };
28004
28939
  const result = OpenCodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28005
28940
  if (!result.success) return {
@@ -28078,7 +29013,7 @@ var OpenCodeSkill = class OpenCodeSkill extends ToolSkill {
28078
29013
  const result = OpenCodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28079
29014
  if (!result.success) {
28080
29015
  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)}`);
29016
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28082
29017
  }
28083
29018
  return new OpenCodeSkill({
28084
29019
  outputRoot: loaded.outputRoot,
@@ -28139,7 +29074,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28139
29074
  relativeDirPath: resolvedDirPath,
28140
29075
  dirName,
28141
29076
  mainFile: {
28142
- name: SKILL_FILE_NAME,
29077
+ name: SKILL_FILE_NAME$1,
28143
29078
  body,
28144
29079
  frontmatter: { ...frontmatter }
28145
29080
  },
@@ -28164,7 +29099,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28164
29099
  validate() {
28165
29100
  if (!this.mainFile) return {
28166
29101
  success: false,
28167
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29102
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28168
29103
  };
28169
29104
  const result = PiSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28170
29105
  if (!result.success) return {
@@ -28239,7 +29174,7 @@ var PiSkill = class PiSkill extends ToolSkill {
28239
29174
  const result = PiSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28240
29175
  if (!result.success) {
28241
29176
  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)}`);
29177
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28243
29178
  }
28244
29179
  return new PiSkill({
28245
29180
  outputRoot: loaded.outputRoot,
@@ -28292,7 +29227,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28292
29227
  relativeDirPath,
28293
29228
  dirName,
28294
29229
  mainFile: {
28295
- name: SKILL_FILE_NAME,
29230
+ name: SKILL_FILE_NAME$1,
28296
29231
  body,
28297
29232
  frontmatter: { ...frontmatter }
28298
29233
  },
@@ -28316,7 +29251,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28316
29251
  validate() {
28317
29252
  if (this.mainFile === void 0) return {
28318
29253
  success: false,
28319
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29254
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28320
29255
  };
28321
29256
  const result = QwencodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28322
29257
  if (!result.success) return {
@@ -28396,7 +29331,7 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
28396
29331
  const result = QwencodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28397
29332
  if (!result.success) {
28398
29333
  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)}`);
29334
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28400
29335
  }
28401
29336
  return new QwencodeSkill({
28402
29337
  outputRoot: loaded.outputRoot,
@@ -28446,7 +29381,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28446
29381
  relativeDirPath,
28447
29382
  dirName,
28448
29383
  mainFile: {
28449
- name: SKILL_FILE_NAME,
29384
+ name: SKILL_FILE_NAME$1,
28450
29385
  body,
28451
29386
  frontmatter: { ...frontmatter }
28452
29387
  },
@@ -28470,7 +29405,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28470
29405
  validate() {
28471
29406
  if (this.mainFile === void 0) return {
28472
29407
  success: false,
28473
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29408
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28474
29409
  };
28475
29410
  const result = ReasonixSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28476
29411
  if (!result.success) return {
@@ -28530,7 +29465,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28530
29465
  const result = ReasonixSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28531
29466
  if (!result.success) {
28532
29467
  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)}`);
29468
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28534
29469
  }
28535
29470
  return new ReasonixSkill({
28536
29471
  outputRoot: loaded.outputRoot,
@@ -28553,7 +29488,7 @@ var ReasonixSkill = class ReasonixSkill extends ToolSkill {
28553
29488
  * skills-feature ownership, matching the previous behavior for such dirs.
28554
29489
  */
28555
29490
  static async isDirOwned({ outputRoot, relativeDirPath, dirName }) {
28556
- const skillFilePath = (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME);
29491
+ const skillFilePath = (0, node_path.join)(outputRoot, relativeDirPath, dirName, SKILL_FILE_NAME$1);
28557
29492
  try {
28558
29493
  const { frontmatter } = parseFrontmatter(await readFileContent(skillFilePath), skillFilePath);
28559
29494
  return frontmatter["runAs"] !== REASONIX_SUBAGENT_RUN_AS;
@@ -28603,7 +29538,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28603
29538
  relativeDirPath,
28604
29539
  dirName,
28605
29540
  mainFile: {
28606
- name: SKILL_FILE_NAME,
29541
+ name: SKILL_FILE_NAME$1,
28607
29542
  body,
28608
29543
  frontmatter: { ...frontmatter }
28609
29544
  },
@@ -28627,7 +29562,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28627
29562
  validate() {
28628
29563
  if (!this.mainFile) return {
28629
29564
  success: false,
28630
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29565
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28631
29566
  };
28632
29567
  const result = ReplitSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28633
29568
  if (!result.success) return {
@@ -28695,7 +29630,7 @@ var ReplitSkill = class ReplitSkill extends ToolSkill {
28695
29630
  const result = ReplitSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28696
29631
  if (!result.success) {
28697
29632
  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)}`);
29633
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28699
29634
  }
28700
29635
  return new ReplitSkill({
28701
29636
  outputRoot: loaded.outputRoot,
@@ -28742,7 +29677,7 @@ var RooSkill = class RooSkill extends ToolSkill {
28742
29677
  relativeDirPath,
28743
29678
  dirName,
28744
29679
  mainFile: {
28745
- name: SKILL_FILE_NAME,
29680
+ name: SKILL_FILE_NAME$1,
28746
29681
  body,
28747
29682
  frontmatter: { ...frontmatter }
28748
29683
  },
@@ -28766,7 +29701,7 @@ var RooSkill = class RooSkill extends ToolSkill {
28766
29701
  validate() {
28767
29702
  if (!this.mainFile) return {
28768
29703
  success: false,
28769
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
29704
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
28770
29705
  };
28771
29706
  const result = RooSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
28772
29707
  if (!result.success) return {
@@ -28830,10 +29765,10 @@ var RooSkill = class RooSkill extends ToolSkill {
28830
29765
  const result = RooSkillFrontmatterSchema.safeParse(loaded.frontmatter);
28831
29766
  if (!result.success) {
28832
29767
  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)}`);
29768
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
28834
29769
  }
28835
29770
  if (result.data.name !== loaded.dirName) {
28836
- const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
29771
+ const skillFilePath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME$1);
28837
29772
  throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
28838
29773
  }
28839
29774
  return new RooSkill({
@@ -29050,7 +29985,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29050
29985
  relativeDirPath,
29051
29986
  dirName,
29052
29987
  mainFile: {
29053
- name: SKILL_FILE_NAME,
29988
+ name: SKILL_FILE_NAME$1,
29054
29989
  body,
29055
29990
  frontmatter: { ...frontmatter }
29056
29991
  },
@@ -29077,7 +30012,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29077
30012
  validate() {
29078
30013
  if (!this.mainFile) return {
29079
30014
  success: false,
29080
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
30015
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29081
30016
  };
29082
30017
  const result = VibeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29083
30018
  if (!result.success) return {
@@ -29141,7 +30076,7 @@ var VibeSkill = class VibeSkill extends ToolSkill {
29141
30076
  const result = VibeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29142
30077
  if (!result.success) {
29143
30078
  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)}`);
30079
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29145
30080
  }
29146
30081
  return new VibeSkill({
29147
30082
  outputRoot: loaded.outputRoot,
@@ -29197,7 +30132,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29197
30132
  relativeDirPath,
29198
30133
  dirName,
29199
30134
  mainFile: {
29200
- name: SKILL_FILE_NAME,
30135
+ name: SKILL_FILE_NAME$1,
29201
30136
  body,
29202
30137
  frontmatter: { ...frontmatter }
29203
30138
  },
@@ -29221,7 +30156,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29221
30156
  validate() {
29222
30157
  if (this.mainFile === void 0) return {
29223
30158
  success: false,
29224
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
30159
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29225
30160
  };
29226
30161
  const result = WarpSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29227
30162
  if (!result.success) return {
@@ -29281,7 +30216,7 @@ var WarpSkill = class WarpSkill extends ToolSkill {
29281
30216
  const result = WarpSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29282
30217
  if (!result.success) {
29283
30218
  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)}`);
30219
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29285
30220
  }
29286
30221
  return new WarpSkill({
29287
30222
  outputRoot: loaded.outputRoot,
@@ -29329,7 +30264,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29329
30264
  relativeDirPath,
29330
30265
  dirName,
29331
30266
  mainFile: {
29332
- name: SKILL_FILE_NAME,
30267
+ name: SKILL_FILE_NAME$1,
29333
30268
  body,
29334
30269
  frontmatter: { ...frontmatter }
29335
30270
  },
@@ -29353,7 +30288,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29353
30288
  validate() {
29354
30289
  if (!this.mainFile) return {
29355
30290
  success: false,
29356
- error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
30291
+ error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME$1} file does not exist`)
29357
30292
  };
29358
30293
  const result = ZedSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
29359
30294
  if (!result.success) return {
@@ -29422,7 +30357,7 @@ var ZedSkill = class ZedSkill extends ToolSkill {
29422
30357
  const result = ZedSkillFrontmatterSchema.safeParse(loaded.frontmatter);
29423
30358
  if (!result.success) {
29424
30359
  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)}`);
30360
+ throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME$1)}: ${formatError(result.error)}`);
29426
30361
  }
29427
30362
  return new ZedSkill({
29428
30363
  outputRoot: loaded.outputRoot,
@@ -29862,7 +30797,8 @@ var SkillsProcessor = class extends DirFeatureProcessor {
29862
30797
  if (factory.class.isDirOwned && !await factory.class.isDirOwned({
29863
30798
  outputRoot: this.outputRoot,
29864
30799
  relativeDirPath: root,
29865
- dirName
30800
+ dirName,
30801
+ inputRoot: this.inputRoot
29866
30802
  })) continue;
29867
30803
  seenDirNames.add(dirName);
29868
30804
  loadEntries.push({
@@ -29893,7 +30829,8 @@ var SkillsProcessor = class extends DirFeatureProcessor {
29893
30829
  if (factory.class.isDirOwned && !await factory.class.isDirOwned({
29894
30830
  outputRoot: this.outputRoot,
29895
30831
  relativeDirPath: root,
29896
- dirName
30832
+ dirName,
30833
+ inputRoot: this.inputRoot
29897
30834
  })) continue;
29898
30835
  const toolSkill = factory.class.forDeletion({
29899
30836
  outputRoot: this.outputRoot,
@@ -30996,7 +31933,6 @@ var CodexCliSubagent = class CodexCliSubagent extends ToolSubagent {
30996
31933
  };
30997
31934
  //#endregion
30998
31935
  //#region src/features/subagents/copilot-subagent.ts
30999
- const REQUIRED_TOOL = "agent/runSubagent";
31000
31936
  const CopilotSubagentFrontmatterSchema = zod_mini.z.looseObject({
31001
31937
  name: zod_mini.z.string(),
31002
31938
  description: zod_mini.z.optional(zod_mini.z.string()),
@@ -31006,10 +31942,6 @@ const normalizeTools = (tools) => {
31006
31942
  if (!tools) return [];
31007
31943
  return Array.isArray(tools) ? tools : [tools];
31008
31944
  };
31009
- const ensureRequiredTool = (tools) => {
31010
- const mergedTools = /* @__PURE__ */ new Set([REQUIRED_TOOL, ...tools]);
31011
- return Array.from(mergedTools);
31012
- };
31013
31945
  const toCopilotAgentFilePath = (relativeFilePath) => {
31014
31946
  if (relativeFilePath.endsWith(".agent.md")) return relativeFilePath;
31015
31947
  if (relativeFilePath.endsWith(".md")) return relativeFilePath.replace(/\.md$/, ".agent.md");
@@ -31063,14 +31995,14 @@ var CopilotSubagent = class CopilotSubagent extends ToolSubagent {
31063
31995
  static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
31064
31996
  const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
31065
31997
  const copilotSection = rulesyncFrontmatter.copilot ?? {};
31998
+ const { tools: _rawTools, ...copilotRest } = copilotSection;
31066
31999
  const toolsField = copilotSection.tools;
31067
32000
  const userTools = normalizeTools(Array.isArray(toolsField) || typeof toolsField === "string" ? toolsField : void 0);
31068
- const mergedTools = ensureRequiredTool(userTools);
31069
32001
  const copilotFrontmatter = {
31070
32002
  name: rulesyncFrontmatter.name,
31071
32003
  description: rulesyncFrontmatter.description,
31072
- ...copilotSection,
31073
- ...mergedTools.length > 0 && { tools: mergedTools }
32004
+ ...copilotRest,
32005
+ ...userTools.length > 0 && { tools: userTools }
31074
32006
  };
31075
32007
  const body = rulesyncSubagent.getBody();
31076
32008
  const fileContent = stringifyFrontmatter(body, copilotFrontmatter);
@@ -33294,7 +34226,7 @@ var ReasonixSubagent = class ReasonixSubagent extends ToolSubagent {
33294
34226
  frontmatter: reasonixFrontmatter,
33295
34227
  body,
33296
34228
  relativeDirPath: paths.relativeDirPath,
33297
- relativeFilePath: (0, node_path.join)(subagentName, SKILL_FILE_NAME),
34229
+ relativeFilePath: (0, node_path.join)(subagentName, SKILL_FILE_NAME$1),
33298
34230
  fileContent,
33299
34231
  validate,
33300
34232
  global
@@ -34300,6 +35232,7 @@ const RulesyncRuleFrontmatterSchema = zod_mini.z.object({
34300
35232
  name: zod_mini.z.optional(zod_mini.z.string()),
34301
35233
  description: zod_mini.z.optional(zod_mini.z.string())
34302
35234
  })),
35235
+ pi: zod_mini.z.optional(zod_mini.z.looseObject({ systemPrompt: zod_mini.z.optional(zod_mini.z.enum(["append"])) })),
34303
35236
  takt: zod_mini.z.optional(zod_mini.z.looseObject({
34304
35237
  name: zod_mini.z.optional(zod_mini.z.string()),
34305
35238
  extends: zod_mini.z.optional(zod_mini.z.string()),
@@ -34463,6 +35396,16 @@ var ToolRule = class extends ToolFile {
34463
35396
  isRoot() {
34464
35397
  return this.root;
34465
35398
  }
35399
+ /**
35400
+ * Whether this rule must be left out of the root rule's reference/MCP
35401
+ * instruction listings even though it is a non-root survivor. Used by files
35402
+ * the tool loads through its own mechanism (e.g. Pi's `APPEND_SYSTEM.md`,
35403
+ * which Pi appends to the system prompt itself — referencing it from
35404
+ * `AGENTS.md` would double-load the content).
35405
+ */
35406
+ isExcludedFromRootReferences() {
35407
+ return false;
35408
+ }
34466
35409
  getDescription() {
34467
35410
  return this.description;
34468
35411
  }
@@ -36809,6 +37752,15 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
36809
37752
  * is `undefined`). This mirrors the grokcli, warp, and deepagents targets.
36810
37753
  *
36811
37754
  * Goose uses plain markdown files (.goosehints) without frontmatter.
37755
+ *
37756
+ * Global scope emits only `~/.config/goose/.goosehints`. Goose v1.41.0 (PR
37757
+ * block/goose#9736) additionally loads the vendor-neutral
37758
+ * `~/.agents/AGENTS.md` alongside the config-dir hints, but rulesync
37759
+ * deliberately does not emit that shared path from the goose target: the
37760
+ * config-dir hints remain fully loaded (no capability loss), and the
37761
+ * cross-tool `~/.agents/AGENTS.md` is already written by targets that own it
37762
+ * (e.g. cline in global mode) — a second writer would need cross-target
37763
+ * shared-file coordination for zero gain (decision recorded in issue #2207).
36812
37764
  */
36813
37765
  var GooseRule = class GooseRule extends ToolRule {
36814
37766
  static getSettablePaths({ global } = {}) {
@@ -37010,14 +37962,21 @@ var HermesagentRule = class HermesagentRule extends ToolRule {
37010
37962
  /**
37011
37963
  * Rule generator for JetBrains Junie AI coding agent
37012
37964
  *
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.
37965
+ * Generates `.junie/AGENTS.md` files based on rulesync rule content. Junie CLI
37966
+ * resolves project guidelines **first-match-wins**: `.junie/AGENTS.md` → root
37967
+ * `AGENTS.md` legacy `.junie/guidelines.md` / `.junie/guidelines/`. Only the
37968
+ * first match is loaded, it documents no `@`-reference or file-inclusion
37969
+ * mechanism, and no `.junie/memories/` read path exists — so non-root rules
37970
+ * are folded into the single root `.junie/AGENTS.md` by the RulesProcessor
37971
+ * (`nonRoot` is `undefined`, mirroring the grokcli / warp / deepagents
37972
+ * targets; decision recorded in issue #2211). The legacy
37973
+ * `.junie/guidelines.md` is still accepted as an import fallback, but
37974
+ * generation always targets `.junie/AGENTS.md`. Junie uses plain markdown
37975
+ * without frontmatter requirements.
37976
+ *
37977
+ * Global (user) scope writes a single `~/.junie/AGENTS.md` file. Junie merges
37978
+ * these user-scope guidelines with the project guidelines (both are included
37979
+ * and marked clearly).
37021
37980
  *
37022
37981
  * @see https://junie.jetbrains.com/docs/junie-ide-plugin.html
37023
37982
  * @see https://junie.jetbrains.com/docs/guidelines-and-memory.html
@@ -37036,15 +37995,12 @@ var JunieRule = class JunieRule extends ToolRule {
37036
37995
  alternativeRoots: [{
37037
37996
  relativeDirPath: buildToolPath(JUNIE_DIR, ".", excludeToolDir),
37038
37997
  relativeFilePath: JUNIE_LEGACY_RULE_FILE_NAME
37039
- }],
37040
- nonRoot: { relativeDirPath: buildToolPath(JUNIE_DIR, "memories", excludeToolDir) }
37998
+ }]
37041
37999
  };
37042
38000
  }
37043
38001
  /**
37044
38002
  * Determines whether a given relative file path refers to a root guideline file.
37045
38003
  * 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
38004
  */
37049
38005
  static isRootRelativeFilePath(relativeFilePath) {
37050
38006
  return relativeFilePath === "AGENTS.md" || relativeFilePath === "guidelines.md";
@@ -37063,10 +38019,7 @@ var JunieRule = class JunieRule extends ToolRule {
37063
38019
  root: true
37064
38020
  });
37065
38021
  }
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;
38022
+ const relativeDirPath = this.getSettablePaths().root.relativeDirPath;
37070
38023
  const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(relativeDirPath, relativeFilePath)));
37071
38024
  return new JunieRule({
37072
38025
  outputRoot,
@@ -37074,7 +38027,7 @@ var JunieRule = class JunieRule extends ToolRule {
37074
38027
  relativeFilePath,
37075
38028
  fileContent,
37076
38029
  validate,
37077
- root: isRoot
38030
+ root: JunieRule.isRootRelativeFilePath(relativeFilePath)
37078
38031
  });
37079
38032
  }
37080
38033
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
@@ -37089,13 +38042,15 @@ var JunieRule = class JunieRule extends ToolRule {
37089
38042
  rootPath: paths.root
37090
38043
  }));
37091
38044
  }
37092
- return new JunieRule(this.buildToolRuleParamsDefault({
38045
+ const { root } = this.getSettablePaths();
38046
+ return new JunieRule({
37093
38047
  outputRoot,
37094
- rulesyncRule,
38048
+ relativeDirPath: root.relativeDirPath,
38049
+ relativeFilePath: root.relativeFilePath,
38050
+ fileContent: rulesyncRule.getBody(),
37095
38051
  validate,
37096
- rootPath: this.getSettablePaths().root,
37097
- nonRootPath: this.getSettablePaths().nonRoot
37098
- }));
38052
+ root: rulesyncRule.getFrontmatter().root ?? false
38053
+ });
37099
38054
  }
37100
38055
  toRulesyncRule() {
37101
38056
  return this.toRulesyncRuleDefault();
@@ -37498,31 +38453,68 @@ var OpenCodeRule = class OpenCodeRule extends ToolRule {
37498
38453
  * RulesProcessor (there is no separate non-root output location — `nonRoot` is
37499
38454
  * `undefined`). This mirrors the codexcli, grokcli, warp, and deepagents targets.
37500
38455
  *
37501
- * Pi also loads two system-prompt instruction files that rulesync does NOT emit:
38456
+ * Pi also loads two system-prompt instruction files. `.pi/APPEND_SYSTEM.md`
38457
+ * (global `~/.pi/agent/APPEND_SYSTEM.md`) *appends* to the default system prompt,
38458
+ * and rulesync emits it from any rule that opts in via the `pi.systemPrompt:
38459
+ * append` frontmatter block: those bodies are routed here instead of being folded
38460
+ * into `AGENTS.md`, and multiple opted-in rules concatenate in source order.
37502
38461
  * `.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.
38462
+ * prompt entirely which silently disables Pi's built-in tool instructions — so
38463
+ * rulesync deliberately never emits it and leaves it to be authored by hand.
38464
+ * See docs/reference/file-formats.md.
37509
38465
  */
37510
38466
  var PiRule = class PiRule extends ToolRule {
37511
- constructor({ fileContent, root, ...rest }) {
38467
+ appendSystemPrompt;
38468
+ constructor({ fileContent, root, appendSystemPrompt = false, ...rest }) {
37512
38469
  super({
37513
38470
  ...rest,
37514
38471
  fileContent,
37515
38472
  root: root ?? false
37516
38473
  });
38474
+ this.appendSystemPrompt = appendSystemPrompt;
37517
38475
  }
37518
38476
  static getSettablePaths({ global = false, excludeToolDir } = {}) {
37519
- return { root: {
37520
- relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".",
37521
- relativeFilePath: PI_RULE_FILE_NAME
37522
- } };
38477
+ return {
38478
+ root: {
38479
+ relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".",
38480
+ relativeFilePath: PI_RULE_FILE_NAME
38481
+ },
38482
+ appendSystemPrompt: {
38483
+ relativeDirPath: global ? buildToolPath(".pi", "agent", excludeToolDir) : ".pi",
38484
+ relativeFilePath: PI_APPEND_SYSTEM_FILE_NAME
38485
+ }
38486
+ };
37523
38487
  }
37524
- static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
37525
- const { root } = this.getSettablePaths({ global });
38488
+ /**
38489
+ * Extra fixed files this tool manages beyond the root rule. The
38490
+ * RulesProcessor enumerates these for import and deletion so a stale
38491
+ * `APPEND_SYSTEM.md` is cleaned up once no rule opts in anymore.
38492
+ */
38493
+ static getExtraFixedFiles({ global = false } = {}) {
38494
+ return [this.getSettablePaths({ global }).appendSystemPrompt];
38495
+ }
38496
+ /**
38497
+ * Pi appends `APPEND_SYSTEM.md` to the system prompt itself, so listing it in
38498
+ * the root rule's reference section (toon/explicit discovery modes) would
38499
+ * double-load the content.
38500
+ */
38501
+ isExcludedFromRootReferences() {
38502
+ return this.appendSystemPrompt;
38503
+ }
38504
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
38505
+ const { root, appendSystemPrompt } = this.getSettablePaths({ global });
38506
+ if (relativeFilePath === "APPEND_SYSTEM.md") {
38507
+ const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(appendSystemPrompt.relativeDirPath, appendSystemPrompt.relativeFilePath)));
38508
+ return new PiRule({
38509
+ outputRoot,
38510
+ relativeDirPath: appendSystemPrompt.relativeDirPath,
38511
+ relativeFilePath: appendSystemPrompt.relativeFilePath,
38512
+ fileContent,
38513
+ validate,
38514
+ root: false,
38515
+ appendSystemPrompt: true
38516
+ });
38517
+ }
37526
38518
  const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(root.relativeDirPath, root.relativeFilePath)));
37527
38519
  return new PiRule({
37528
38520
  outputRoot,
@@ -37534,8 +38526,18 @@ var PiRule = class PiRule extends ToolRule {
37534
38526
  });
37535
38527
  }
37536
38528
  static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
37537
- const { root } = this.getSettablePaths({ global });
37538
- const isRoot = rulesyncRule.getFrontmatter().root ?? false;
38529
+ const { root, appendSystemPrompt } = this.getSettablePaths({ global });
38530
+ const frontmatter = rulesyncRule.getFrontmatter();
38531
+ if (!frontmatter.root && frontmatter.pi?.systemPrompt === "append") return new PiRule({
38532
+ outputRoot,
38533
+ relativeDirPath: appendSystemPrompt.relativeDirPath,
38534
+ relativeFilePath: appendSystemPrompt.relativeFilePath,
38535
+ fileContent: rulesyncRule.getBody(),
38536
+ validate,
38537
+ root: false,
38538
+ appendSystemPrompt: true
38539
+ });
38540
+ const isRoot = frontmatter.root ?? false;
37539
38541
  return new PiRule({
37540
38542
  outputRoot,
37541
38543
  relativeDirPath: root.relativeDirPath,
@@ -37546,6 +38548,17 @@ var PiRule = class PiRule extends ToolRule {
37546
38548
  });
37547
38549
  }
37548
38550
  toRulesyncRule() {
38551
+ if (this.appendSystemPrompt) return new RulesyncRule({
38552
+ outputRoot: process.cwd(),
38553
+ relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
38554
+ relativeFilePath: PI_APPEND_SYSTEM_FILE_NAME,
38555
+ frontmatter: {
38556
+ root: false,
38557
+ targets: ["pi"],
38558
+ pi: { systemPrompt: "append" }
38559
+ },
38560
+ body: this.getFileContent()
38561
+ });
37549
38562
  return this.toRulesyncRuleDefault();
37550
38563
  }
37551
38564
  validate() {
@@ -37556,6 +38569,15 @@ var PiRule = class PiRule extends ToolRule {
37556
38569
  }
37557
38570
  static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
37558
38571
  const { root } = this.getSettablePaths({ global });
38572
+ if (relativeFilePath === "APPEND_SYSTEM.md") return new PiRule({
38573
+ outputRoot,
38574
+ relativeDirPath,
38575
+ relativeFilePath,
38576
+ fileContent: "",
38577
+ validate: false,
38578
+ root: false,
38579
+ appendSystemPrompt: true
38580
+ });
37559
38581
  const isRoot = relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === root.relativeDirPath);
37560
38582
  return new PiRule({
37561
38583
  outputRoot,
@@ -38715,7 +39737,8 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
38715
39737
  meta: {
38716
39738
  extension: "md",
38717
39739
  supportsGlobal: true,
38718
- ruleDiscoveryMode: "toon"
39740
+ ruleDiscoveryMode: "auto",
39741
+ foldsNonRootIntoRoot: true
38719
39742
  }
38720
39743
  }],
38721
39744
  ["kilo", {
@@ -38981,7 +40004,7 @@ var RulesProcessor = class extends FeatureProcessor {
38981
40004
  */
38982
40005
  async buildMcpInstructionFiles({ toolRules, meta }) {
38983
40006
  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())));
40007
+ const instructionPaths = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences()).map((rule) => toPosixPath((0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath())));
38985
40008
  if (instructionPaths.length === 0) return [];
38986
40009
  return [await meta.mcpInstructionsRegistrar.fromInstructions({
38987
40010
  outputRoot: this.outputRoot,
@@ -39013,7 +40036,7 @@ var RulesProcessor = class extends FeatureProcessor {
39013
40036
  const toolRelativeDirPath = skillClass.getSettablePaths({ global: this.global }).relativeDirPath;
39014
40037
  return this.skills.filter((skill) => skillClass.isTargetedByRulesyncSkill(skill)).map((skill) => {
39015
40038
  const frontmatter = skill.getFrontmatter();
39016
- const relativePath = (0, node_path.join)(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME);
40039
+ const relativePath = (0, node_path.join)(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME$1);
39017
40040
  return {
39018
40041
  name: frontmatter.name,
39019
40042
  description: frontmatter.description,
@@ -39037,11 +40060,25 @@ var RulesProcessor = class extends FeatureProcessor {
39037
40060
  */
39038
40061
  foldNonRootRulesIntoRootRule(toolRules) {
39039
40062
  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);
40063
+ const groups = /* @__PURE__ */ new Map();
40064
+ for (const rule of toolRules) {
40065
+ const path = (0, node_path.join)(rule.getRelativeDirPath(), rule.getRelativeFilePath());
40066
+ const group = groups.get(path);
40067
+ if (group) group.push(rule);
40068
+ else groups.set(path, [rule]);
40069
+ }
40070
+ const survivors = /* @__PURE__ */ new Set();
40071
+ for (const group of groups.values()) {
40072
+ const target = group.find((rule) => rule.isRoot()) ?? group[0];
40073
+ if (!target) continue;
40074
+ const mergedContent = [target, ...group.filter((rule) => rule !== target)].map((rule) => rule.getFileContent().trim()).filter((content) => content.length > 0).join("\n\n");
40075
+ target.setFileContent(mergedContent);
40076
+ survivors.add(target);
40077
+ }
40078
+ for (let i = toolRules.length - 1; i >= 0; i--) {
40079
+ const rule = toolRules[i];
40080
+ if (rule && !survivors.has(rule)) toolRules.splice(i, 1);
40081
+ }
39045
40082
  }
39046
40083
  /**
39047
40084
  * Handle localRoot rule generation based on tool target.
@@ -39251,6 +40288,27 @@ As this project's AI coding tool, you must follow the additional conventions bel
39251
40288
  const mirrorPaths = await findFilesByGlobs(mirrorGlob);
39252
40289
  return buildDeletionRulesFromPaths(mirrorPaths);
39253
40290
  })();
40291
+ const extraFixedToolRules = await (async () => {
40292
+ const extraFiles = factory.class.getExtraFixedFiles?.({ global: this.global });
40293
+ if (!extraFiles || extraFiles.length === 0) return [];
40294
+ const filePaths = await findFilesByGlobs(extraFiles.map((file) => (0, node_path.join)(this.outputRoot, file.relativeDirPath, file.relativeFilePath)));
40295
+ if (filePaths.length === 0) return [];
40296
+ if (forDeletion) return buildDeletionRulesFromPaths(filePaths);
40297
+ return await Promise.all(filePaths.map((filePath) => {
40298
+ const relativeDirPath = resolveRelativeDirPath(filePath);
40299
+ checkPathTraversal({
40300
+ relativePath: relativeDirPath,
40301
+ intendedRootDir: this.outputRoot
40302
+ });
40303
+ return factory.class.fromFile({
40304
+ outputRoot: this.outputRoot,
40305
+ relativeDirPath,
40306
+ relativeFilePath: (0, node_path.basename)(filePath),
40307
+ global: this.global
40308
+ });
40309
+ }));
40310
+ })();
40311
+ this.logger.debug(`Found ${extraFixedToolRules.length} extra fixed tool rule files`);
39254
40312
  const nonRootToolRules = await (async () => {
39255
40313
  if (!settablePaths.nonRoot) return [];
39256
40314
  const nonRootOutputRoot = (0, node_path.join)(this.outputRoot, settablePaths.nonRoot.relativeDirPath);
@@ -39286,6 +40344,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39286
40344
  ...rootToolRules,
39287
40345
  ...localRootToolRules,
39288
40346
  ...rootMirrorDeletionRules,
40347
+ ...extraFixedToolRules,
39289
40348
  ...nonRootToolRules
39290
40349
  ];
39291
40350
  } catch (error) {
@@ -39313,7 +40372,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39313
40372
  return toolRuleFactories.get(result.data);
39314
40373
  }
39315
40374
  generateToonReferencesSection(toolRules) {
39316
- const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot());
40375
+ const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences());
39317
40376
  if (toolRulesWithoutRoot.length === 0) return "";
39318
40377
  const lines = [];
39319
40378
  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 +40388,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39329
40388
  return lines.join("\n") + "\n\n";
39330
40389
  }
39331
40390
  generateReferencesSection(toolRules) {
39332
- const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot());
40391
+ const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot() && !rule.isExcludedFromRootReferences());
39333
40392
  if (toolRulesWithoutRoot.length === 0) return "";
39334
40393
  const lines = [];
39335
40394
  lines.push("Please also reference the following rules as needed:");
@@ -39350,7 +40409,7 @@ As this project's AI coding tool, you must follow the additional conventions bel
39350
40409
  */
39351
40410
  async function convertFromTool(params) {
39352
40411
  const ctx = params;
39353
- const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount] = [
40412
+ const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount, checksCount] = [
39354
40413
  await runFeatureConvert(ctx, buildRulesStrategy(ctx)),
39355
40414
  await runFeatureConvert(ctx, buildIgnoreStrategy(ctx)),
39356
40415
  await runFeatureConvert(ctx, buildMcpStrategy(ctx)),
@@ -39358,7 +40417,8 @@ async function convertFromTool(params) {
39358
40417
  await runFeatureConvert(ctx, buildSubagentsStrategy(ctx)),
39359
40418
  await runFeatureConvert(ctx, buildSkillsStrategy(ctx)),
39360
40419
  await runFeatureConvert(ctx, buildHooksStrategy(ctx)),
39361
- await runFeatureConvert(ctx, buildPermissionsStrategy(ctx))
40420
+ await runFeatureConvert(ctx, buildPermissionsStrategy(ctx)),
40421
+ await runFeatureConvert(ctx, buildChecksStrategy(ctx))
39362
40422
  ];
39363
40423
  return {
39364
40424
  rulesCount,
@@ -39368,7 +40428,8 @@ async function convertFromTool(params) {
39368
40428
  subagentsCount,
39369
40429
  skillsCount,
39370
40430
  hooksCount,
39371
- permissionsCount
40431
+ permissionsCount,
40432
+ checksCount
39372
40433
  };
39373
40434
  }
39374
40435
  async function runFeatureConvert(ctx, strategy) {
@@ -39601,6 +40662,27 @@ function buildPermissionsStrategy(ctx) {
39601
40662
  write: (p, files) => p.writeAiFiles(files)
39602
40663
  };
39603
40664
  }
40665
+ function buildChecksStrategy(ctx) {
40666
+ const { config, logger } = ctx;
40667
+ const global = config.getGlobal();
40668
+ const outputRoot = getOutputRoot(config);
40669
+ return {
40670
+ feature: "checks",
40671
+ itemLabel: "check file(s)",
40672
+ allTargets: ChecksProcessor.getToolTargets({ global }),
40673
+ createProcessor: ({ toolTarget, dryRun }) => new ChecksProcessor({
40674
+ outputRoot,
40675
+ toolTarget,
40676
+ global,
40677
+ dryRun,
40678
+ logger
40679
+ }),
40680
+ loadSource: (p) => p.loadToolFiles(),
40681
+ toRulesync: (p, files) => p.convertToolFilesToRulesyncFiles(files),
40682
+ fromRulesync: (p, files) => p.convertRulesyncFilesToToolFiles(files),
40683
+ write: (p, files) => p.writeAiFiles(files)
40684
+ };
40685
+ }
39604
40686
  //#endregion
39605
40687
  //#region src/types/processor-registry.ts
39606
40688
  const PROCESSOR_REGISTRY = [
@@ -39651,6 +40733,12 @@ const PROCESSOR_REGISTRY = [
39651
40733
  processor: PermissionsProcessor,
39652
40734
  schema: PermissionsProcessorToolTargetSchema,
39653
40735
  factory: toolPermissionsFactories
40736
+ },
40737
+ {
40738
+ feature: "checks",
40739
+ processor: ChecksProcessor,
40740
+ schema: ChecksProcessorToolTargetSchema,
40741
+ factory: toolCheckFactories
39654
40742
  }
39655
40743
  ];
39656
40744
  const getProcessorRegistryEntry = (feature) => {
@@ -40000,6 +41088,7 @@ const GENERATION_STEP_GRAPH = [
40000
41088
  id: "permissions",
40001
41089
  ...sharedWriteMeta("permissions")
40002
41090
  },
41091
+ { id: "checks" },
40003
41092
  {
40004
41093
  id: "rules",
40005
41094
  ...sharedWriteMeta("rules"),
@@ -40085,6 +41174,10 @@ async function generate(params) {
40085
41174
  config,
40086
41175
  logger
40087
41176
  }),
41177
+ checks: () => generateChecksCore({
41178
+ config,
41179
+ logger
41180
+ }),
40088
41181
  rules: () => generateRulesCore({
40089
41182
  config,
40090
41183
  logger,
@@ -40121,6 +41214,8 @@ async function generate(params) {
40121
41214
  hooksPaths: get("hooks").paths,
40122
41215
  permissionsCount: get("permissions").count,
40123
41216
  permissionsPaths: get("permissions").paths,
41217
+ checksCount: get("checks").count,
41218
+ checksPaths: get("checks").paths,
40124
41219
  skills: skillsResult.skills,
40125
41220
  hasDiff
40126
41221
  };
@@ -40489,6 +41584,44 @@ async function generatePermissionsCore(params) {
40489
41584
  hasDiff
40490
41585
  };
40491
41586
  }
41587
+ async function generateChecksCore(params) {
41588
+ const { config, logger } = params;
41589
+ let totalCount = 0;
41590
+ const allPaths = [];
41591
+ let hasDiff = false;
41592
+ const supportedChecksTargets = ChecksProcessor.getToolTargets({ global: config.getGlobal() });
41593
+ const toolTargets = (0, es_toolkit.intersection)(config.getTargets(), supportedChecksTargets);
41594
+ warnUnsupportedTargets({
41595
+ config,
41596
+ supportedTargets: supportedChecksTargets,
41597
+ featureName: "checks",
41598
+ logger
41599
+ });
41600
+ for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
41601
+ if (!config.getFeatures(toolTarget).includes("checks")) continue;
41602
+ const processor = new ChecksProcessor({
41603
+ outputRoot,
41604
+ inputRoot: config.getInputRoot(),
41605
+ toolTarget,
41606
+ global: config.getGlobal(),
41607
+ dryRun: config.isPreviewMode(),
41608
+ logger
41609
+ });
41610
+ const result = await processFeatureWithRulesyncFiles({
41611
+ config,
41612
+ processor,
41613
+ rulesyncFiles: await processor.loadRulesyncFiles()
41614
+ });
41615
+ totalCount += result.count;
41616
+ allPaths.push(...result.paths);
41617
+ if (result.hasDiff) hasDiff = true;
41618
+ }
41619
+ return {
41620
+ count: totalCount,
41621
+ paths: allPaths,
41622
+ hasDiff
41623
+ };
41624
+ }
40492
41625
  //#endregion
40493
41626
  //#region src/lib/import.ts
40494
41627
  /**
@@ -40546,6 +41679,11 @@ async function importFromTool(params) {
40546
41679
  config,
40547
41680
  tool,
40548
41681
  logger
41682
+ }),
41683
+ checksCount: await importChecksCore({
41684
+ config,
41685
+ tool,
41686
+ logger
40549
41687
  })
40550
41688
  };
40551
41689
  }
@@ -40762,6 +41900,27 @@ async function importPermissionsCore(params) {
40762
41900
  if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} permissions file(s)`);
40763
41901
  return writtenCount;
40764
41902
  }
41903
+ async function importChecksCore(params) {
41904
+ const { config, tool, logger } = params;
41905
+ if (!config.getFeatures(tool).includes("checks")) return 0;
41906
+ const global = config.getGlobal();
41907
+ if (!ChecksProcessor.getToolTargets({ global }).includes(tool)) return 0;
41908
+ const checksProcessor = new ChecksProcessor({
41909
+ outputRoot: config.getOutputRoots()[0] ?? ".",
41910
+ toolTarget: tool,
41911
+ global,
41912
+ logger
41913
+ });
41914
+ const toolFiles = await checksProcessor.loadToolFiles();
41915
+ if (toolFiles.length === 0) {
41916
+ logger.warn(`No check files found for ${tool}. Skipping import.`);
41917
+ return 0;
41918
+ }
41919
+ const rulesyncFiles = await checksProcessor.convertToolFilesToRulesyncFiles(toolFiles);
41920
+ const { count: writtenCount } = await checksProcessor.writeAiFiles(rulesyncFiles);
41921
+ if (config.getVerbose() && writtenCount > 0) logger.success(`Created ${writtenCount} check files`);
41922
+ return writtenCount;
41923
+ }
40765
41924
  //#endregion
40766
41925
  Object.defineProperty(exports, "ALL_FEATURES", {
40767
41926
  enumerable: true,
@@ -40823,12 +41982,24 @@ Object.defineProperty(exports, "CLIError", {
40823
41982
  return CLIError;
40824
41983
  }
40825
41984
  });
41985
+ Object.defineProperty(exports, "CODEXCLI_BASH_RULES_FILE_NAME", {
41986
+ enumerable: true,
41987
+ get: function() {
41988
+ return CODEXCLI_BASH_RULES_FILE_NAME;
41989
+ }
41990
+ });
40826
41991
  Object.defineProperty(exports, "CODEXCLI_DIR", {
40827
41992
  enumerable: true,
40828
41993
  get: function() {
40829
41994
  return CODEXCLI_DIR;
40830
41995
  }
40831
41996
  });
41997
+ Object.defineProperty(exports, "ChecksProcessor", {
41998
+ enumerable: true,
41999
+ get: function() {
42000
+ return ChecksProcessor;
42001
+ }
42002
+ });
40832
42003
  Object.defineProperty(exports, "CommandsProcessor", {
40833
42004
  enumerable: true,
40834
42005
  get: function() {
@@ -40895,6 +42066,12 @@ Object.defineProperty(exports, "RULESYNC_AIIGNORE_RELATIVE_FILE_PATH", {
40895
42066
  return RULESYNC_AIIGNORE_RELATIVE_FILE_PATH;
40896
42067
  }
40897
42068
  });
42069
+ Object.defineProperty(exports, "RULESYNC_CHECKS_RELATIVE_DIR_PATH", {
42070
+ enumerable: true,
42071
+ get: function() {
42072
+ return RULESYNC_CHECKS_RELATIVE_DIR_PATH;
42073
+ }
42074
+ });
40898
42075
  Object.defineProperty(exports, "RULESYNC_COMMANDS_RELATIVE_DIR_PATH", {
40899
42076
  enumerable: true,
40900
42077
  get: function() {
@@ -40973,6 +42150,12 @@ Object.defineProperty(exports, "RULESYNC_MCP_SCHEMA_URL", {
40973
42150
  return RULESYNC_MCP_SCHEMA_URL;
40974
42151
  }
40975
42152
  });
42153
+ Object.defineProperty(exports, "RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH", {
42154
+ enumerable: true,
42155
+ get: function() {
42156
+ return RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH;
42157
+ }
42158
+ });
40976
42159
  Object.defineProperty(exports, "RULESYNC_OVERVIEW_FILE_NAME", {
40977
42160
  enumerable: true,
40978
42161
  get: function() {
@@ -41039,6 +42222,18 @@ Object.defineProperty(exports, "RulesProcessor", {
41039
42222
  return RulesProcessor;
41040
42223
  }
41041
42224
  });
42225
+ Object.defineProperty(exports, "RulesyncCheck", {
42226
+ enumerable: true,
42227
+ get: function() {
42228
+ return RulesyncCheck;
42229
+ }
42230
+ });
42231
+ Object.defineProperty(exports, "RulesyncCheckFrontmatterSchema", {
42232
+ enumerable: true,
42233
+ get: function() {
42234
+ return RulesyncCheckFrontmatterSchema;
42235
+ }
42236
+ });
41042
42237
  Object.defineProperty(exports, "RulesyncCommand", {
41043
42238
  enumerable: true,
41044
42239
  get: function() {
@@ -41114,7 +42309,7 @@ Object.defineProperty(exports, "RulesyncSubagentFrontmatterSchema", {
41114
42309
  Object.defineProperty(exports, "SKILL_FILE_NAME", {
41115
42310
  enumerable: true,
41116
42311
  get: function() {
41117
- return SKILL_FILE_NAME;
42312
+ return SKILL_FILE_NAME$1;
41118
42313
  }
41119
42314
  });
41120
42315
  Object.defineProperty(exports, "SkillsProcessor", {